SlideShare a Scribd company logo
PROGRAMMING ACTIONSCRIPT™
3.0
© 2007 Adobe Systems Incorporated. All rights reserved.
Programming ActionScript™ 3.0
If this guide is distributed with software that includes an end-user agreement, this guide, as well as the software described in it, is
furnished under license and may be used or copied only in accordance with the terms of such license. Except as permitted by any
such license, no part of this guide may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means,
electronic, mechanical, recording, or otherwise, without the prior written permission of Adobe Systems Incorporated. Please note
that the content in this guide is protected under copyright law even if it is not distributed with software that includes an end-user
license agreement.
The content of this guide is furnished for informational use only, is subject to change without notice, and should not be
construed as a commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsibility or liability
for any errors or inaccuracies that may appear in the informational content contained in this guide.
Please remember that existing artwork or images that you may want to include in your project may be protected under copyright
law. The unauthorized incorporation of such material into your new work could be a violation of the rights of the copyright
owner. Please be sure to obtain any permission required from the copyright owner.
Any references to company names in sample templates are for demonstration purposes only and are not intended to refer to any
actual organization.
Adobe, the Adobe logo, Flex, Flex Builder and Flash Player are either registered trademarks or trademarks of Adobe Systems
Incorporated in the United States and/or other countries.
ActiveX and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United States and other
countries. Macintosh is a trademark of Apple Inc., registered in the United States and other countries. All other trademarks are
the property of their respective owners.
Speech compression and decompression technology licensed from Nellymoser, Inc. (www.nellymoser.com).
Sorenson™ Spark™ video compression and decompression technology licensed from Sorenson Media, Inc.
Opera ® browser Copyright © 1995-2002 Opera Software ASA and its suppliers. All rights reserved.
Adobe Systems Incorporated, 345 Park Avenue, San Jose, California 95110, USA
Notice to U.S. government end users. The software and documentation are “Commercial Items,” as that term is defined at 48
C.F.R. §2.101, consisting of “Commercial Computer Software” and “Commercial Computer Software Documentation,” as such
terms are used in 48 C.F.R. §12.212 or 48 C.F.R. §227.7202, as applicable. Consistent with 48 C.F.R. §12.212 or 48 C.F.R.
§§227.7202-1 through 227.7202-4, as applicable, the Commercial Computer Software and Commercial Computer Software
Documentation are being licensed to U.S. Government end users (a) only as Commercial items and (b) with only those rights as
are granted to all other end users pursuant to the terms and conditions herein. Unpublished-rights reserved under the copyright
laws of the United States. Adobe Systems Incorporated, 345 Park Avenue, San Jose, CA 95110-2704, USA. For U.S.
Government End Users, Adobe agrees to comply with all applicable equal opportunity laws including, if appropriate, the
provisions of Executive Order 11246, as amended, Section 402 of the Vietnam Era Veterans Readjustment Assistance Act of
1974 (38 USC 4212), and Section 503 of the Rehabilitation Act of 1973, as amended, and the regulations at 41 CFR Parts 60-1
through 60-60, 60-250 ,and 60-741. The affirmative action clause and regulations contained in the preceding sentence shall be
incorporated by reference.
3
Contents
About this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Using this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Accessing ActionScript documentation . . . . . . . . . . . . . . . . . . . . . . . . . . 15
ActionScript learning resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Chapter 1: Introduction to ActionScript 3.0 . . . . . . . . . . . . . . . . . . 19
About ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Advantages of ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
What’s new in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Core language features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Flash Player API features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Compatibility with previous versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Chapter 2: Getting started with ActionScript. . . . . . . . . . . . . . . . .27
Programming fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27
What computer programs do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27
Variables and constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Working with objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Basic event handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Examining the event-handling process . . . . . . . . . . . . . . . . . . . . . 35
Event-handling examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Creating object instances. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Common program elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Example: Animation portfolio piece . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Building applications with ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . .47
Options for organizing your code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Choosing the right tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
The ActionScript development process . . . . . . . . . . . . . . . . . . . . . . . . 51
Creating your own classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Strategies for designing a class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Writing the code for a class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4
Suggestions for organizing your classes . . . . . . . . . . . . . . . . . . . . . . . 56
Example: Creating a basic application . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Running subsequent examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Chapter 3: ActionScript language and syntax . . . . . . . . . . . . . . . . 67
Language overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Objects and classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Packages and namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Dynamic classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Data type descriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Type conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .119
Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Basic function concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Function parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Functions as objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Function scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Chapter 4: Object-oriented programming in ActionScript. . . . . 137
Basics of object-oriented programming. . . . . . . . . . . . . . . . . . . . . . . . . 138
Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Class definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141
Class property attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Enumerations with classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Embedded asset classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Inheritance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Advanced topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Example: GeometricShapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
5
Chapter 5: Working with dates and times. . . . . . . . . . . . . . . . . . . 191
Basics of dates and times. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
Managing calendar dates and times . . . . . . . . . . . . . . . . . . . . . . . . . . . .193
Controlling time intervals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .196
Example: Simple analog clock. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .199
Chapter 6: Working with strings . . . . . . . . . . . . . . . . . . . . . . . . . 203
Basics of strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Creating strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
The length property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Working with characters in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Comparing strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Obtaining string representations of other objects. . . . . . . . . . . . . . . . 209
Concatenating strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Finding substrings and patterns in strings . . . . . . . . . . . . . . . . . . . . . . .210
Converting strings between uppercase and lowercase . . . . . . . . . . . .215
Example: ASCII art. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .216
Chapter 7: Working with arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Basics of arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Indexed arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Associative arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
Multidimensional arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
Cloning arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241
Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241
Example: PlayList. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Chapter 8: Handling errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Basics of error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Types of errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Error handling in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
ActionScript 3.0 error-handling elements . . . . . . . . . . . . . . . . . . . . 260
Error-handling strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .261
Working with the debugger version of Flash Player . . . . . . . . . . . . . . .261
Handling synchronous errors in an application . . . . . . . . . . . . . . . . . . 262
Creating custom error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Responding to error events and status . . . . . . . . . . . . . . . . . . . . . . . . . 268
6
Comparing the Error classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272
ECMAScript core Error classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272
ActionScript core Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .275
flash.error package Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . .276
Example: CustomErrors application . . . . . . . . . . . . . . . . . . . . . . . . . . . .278
Chapter 9: Using regular expressions. . . . . . . . . . . . . . . . . . . . . 285
Basics of regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .286
Regular expression syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .288
Creating an instance of a regular expression . . . . . . . . . . . . . . . . . .289
Characters, metacharacters, and metasequences . . . . . . . . . . . . 290
Character classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .293
Quantifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Alternation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297
Groups. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297
Flags and properties. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
Methods for using regular expressions with strings. . . . . . . . . . . . . . 305
Example: A Wiki parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Chapter 10: Handling events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
Basics of handling events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
How ActionScript 3.0 event handling differs from earlier versions . 317
The event flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Event objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322
Event listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327
Example: Alarm Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .335
Chapter 11: Working with XML. . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Basics of XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
The E4X approach to XML processing . . . . . . . . . . . . . . . . . . . . . . . . .348
XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
XMLList objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .353
Initializing XML variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .354
Assembling and transforming XML objects. . . . . . . . . . . . . . . . . . . . . .356
Traversing XML structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358
Using XML namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .363
XML type conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364
Reading external XML documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . .366
Example: Loading RSS data from the Internet. . . . . . . . . . . . . . . . . . .367
7
Chapter 12: Display programming . . . . . . . . . . . . . . . . . . . . . . . . . 371
Basics of display programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Core display classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Advantages of the display list approach . . . . . . . . . . . . . . . . . . . . . . . . 379
Working with display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
Properties and methods of the DisplayObject class . . . . . . . . . . . 382
Adding display objects to the display list . . . . . . . . . . . . . . . . . . . . . 383
Working with display object containers . . . . . . . . . . . . . . . . . . . . . . 383
Traversing the display list. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Setting Stage properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
Handling events for display objects . . . . . . . . . . . . . . . . . . . . . . . . . . 392
Choosing a DisplayObject subclass. . . . . . . . . . . . . . . . . . . . . . . . . . 393
Manipulating display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Changing position . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Panning and scrolling display objects . . . . . . . . . . . . . . . . . . . . . . . . 400
Manipulating size and scaling objects. . . . . . . . . . . . . . . . . . . . . . . . 402
Controlling distortion when scaling . . . . . . . . . . . . . . . . . . . . . . . . 403
Caching display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
When to enable caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Enabling bitmap caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
Setting an opaque background color . . . . . . . . . . . . . . . . . . . . . . 409
Applying blending modes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Adjusting DisplayObject colors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .410
Setting color values with code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
Altering color and brightness effects with code . . . . . . . . . . . . . .412
Rotating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413
Fading objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413
Masking display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .414
Animating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .416
Loading display content dynamically. . . . . . . . . . . . . . . . . . . . . . . . . . . .418
Loading display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .419
Monitoring loading progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
Specifying loading context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .421
Example: SpriteArranger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
Chapter 13: Working with geometry . . . . . . . . . . . . . . . . . . . . . . .431
Basics of geometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .431
Using Point objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Using Rectangle objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Using Matrix objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
Example: Applying a matrix transformation to a display object . . . . 442
8
Chapter 14: Using the drawing API . . . . . . . . . . . . . . . . . . . . . . . 447
Basics of using the drawing API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448
Understanding the Graphics class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Drawing lines and curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Drawing shapes using built-in methods . . . . . . . . . . . . . . . . . . . . . . . . .453
Creating gradient lines and fills. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
Using the Math class with drawing methods . . . . . . . . . . . . . . . . . . . . 460
Animating with the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
Example: Algorithmic Visual Generator . . . . . . . . . . . . . . . . . . . . . . . . .462
Chapter 15: Filtering display objects . . . . . . . . . . . . . . . . . . . . . . 465
Basics of filtering display objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465
Creating and applying filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467
Creating a new filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467
Applying a filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467
How filters work. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .470
Potential issues for working with filters . . . . . . . . . . . . . . . . . . . . . . .470
Available display filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472
Bevel filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .473
Blur filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .474
Drop shadow filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .474
Glow filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .475
Gradient bevel filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .476
Gradient glow filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
Example: Combining basic filters . . . . . . . . . . . . . . . . . . . . . . . . . . . .478
Color matrix filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
Convolution filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Displacement map filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .484
Example: Filter Workbench. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
Chapter 16: Working with movie clips. . . . . . . . . . . . . . . . . . . . . . 491
Basics of movie clips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Working with MovieClip objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Controlling movie clip playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Working with scenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .497
Creating MovieClip objects with ActionScript . . . . . . . . . . . . . . . . . . 498
Exporting library symbols for ActionScript . . . . . . . . . . . . . . . . . . . 498
Loading an external SWF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
Example: RuntimeAssetsExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
9
Chapter 17: Working with text . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Basics of working with text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .510
Displaying text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513
Types of text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513
Modifying the text field contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . .514
Displaying HTML text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .514
Using images in text fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .515
Scrolling text in a text field. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .516
Selecting and manipulating text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
Capturing text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .518
Restricting text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Formatting text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Assigning text formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521
Applying cascading style sheets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521
Loading an external CSS file. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
Formatting ranges of text within a text field. . . . . . . . . . . . . . . . . . . 524
Advanced text rendering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
Working with static text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
Example: Newspaper-style text formatting . . . . . . . . . . . . . . . . . . . . . 529
Reading the external CSS file. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
Arranging story elements on the page . . . . . . . . . . . . . . . . . . . . . . . 532
Altering font size to fit the field size . . . . . . . . . . . . . . . . . . . . . . . . . . 533
Splitting text across multiple columns. . . . . . . . . . . . . . . . . . . . . . . . 535
Chapter 18: Working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . 539
Basics of working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
The Bitmap and BitmapData classes . . . . . . . . . . . . . . . . . . . . . . . . . . 543
Manipulating pixels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
Manipulating individual pixels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
Pixel-level collision detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
Copying bitmap data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Making textures with noise functions. . . . . . . . . . . . . . . . . . . . . . . . . . . 550
Scrolling bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
Example: Animating sprites using an offscreen bitmap . . . . . . . . . . . 553
Chapter 19: Working with video . . . . . . . . . . . . . . . . . . . . . . . . . . 555
Basics of video . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Understanding the Flash Video (FLV) format . . . . . . . . . . . . . . . . . . . 559
Understanding the Video class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
Loading video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .561
Controlling video playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
Detecting the end of a video stream . . . . . . . . . . . . . . . . . . . . . . . . . 563
10
Streaming video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .564
Understanding cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .565
Writing callback methods for onCuePoint and onMetaData . . . . . . .566
Set the NetStream object’s client property to an Object . . . . . . . .567
Create a custom class and define methods
to handle the callback methods. . . . . . . . . . . . . . . . . . . . . . . . . . . .568
Extend the NetStream class and add methods
to handle the callback methods. . . . . . . . . . . . . . . . . . . . . . . . . . . 569
Extend the NetStream class and make it dynamic . . . . . . . . . . . . .570
Set the NetStream object’s client property to this. . . . . . . . . . . . . .572
Using cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .572
Using video metadata. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .573
Capturing camera input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
Understanding the Camera class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
Displaying camera content on-screen . . . . . . . . . . . . . . . . . . . . . . . .578
Designing your camera application. . . . . . . . . . . . . . . . . . . . . . . . . . .578
Connecting to a user’s camera. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .578
Verifying that cameras are installed . . . . . . . . . . . . . . . . . . . . . . . . . .579
Detecting permissions for camera access . . . . . . . . . . . . . . . . . . . 580
Maximizing video quality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .582
Monitoring playback conditions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .583
Sending video to a server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .584
Advanced topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .585
Flash Player compatibility with encoded FLV files . . . . . . . . . . . . .585
About configuring FLV files for hosting on a server . . . . . . . . . . . .585
About targeting local FLV files on the Macintosh . . . . . . . . . . . . . .586
Example: Video Jukebox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .587
Chapter 20: Working with sound . . . . . . . . . . . . . . . . . . . . . . . . 595
Basics of working with sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
Understanding the sound architecture . . . . . . . . . . . . . . . . . . . . . . . . . 599
Loading external sound files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
Working with embedded sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
Working with streaming sound files . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
Playing sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
Pausing and resuming a sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
Monitoring playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .607
Stopping streaming sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
Security considerations when loading and playing sounds . . . . . . . 609
Controlling sound volume and panning . . . . . . . . . . . . . . . . . . . . . . . . . .611
Working with sound metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
Accessing raw sound data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
Capturing sound input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
11
Accessing a microphone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
Routing microphone audio to local speakers. . . . . . . . . . . . . . . . . . .619
Altering microphone audio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .619
Detecting microphone activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
Sending audio to and from a media server. . . . . . . . . . . . . . . . . . . . .621
Example: Podcast Player . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
Reading RSS data for a podcast channel . . . . . . . . . . . . . . . . . . . . 623
Simplifying sound loading and playback
using the SoundFacade class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
Displaying playback progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627
Pausing and resuming playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
Extending the Podcast Player example . . . . . . . . . . . . . . . . . . . . . . 629
Chapter 21: Capturing user input . . . . . . . . . . . . . . . . . . . . . . . . . .631
Basics of user input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .631
Capturing keyboard input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
Capturing mouse input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
Example: WordSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .641
Chapter 22: Networking and communication . . . . . . . . . . . . . . 645
Basics of networking and communication . . . . . . . . . . . . . . . . . . . . . . 646
Working with external data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
Connecting to other Flash Player instances. . . . . . . . . . . . . . . . . . . . . 656
Socket connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
Storing local data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
Working with file upload and download . . . . . . . . . . . . . . . . . . . . . . . . . 671
Example: Building a Telnet client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
Example: Uploading and downloading files . . . . . . . . . . . . . . . . . . . . . 685
Chapter 23: Client system environment . . . . . . . . . . . . . . . . . . . 695
Basics of the client system environment. . . . . . . . . . . . . . . . . . . . . . . . 695
Using the System class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698
Using the Capabilities class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
Using the ApplicationDomain class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700
Using the IME class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
Example: Detecting system capabilities . . . . . . . . . . . . . . . . . . . . . . . . 709
12
Chapter 24: Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
Basics of printing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
Printing a page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718
Flash Player tasks and system printing. . . . . . . . . . . . . . . . . . . . . . . . . . 719
Setting size, scale, and orientation . . . . . . . . . . . . . . . . . . . . . . . . . . . . .722
Example: Multiple-page printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .725
Example: Scaling, cropping, and responding . . . . . . . . . . . . . . . . . . . . 727
Chapter 25: Using the external API . . . . . . . . . . . . . . . . . . . . . . . 731
Basics of using the external API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .732
External API requirements and advantages . . . . . . . . . . . . . . . . . . . . .735
Using the ExternalInterface class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .736
Getting information about the external container . . . . . . . . . . . . . . 737
Calling external code from ActionScript . . . . . . . . . . . . . . . . . . . . . . 737
Calling ActionScript code from the container. . . . . . . . . . . . . . . . . .739
The external API’s XML format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .740
Example: Using the external API with a web page container. . . . . . .742
Example: Using the external API with an ActiveX container . . . . . . .749
Chapter 26: Flash Player security. . . . . . . . . . . . . . . . . . . . . . . . .757
Flash Player security overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .758
Overview of permission controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .760
Security sandboxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770
Restricting networking APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
Full-screen mode security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
Loading content. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
Cross-scripting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779
Accessing loaded media as data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .783
Loading data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .786
Loading embedded content from SWF files
imported into a security domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .788
Working with legacy content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .789
Setting LocalConnection permissions . . . . . . . . . . . . . . . . . . . . . . . . . .790
Controlling access to scripts in a host web page . . . . . . . . . . . . . . . . .790
Shared objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .792
Camera, microphone, clipboard, mouse, and keyboard access . . . .794
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
13
About this manual
This manual provides a foundation for developing applications in ActionScript™ 3.0. To best
understand the ideas and techniques described, you should already be familiar with general
programming concepts such as data types, variables, loops, and functions. You should also
understand basic object-oriented programming concepts such as classes and inheritance. Prior
knowledge of ActionScript 1.0 or ActionScript 2.0 is helpful but not necessary.
Contents
Using this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Accessing ActionScript documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
ActionScript learning resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
14 About this manual
Using this manual
The chapters in this manual are organized into the following logical groups to help you better
find related areas of ActionScript documentation:
This manual also contains numerous sample files that demonstrate application programming
concepts for important or commonly used classes. Sample files are packaged in ways to make
them easier to load and use with Adobe® Flash® CS3 Professional and may include wrapper
files. However, the core sample code is pure ActionScript 3.0 that you can use in whichever
development environment you prefer.
ActionScript 3.0 can be written and compiled a number of ways, including:
■ Using the Adobe Flex Builder 2 development environment
■ Using any text editor and a command-line compiler, such as the one provided with Flex
Builder 2
■ Using the Adobe® Flash® CS3 Professional authoring tool
For more information about ActionScript development environments, see Chapter 1,
“Introduction to ActionScript 3.0.”
Chapters Description
Chapters 1 through 4, overview of
ActionScript programming
Discusses core ActionScript 3.0 concepts,
including language syntax, statements and
operators, the ECMAScript edition 4 draft
language specification, object-oriented
ActionScript programming, and the new
approach to managing display objects on the
Adobe® Flash® Player 9 display list.
Chapters 5 through 10, core ActionScript
3.0 data types and classes
Describes top-level data types in ActionScript
3.0 that are also part of the ECMAScript draft
specification.
Chapters 11 through 26, Flash Player APIs Describes important features that are
implemented in packages and classes specific to
Adobe Flash Player 9, including event handling,
networking and communications, file input and
output, the external interface, the application
security model, and more.
Accessing ActionScript documentation 15
To understand the code samples in this manual, you don’t need to have prior experience using
integrated development environments for ActionScript, such as Flex Builder or the Flash
authoring tool. You will, however, want to refer to the documentation for those tools to learn
how to use them to write and compile ActionScript 3.0 code. For more information, see
“Accessing ActionScript documentation” on page 15.
Accessing ActionScript documentation
Because this manual focuses on describing ActionScript 3.0, which is a rich and powerful
object-oriented programming language, it does not extensively cover the application
development process or workflow within a particular tool or server architecture. So in
addition to Programming ActionScript 3.0, you’ll want to consult other sources of
documentation as you design, develop, test, and deploy ActionScript 3.0 applications.
ActionScript 3.0 documentation
This manual familiarizes you with the concepts behind the ActionScript 3.0 programming
language and gives you implementation details and samples illustrating important language
features. However, this manual is not a complete language reference. For that, see the
ActionScript 3.0 Language and Components Reference, which describes every class, method,
property, and event in the language. The ActionScript 3.0 Language and Components Reference
provides detailed reference information about the core language, Flash components (in the fl
packages), and Flash Player APIs (in the flash packages).
Flash documentation
If you use the Flash development environment, you may want to consult these manuals:
Book Description
Using Flash Describes how to develop your dynamic web
applications in the Flash authoring environment
Programming ActionScript 3.0 Describes specific usage of the ActionScript
3.0 language and core Flash Player API
ActionScript 3.0 Language and Components
Reference
Provides syntax, usage, and code examples for
the Flash components and ActionScript 3.0
API
Using ActionScript 3.0 Components Explains the details of using components to
develop Flash applications
16 About this manual
Learning ActionScript 2.0 in Adobe Flash Provides an overview of ActionScript 2.0
syntax and explains how to use ActionScript
2.0 when working with different types of
objects
ActionScript 2.0 Language Reference Provides syntax, usage, and code examples for
the Flash components and ActionScript 2.0
API
Using ActionScript 2.0 Components Explains in detail how to use ActionScript 2.0
components to develop Flash applications
ActionScript 2.0 Components Language
Reference
Describes each component available in the
Version 2 Adobe Component Architecture,
along with its API
Extending Flash Describes the objects, methods, and properties
available in the JavaScript API
Getting Started with Flash Lite 2.x Explains how to use Adobe® Flash® Lite™ 2.x to
develop applications and provides syntax,
usage, and code examples for the ActionScript
features that are available with Flash Lite 2.x
Developing Flash Lite 2.x Applications Explains how to develop Flash Lite 2.x
applications
Introduction to Flash Lite 2.x ActionScript Introduces how to develop applications with
Flash Lite 2.x and describes all the ActionScript
features available to Flash Lite 2.x developers
Flash Lite 2.x ActionScript Language
Reference
Provides syntax, usage, and code examples for
the ActionScript 2.0 API that is available in
Flash Lite 2.x
Getting Started with Flash Lite 1.x Provides an introduction to Flash Lite 1.x and
describes how to test your content using the
Adobe® Device Central CS3 emulator
Developing Flash Lite 1.x Applications Describes how to develop applications for
mobile devices using Flash Lite 1.x
Learning Flash Lite 1.x ActionScript Explains how to use ActionScript in Flash Lite
1.x applications and describes all the
ActionScript features available with Flash Lite
1.x
Flash Lite 1.x ActionScript Language
Reference
Provides the syntax and usage of ActionScript
elements that are available with Flash Lite 1.x
Book Description
ActionScript learning resources 17
ActionScript learning resources
In addition to the content in these manuals, Adobe provides regularly updated articles, design
ideas, and examples at the Adobe Developer Center and the Adobe Design Center.
Adobe Developer Center
The Adobe Developer Center is your resource for up-to-the-minute information on
ActionScript, articles about real-world application development, and information about
important emerging issues. View the Developer Center at www.adobe.com/devnet/.
Adobe Design Center
Learn the latest in digital design and motion graphics. Browse work by leading artists, discover
new design trends, and hone your skills with tutorials, key workflows, and advanced
techniques. Check back twice a month for fresh tutorials and articles, and inspirational gallery
pieces. View the Design Center at www.adobe.com/designcenter/.
18 About this manual
19
1
CHAPTER 1
Introduction to
ActionScript 3.0
This chapter provides an overview of ActionScript 3.0, the newest and most revolutionary
version of ActionScript.
Contents
About ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Advantages of ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20
What’s new in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Compatibility with previous versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
About ActionScript
ActionScript is the programming language for the Adobe Flash Player run-time environment.
It enables interactivity, data handling, and much more in Flash content and applications.
ActionScript is executed by the ActionScript Virtual Machine (AVM), which is part of Flash
Player. ActionScript code is typically compiled into bytecode format (a sort of programming
language that’s written and understood by computers) by a compiler, such as the one built
into Adobe Flash CS3 Professional or Adobe® Flex™ Builder™, or that is available in the
Adobe® Flex™ SDK and the Flex™ Data Services. The bytecode is embedded in SWF files,
which are executed by the Flash Player, the run-time environment.
ActionScript 3.0 offers a robust programming model that will be familiar to developers with a
basic knowledge of object-oriented programming. Some of the key features of ActionScript
3.0 include the following:
■ A new ActionScript Virtual Machine, called AVM2, that uses a new bytecode instruction
set and provides significant performance improvements
■ A more modern compiler code base that adheres much more closely to the ECMAScript
(ECMA 262) standard and that performs deeper optimizations than previous versions of
the compiler
20 Introduction to ActionScript 3.0
■ An expanded and improved application programming interface (API), with low-level
control of objects and a true object-oriented model
■ A core language based on the upcoming ECMAScript (ECMA-262) edition 4 draft
language specification
■ An XML API based on the ECMAScript for XML (E4X) specification (ECMA-357
edition 2). E4X is a language extension to ECMAScript that adds XML as a native data
type of the language.
■ An event model based on the Document Object Model (DOM) Level 3 Events
Specification
Advantages of ActionScript 3.0
ActionScript 3.0 goes beyond the scripting capabilities of previous versions of ActionScript. It
is designed to facilitate the creation of highly complex applications with large data sets and
object-oriented, reusable code bases. While ActionScript 3.0 is not required for content that
runs in Adobe Flash Player 9, it opens the door to performance improvements that are only
available with the AVM2, the new virtual machine. ActionScript 3.0 code can execute up to
ten times faster than legacy ActionScript code.
The older version of ActionScript Virtual Machine, AVM1, executes ActionScript 1.0 and
ActionScript 2.0 code. AVM1 is supported by Flash Player 9 for backward compatibility with
existing and legacy content. For more information, see “Compatibility with previous versions”
on page 24.
What’s new in ActionScript 3.0 21
What’s new in ActionScript 3.0
Although ActionScript 3.0 contains many classes and features that will be familiar to
ActionScript programmers, ActionScript 3.0 is architecturally and conceptually different from
previous versions of ActionScript. The enhancements in ActionScript 3.0 include new
features of the core language and an improved Flash Player API that provides increased
control of low-level objects.
Core language features
The core language defines the basic building blocks of the programming language, such as
statements, expressions, conditions, loops, and types. ActionScript 3.0 contains many new
features that speed up the development process.
Run-time exceptions
ActionScript 3.0 reports more error conditions than previous versions of ActionScript. Run-
time exceptions are used for common error conditions, improving the debugging experience
and enabling you to develop applications that handle errors robustly. Run-time errors can
provide stack traces annotated with source file and line number information, helping you
quickly pinpoint errors.
Run-time types
In ActionScript 2.0, type annotations were primarily a developer aid; at run time, all values
were dynamically typed. In ActionScript 3.0, type information is preserved at run time, and
used for a number of purposes. Flash Player 9 performs run-time type checking, improving
the system’s type safety. Type information is also used to represent variables in native machine
representations, improving performance and reducing memory usage.
Sealed classes
ActionScript 3.0 introduces the concept of sealed classes. A sealed class possesses only the
fixed set of properties and methods that were defined at compile time; additional properties
and methods cannot be added. This enables stricter compile-time checking, resulting in more
robust programs. It also improves memory usage by not requiring an internal hash table for
each object instance. Dynamic classes are also possible using the dynamic keyword. All classes
in ActionScript 3.0 are sealed by default, but can be declared to be dynamic with the dynamic
keyword.
22 Introduction to ActionScript 3.0
Method closures
ActionScript 3.0 enables a method closure to automatically remember its original object
instance. This feature is useful for event handling. In ActionScript 2.0, method closures
would not remember what object instance they were extracted from, leading to unexpected
behavior when the method closure was invoked. The mx.utils.Delegate class was a popular
workaround, but it is no longer needed.
ECMAScript for XML (E4X)
ActionScript 3.0 implements ECMAScript for XML (E4X), recently standardized as ECMA-
357. E4X offers a natural, fluent set of language constructs for manipulating XML. In
contrast to traditional XML-parsing APIs, XML with E4X performs like a native data type of
the language. E4X streamlines the development of applications that manipulate XML by
drastically reducing the amount of code needed. For more information about the ActionScript
3.0 implementation of E4X, see Chapter 11, “Working with XML,” on page 343.
To view ECMA’s E4X specification, go to www.ecma-international.org.
Regular expressions
ActionScript 3.0 includes native support for regular expressions so that you can quickly search
for and manipulate strings. ActionScript 3.0 implements support for regular expressions as
they are defined in the ECMAScript (ECMA-262) edition 3 language specification.
Namespaces
Namespaces are similar to the traditional access specifiers used to control visibility of
declarations (public, private, protected). They work as custom access specifiers, which
can have names of your choice. Namespaces are outfitted with a Universal Resource Identifier
(URI) to avoid collisions, and are also used to represent XML namespaces when you work
with E4X.
New primitive types
ActionScript 2.0 has a single numeric type, Number, a double-precision, floating-point
number. ActionScript 3.0 contains the int and uint types. The int type is a 32-bit signed
integer that lets ActionScript code take advantage of the fast integer math capabilities of the
CPU. The int type is useful for loop counters and variables where integers are used. The uint
type is an unsigned, 32-bit integer type that is useful for RGB color values, byte counts, and
more.
What’s new in ActionScript 3.0 23
Flash Player API features
The Flash Player API in ActionScript 3.0 contains many new classes that allow you to control
objects at a low level. The architecture of the language is completely new and more intuitive.
While there are too many new classes to cover in detail here, the following sections highlight
some significant changes.
DOM3 event model
Document Object Model Level 3 event model (DOM3) provides a standard way of
generating and handling event messages so that objects within applications can interact and
communicate, maintaining their state and responding to change. Patterned after the World
Wide Web Consortium DOM Level 3 Events Specification, this model provides a clearer and
more efficient mechanism than the event systems available in previous versions of
ActionScript.
Events and error events are located in the flash.events package. The Flash components
framework uses the same event model as the Flash Player API, so the event system is unified
across the Flash platform.
Display list API
The API for accessing the Flash Player display list—the tree that contains any visual elements
in a Flash application—consists of classes for working with visual primitives in Flash.
The new Sprite class is a lightweight building block, similar to the MovieClip class but more
appropriate as a base class for UI components. The new Shape class represents raw vector
shapes. These classes can be instantiated naturally with the new operator and can be
dynamically re-parented at any time.
Depth management is now automatic and built into Flash Player, rendering assignment of
depth numbers unnecessary. New methods are provided for specifying and managing the z-
order of objects.
Handling dynamic data and content
ActionScript 3.0 contains mechanisms for loading and handling assets and data in your Flash
application that are intuitive and consistent across the API. The new Loader class provides a
single mechanism for loading SWF files and image assets and provides a way to access detailed
information about loaded content. The URLLoader class provides a separate mechanism for
loading text and binary data in data-driven applications. The Socket class provides a means to
read and write binary data to server sockets in any format.
24 Introduction to ActionScript 3.0
Low-level data access
Various APIs provide low-level access to data that was never before available in ActionScript.
For data that is being downloaded, the URLStream class, which is implemented by
URLLoader, provides access to data as raw binary data while it is being downloaded. The
ByteArray class lets you optimize reading, writing, and working with binary data. The new
Sound API provides detailed control of sound through the SoundChannel and SoundMixer
classes. New APIs dealing with security provide information about the security privileges of a
SWF file or loaded content, enabling you to better handle security errors.
Working with text
ActionScript 3.0 contains a flash.text package for all text-related APIs. The TextLineMetrics
class provides detailed metrics for a line of text within a text field; it replaces the
TextField.getLineMetrics() method in ActionScript 2.0. The TextField class contains a
number of interesting new low-level methods that can provide specific information about a
line of text or a single character in a text field. These methods include
getCharBoundaries(), which returns a rectangle representing the bounding box of a
character, getCharIndexAtPoint(), which returns the index of the character at a specified
point, and getFirstCharInParagraph(), which returns the index of the first character in a
paragraph. Line-level methods include getLineLength(), which returns the number of
characters in a specified line of text, and getLineText(), which returns the text of the
specified line. A new Font class provides a means to manage embedded fonts in SWF files.
Compatibility with previous versions
As always, Flash Player provides full backward compatibility with previously published
content. Any content that ran in previous versions of Flash Player runs in Flash Player 9. The
introduction of ActionScript 3.0 in Flash Player 9, however, does present some challenges for
interoperability between old and new content running in Flash Player 9. The compatibility
issues include the following:
■ A single SWF file cannot combine ActionScript 1.0 or 2.0 code with ActionScript 3.0
code.
■ ActionScript 3.0 code can load a SWF file written in ActionScript 1.0 or 2.0, but it
cannot access the SWF file’s variables and functions.
Compatibility with previous versions 25
■ SWF files written in ActionScript 1.0 or 2.0 cannot load SWF files written in
ActionScript 3.0. This means that SWF files authored in Flash 8 or Flex Builder 1.5 or
earlier versions cannot load ActionScript 3.0 SWF files.
The only exception to this rule is that an ActionScript 2.0 SWF file can replace itself with
an ActionScript 3.0 SWF file, as long as the ActionScript 2.0 SWF file hasn't previously
loaded anything into any of its levels. An ActionScript 2.0 SWF file can do this through a
call to loadMovieNum(), passing a value of 0 to the level parameter.
■ In general, SWF files written in ActionScript 1.0 or 2.0 must be migrated if they are to
work together with SWF files written in ActionScript 3.0. For example, say you created a
media player using ActionScript 2.0. The media player loads various content that was also
created using ActionScript 2.0. You cannot create new content in ActionScript 3.0 and
load it in the media player. You must migrate the video player to ActionScript 3.0.
If, however, you create a media player in ActionScript 3.0, that media player can perform
simple loads of your ActionScript 2.0 content.
26 Introduction to ActionScript 3.0
The following table summarizes the limitations of previous versions of Flash Player in relation
to loading new content and executing code, as well as the limitations for cross-scripting
between SWF files written in different versions of ActionScript.
Supported
functionality
Run-time environment
Flash Player 7 Flash Player 8 Flash Player 9
Can load SWFs
published for
7 and earlier 8 and earlier 9 and earlier
Contains this AVM AVM1 AVM1 AVM1 and AVM2
Runs SWFs written in
ActionScript
1.0 and 2.0 1.0 and 2.0 1.0 and 2.0, and 3.0
Supported
functionality*
* Content running in Flash Player 9 or later. Content running in Flash Player 8 or earlier can
load, display, execute, and cross-script only ActionScript 1.0 and 2.0.
Content created in
ActionScript 1.0 and 2.0 ActionScript 3.0
Can load content and
execute code in content
created in
ActionScript 1.0 and 2.0 only
ActionScript 1.0 and 2.0,
and ActionScript 3.0
Can cross script content
created in
ActionScript 1.0 and 2.0 only†
† ActionScript 3.0 through Local Connection.
ActionScript 3.0‡
‡ ActionScript 1.0 and 2.0 through LocalConnection.
27
2
CHAPTER 2
Getting started with
ActionScript
This chapter is designed to get you started with ActionScript programming and give you the
background you’ll need to understand the concepts and examples in the rest of this manual.
We’ll begin with a discussion of basic programming concepts, described in the context of how
to apply them in ActionScript. We’ll also cover the essentials of how to organize and build an
ActionScript application.
Contents
Programming fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Working with objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Common program elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Example: Animation portfolio piece . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Building applications with ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Creating your own classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Example: Creating a basic application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Running subsequent examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Programming fundamentals
Since ActionScript is a programming language, it will help you learn ActionScript if you first
understand a few general computer programming concepts.
What computer programs do
First of all, it’s useful to have a conceptual idea of what a computer program is and what it
does. There are two main aspects to a computer program:
■ A program is a series of instructions or steps for the computer to carry out.
■ Each step ultimately involves manipulating some piece of information or data.
28 Getting started with ActionScript
In a general sense, a computer program is just a list of step-by-step instructions that you give
to the computer, which it performs one by one. Each individual instruction is known as a
statement. As you’ll see throughout this manual, in ActionScript, each statement is written
with a semicolon at the end.
In essence, all that a given instruction in a program does is manipulate some bit of data that’s
stored in the computer’s memory. In a simple case, you might instruct the computer to add
two numbers and store the result in its memory. In a more complex case, imagine there is a
rectangle drawn on the screen, and you want to write a program to move it somewhere else on
the screen. The computer is keeping track of certain information about the rectangle—the x,
y coordinates where it’s located, how wide and tall it is, what color it is, and so forth. Each of
those bits of information is stored somewhere in the computer’s memory. A program to move
the rectangle to a different location would have steps like “change the x coordinate to 200;
change the y coordinate to 150” (in other words, specifying new values to be used for the x
and y coordinates). Of course, the computer does something with this data to actually turn
those numbers into the image that appears on the computer screen; but for the level of detail
we’re interested in, it’s enough to know that the process of “moving a rectangle on the screen”
really just involves changing bits of data in the computer’s memory.
Variables and constants
Since programming mainly involves changing pieces of information in the computer’s
memory, there needs to be a way to represent a single piece of information in the program. A
variable is a name that represents a value in the computer’s memory. As you write statements
to manipulate values, you write the variable’s name in place of the value; any time the
computer sees the variable name in your program, it looks in its memory and uses the value it
finds there. For example, if you have two variables named value1 and value2, each
containing a number, to add those two numbers you could write the statement:
value1 + value2
When it’s actually carrying out the steps, the computer will look to see the values in each
variable, and add them together.
In ActionScript 3.0, a variable actually consists of three different parts:
■ The variable’s name
■ The type of data that can be stored in the variable
■ The actual value stored in the computer’s memory
Programming fundamentals 29
We’ve just discussed how the computer uses the name as a placeholder for the value. The data
type is also important. When you create a variable in ActionScript, you specify the specific
type of data that it will hold; from that point on, your program’s instructions can store only
that type of data in the variable, and you can manipulate the value using the particular
characteristics associated with its data type. In ActionScript, to create a variable (known as
declaring the variable), you use the var statement:
var value1:Number;
In this case, we’ve told the computer to create a variable named value1, which will hold only
Number data (“Number” is a specific data type defined in ActionScript). You can also store a
value in the variable right away:
var value2:Number = 17;
In the Adobe Flash CS3 Professional, there is another way to declare a variable. When you
place a movie clip symbol, button symbol, or text field on the Stage, you can give it an
instance name in the Property inspector. Behind the scenes, Flash creates a variable with the
same name as the instance name, which you can use in your ActionScript code to refer to that
Stage item. So, for example, if you have a movie clip symbol on the Stage and you give it the
instance name rocketShip, whenever you use the variable rocketShip in your ActionScript
code, you will in fact be manipulating that movie clip.
Data types
In ActionScript, there are many data types that you can use as the data type of the variables
you create. Some of these can be thought of as “simple” or “fundamental” data types:
■ String: a textual value, like a name or the text of a book chapter
■ Numeric: ActionScript 3.0 includes three specific data types for numeric data:
■ Number: any numeric value, including values with or without a fraction
■ int: an integer (a whole number without a fraction)
■ uint: an “unsigned” integer, meaning a whole number that can’t be negative
■ Boolean: a true-or-false value, such as whether a switch is on or whether two values are
equal
30 Getting started with ActionScript
The simple data types represent a single piece of information: for example, a single number or
a single sequence of text. However, the majority of the data types defined in ActionScript
could be described as complex data types, because they represent a set of values grouped
together. For example, a variable with the data type Date represents a single value—a moment
in time. Nevertheless, that date value is actually represented as several values: the day, month,
year, hours, minutes, seconds, and so on, all of which are individual numbers. So while we
think of a date as a single value (and we can treat it as a single value by creating a Date
variable), internally the computer thinks of it as a group of several values that, put together,
define a single date.
Most of the built-in data types, as well as data types defined by programmers, are complex
data types. Some of the complex data types you might recognize are:
■ MovieClip: a movie clip symbol
■ TextField: a dynamic or input text field
■ SimpleButton: a button symbol
■ Date: information about a single moment in time (a date and time)
Two words that are often used as synonyms for data type are class and object. A class is simply
the definition of a data type—it’s like a template for all objects of the data type, like saying “all
variables of the Example data type have these characteristics: A, B, and C.” An object, on the
other hand, is just an actual instance of a class; a variable whose data type is MovieClip could
be described as a MovieClip object. The following are different ways of saying the same thing:
■ The data type of the variable myVariable is Number.
■ The variable myVariable is a Number instance.
■ The variable myVariable is a Number object.
■ The variable myVariable is an instance of the Number class.
Working with objects 31
Working with objects
ActionScript is what’s known as an object-oriented programming language. Object-oriented
programming is simply an approach to programming—really nothing more than a way to
organize the code in a program, using objects.
Earlier we defined a computer program as a series of steps or instructions that the computer
performs. Conceptually, then, we might imagine a computer program as just a single long list
of instructions. However, in object-oriented programming, the program instructions are
divided among different objects—the code is grouped into chunks of functionality, so related
types of functionality or related pieces of information are grouped together in one container.
In fact, if you’ve worked with symbols in Flash, you’re already used to working with objects.
Imagine you’ve defined a movie clip symbol—let’s say it’s a drawing of a rectangle—and
you’ve placed a copy of it on the Stage. That movie clip symbol is also (literally) an object in
ActionScript; it’s an instance of the MovieClip class.
There are various characteristics of the movie clip that you can modify. For example, when it’s
selected there are values you can change in the Property inspector, like its x coordinate, or its
width, or various color adjustments like changing its alpha (transparency), or applying a drop-
shadow filter to it. Other Flash tools let you make more changes, like using the Free
Transform tool to rotate the rectangle. All of these things that you can do to modify a movie
clip symbol in the Flash authoring environment are also things you can do in ActionScript by
changing the pieces of data that are all put together into a single bundle called a MovieClip
object.
In ActionScript object-oriented programming, there are three types of characteristics that any
class can include:
■ Properties
■ Methods
■ Events
Together, these elements are used to manage the pieces of data used by the program and to
decide what actions are carried out and in what order.
Properties
A property represents one of the pieces of data that are bundled together in an object. A song
object might have properties named artist and title; the MovieClip class has properties
like rotation, x, width, and alpha. You work with properties like individual variables—in
fact, you might think of properties as simply the “child” variables contained in an object.
32 Getting started with ActionScript
Here are some examples of ActionScript code that uses properties. This line of code moves the
MovieClip named square to the x coordinate 100 pixels:
square.x = 100;
This code uses the rotation property to make the square MovieClip rotate to match the
rotation of the triangle MovieClip:
square.rotation = triangle.rotation;
This code alters the horizontal scale of the square MovieClip so that it’s one-and-a-half times
wider than it used to be:
square.scaleX = 1.5;
Notice the common structure: you use a variable (square, triangle) as the name of the
object, followed by a period (.) and then the name of the property (x, rotation, scaleX).
The period, known as the dot operator, is used to indicate that you’re accessing one of the child
elements of an object. The whole structure together, “variable name-dot-property name,” is
used like a single variable, as a name for a single value in the computer’s memory.
Methods
A method is an action that can be performed by an object. For example, if you’ve made a
movie clip symbol in Flash with several keyframes and animation on its timeline, that movie
clip can play, or stop, or be instructed to move the playhead to a particular frame.
This code instructs the MovieClip named shortFilm to start playing:
shortFilm.play();
This line makes the MovieClip named shortFilm stop playing (the playhead stops in place,
like pausing a video):
shortFilm.stop();
This code makes a MovieClip named shortFilm move its playhead to Frame 1 and stop
playing (like rewinding a video):
shortFilm.gotoAndStop(1);
Working with objects 33
As you can see, methods, like properties, are accessed by writing the object’s name (a variable),
then a period, and then the name of the method followed by parentheses. The parentheses are
the way that you indicate that you’re calling the method—or in other words, instructing the
object to perform that action. Sometimes values (or variables) are placed in the parentheses, as
a way to pass along additional information that is needed to carry out the action. These values
are known as method parameters. For example, the gotoAndStop() method needs to know
which frame it should go to, so it requires a single parameter in the parentheses. Other
methods, like play() and stop(), are self-explanatory, so they don’t require extra
information. Nevertheless, they are still written with parentheses.
Unlike properties (and variables), methods aren’t used as value placeholders. However, some
methods can perform calculations and return a result that can be used like a variable. For
example, the Number class’s toString() method converts the numeric value to its text
representation:
var numericData:Number = 9;
var textData:String = numericData.toString();
For instance, you would use the toString() method if you wanted to display the value of a
Number variable in a text field on the screen. The TextField class’s text property (which
represents the actual text content displayed on the screen) is defined as a String, so it can
contain only text values. This line of code converts the numeric value in the variable
numericData to text, and then makes it show up on the screen in the TextField object named
calculatorDisplay:
calculatorDisplay.text = numericData.toString();
Events
We’ve described a computer program as a series of instructions that the computer carries out
step-by-step. Some simple computer programs consist of nothing more than that—a few steps
which the computer carries out, at which point the program ends. However, ActionScript
programs are designed to keep running, waiting for user input or other things to happen.
Events are the mechanism that determines which instructions the computer carries out and
when.
In essence, events are things that happen that ActionScript is aware of and can respond to.
Many events are related to user interaction—like a user clicking a button, or pressing a key on
the keyboard—but there are also other types of events. For example, if you use ActionScript to
load an external image, there is an event that can let you know when the image has finished
loading. In essence, when an ActionScript program is running, Adobe Flash Player just sits
and waits for certain things to happen, and when those things happen, it runs the specific
ActionScript code that you’ve specified for those events.
34 Getting started with ActionScript
Basic event handling
The technique for specifying certain actions that should be performed in response to
particular events is known as event handling. When you are writing ActionScript code to
perform event handling, there are three important elements you’ll want to identify:
■ The event source: Which object is the one the event is going to happen to? For instance,
which button will be clicked, or which Loader object is loading the image? The event
source is also known as the event target, because it’s the object where the event is targeted
by Flash Player (where the event actually happens).
■ The event: What is the thing that is going to happen, the thing that you want to respond
to? This is important to identify, because many objects trigger several events.
■ The response: What step(s) do you want performed when the event happens?
Any time you write ActionScript code to handle events, it will include these three elements,
and the code will follow this basic structure (elements in bold are placeholders you’d fill in for
your specific case):
function eventResponse(eventObject:EventType):void
{
// Actions performed in response to the event go here.
}
eventSource.addEventListener(EventType.EVENT_NAME, eventResponse);
This code does two things. First, it defines a function, which is the way to specify the actions
you want performed in response to the event. Next, it calls the addEventListener() method
of the source object, in essence “subscribing” the function to the specified event so that when
the event happens, the function’s actions are carried out. We’ll consider each of these parts in
more detail.
A function provides a way for you to group actions together, with a single name that is like a
shortcut name to carry out the actions. A function is identical to a method except that it isn’t
necessarily associated with a specific class (in fact, a method could be defined as a function
that is associated with a particular class). When you’re creating a function for event handling,
you must choose the name for the function (named eventResponse in this case), and you
must also specify one parameter (named eventObject in this example). Specifying a function
parameter is like declaring a variable, so you also have to indicate the data type of the
parameter. There is an ActionScript class defined for each event, and the data type you specify
for the function parameter is always the class associated with the particular event you want to
respond to. Finally, between the opening and closing curly braces ({ ... }), you write the
instructions you want the computer to carry out when the event happens.
Working with objects 35
Once you’ve written the event-handling function, you need to tell the event source object (the
object that the event happens to—for example, the button) that you want your function to be
called when the event happens. You do this by calling the addEventListener() method of
that object (all objects that have events also have an addEventListener() method). The
addEventListener() method takes two parameters:
■ First, the name of the specific event you want to respond to. Once again, each event is
affiliated with a specific class, and that class will have a special value predefined for each
event—sort of like the event’s own unique name, which you should use for the first
parameter.
■ Second, the name of your event response function. Note that a function name is written
without parentheses when it’s passed as a parameter.
Examining the event-handling process
The following is a step-by-step description of the process that happens when you create an
event listener. In this case, it’s an example of creating a listener function that is called when an
object named myButton is clicked.
The actual code written by the programmer is as follows:
function eventResponse(event:MouseEvent):void
{
// Actions performed in response to the event go here.
}
myButton.addEventListener(MouseEvent.CLICK, eventResponse);
36 Getting started with ActionScript
Here is how this code would actually work when it’s running in Flash Player:
1. When the SWF file loads, Flash Player makes note of the fact that there’s a function named
eventResponse().
2. Flash Player then runs the code (specifically, the lines of code that aren’t in a function). In
this case that’s only one line of code: calling the addEventListener() method on the
event source object (named myButton) and passing the eventResponse function as a
parameter.
Working with objects 37
a. Internally, myButton has a list of functions that are listening to each of its events, so
when its addEventListener() method is called, myButton stores the
eventResponse() function in its list of event listeners.
3. At some point, the user clicks the myButton object, triggering its click event (identified
as MouseEvent.CLICK in the code).
At that point, the following occurs:
38 Getting started with ActionScript
a. Flash Player creates an object, an instance of the class associated with the event in
question (MouseEvent in this example). For many events this will be an instance of the
Event class; for mouse events it will be a MouseEvent instance; and for other events it
will be an instance of the class that’s associated with that event. This object that’s
created is known as the event object, and it contains specific information about the
event that happened: what type of event it is, where it happened, and other event-
specific information if applicable.
b. Flash Player then looks at the list of event listeners stored by myButton. It goes
through these functions one by one, calling each function and passing the event object
to the function as a parameter. Since the eventResponse() function is one of
myButton’s listeners, as part of this process Flash Player calls the eventResponse()
function.
Working with objects 39
c. When the eventResponse() function is called, the code in that function runs, so
your specified actions are carried out.
Event-handling examples
Here are a few more concrete examples of events to give you an idea of some of the common
event elements and possible variations available when you write event-handling code:
■ Clicking a button to start the current movie clip playing. In the following example,
playButton is the instance name of the button, and this is a special name meaning “the
current object”:
this.stop();
function playMovie(event:MouseEvent):void
{
this.play();
}
playButton.addEventListener(MouseEvent.CLICK, playMovie);
■ Detecting typing in a text field. In this example, entryText is an input text field, and
outputText is a dynamic text field:
function updateOutput(event:TextEvent):void
{
var pressedKey:String = event.text;
outputText.text = "You typed: " + pressedKey;
}
entryText.addEventListener(TextEvent.TEXT_INPUT, updateOutput);
40 Getting started with ActionScript
■ Clicking a button to navigate to a URL. In this case, linkButton is the instance name of
the button:
function gotoAdobeSite(event:MouseEvent):void
{
var adobeURL:URLRequest = new URLRequest("http://www.adobe.com/");
navigateToURL(adobeURL);
}
linkButton.addEventListener(MouseEvent.CLICK, gotoAdobeSite);
Creating object instances
Of course, before you can use an object in ActionScript, the object has to exist in the first
place. One part of creating an object is declaring a variable; however, declaring a variable only
creates an empty place in the computer’s memory. You must assign an actual value to the
variable—that is, create an object and store it in the variable—before you attempt to use or
manipulate it. The process of creating an object is known as instantiating the object—in other
words, creating an instance of a particular class.
One simple way to create an object instance doesn’t involve ActionScript at all. In Flash, when
you place a movie clip symbol, button symbol, or text field on the Stage, and you assign it an
instance name in the Property inspector, Flash automatically declares a variable with that
instance name, creates an object instance, and stores that object in the variable. Likewise, in
Adobe Flex Builder when you create a component in Macromedia® MXML™ from Adobe
(either by coding an MXML tag or by placing the component on the editor in Design mode)
and assign an ID to that component (in the MXML markup or in the Flex Properties view),
that ID becomes the name of an ActionScript variable, and an instance of the component is
created and stored in the variable.
However, you won’t always want to create an object visually. There are also several ways you
can create object instances using only ActionScript. First, with several ActionScript data types,
you can create an instance using a literal expression—a value written directly into the
ActionScript code. Here are some examples:
■ Literal numeric value (enter the number directly):
var someNumber:Number = 17.239;
var someNegativeInteger:int = -53;
var someUint:uint = 22;
■ Literal String value (surround the text with double quotation marks):
var firstName:String = "George";
var soliloquy:String = "To be or not to be, that is the question...";
Working with objects 41
■ Literal Boolean value (use the literal values true or false):
var niceWeather:Boolean = true;
var playingOutside:Boolean = false;
■ Literal XML value (enter the XML directly):
var employee:XML = <employee>
<firstName>Harold</firstName>
<lastName>Webster</lastName>
</employee>;
ActionScript also defines literal expressions for the Array, RegExp, Object, and Function data
types. For details on these classes, see “Working with arrays” on page 223, “Using regular
expressions” on page 285, and “Object data type” on page 97.
For any other data type, to create an object instance you use the new operator with the class
name, like this:
var raceCar:MovieClip = new MovieClip();
var birthday:Date = new Date(2006, 7, 9);
Creating an object using the new operator is often referred to as “calling the class’s
constructor.” A constructor is a special method that is called as part of the process of creating
an instance of a class. Notice that when you create an instance in this way, you put parentheses
after the class name, and sometimes you specify parameter values—two things that you also
do when calling a method.
It’s important to be familiar with the new ClassName() way of creating objects. If you need
to create an instance of any ActionScript data type that doesn’t have a visual representation
(and hence can’t be created by placing an item on the Flash Stage or the Design mode of Flex
Builder’s MXML editor), you can only do so by creating the object directly in ActionScript
using the new operator.
In Flash specifically, the new operator can also be used to create an instance of a movie clip
symbol that is defined in the Library but isn’t placed on the Stage. For more about this, see
“Creating MovieClip objects with ActionScript” on page 498.
NOTE
Even for those data types that let you create instances using a literal expression, you can
still use the new operator to create an object instance. For instance, these two lines of
code do exactly the same thing:
var someNumber:Number = 6.33;
var someNumber:Number = new Number(6.33);
42 Getting started with ActionScript
Common program elements
In addition to declaring variables, creating object instances, and manipulating objects using
their properties and methods, there are a few other building blocks that you use to create an
ActionScript program.
Operators
Operators are special symbols (or occasionally words) that are used to perform calculations.
They are mostly used for math operations, and also used when comparing values to each
other. As a general rule, an operator uses one or more values and “works out” to a single result.
For example:
■ The addition operator (+) adds two values together, resulting in a single number:
var sum:Number = 23 + 32;
■ The multiplication operator (*) multiplies one value by another, resulting in a single
number:
var energy:Number = mass * speedOfLight * speedOfLight;
■ The equality operator (==) compares two values to see if they are equal, resulting in a
single true-or-false (Boolean) value:
if (dayOfWeek == "Wednesday")
{
takeOutTrash();
}
As shown here, the equality operator and the other “comparison” operators are most
commonly used with the if statement to determine if certain instructions should be
carried out or not.
For more details and examples of using operators, see “Operators” on page 109.
Comments
As you’re writing ActionScript, you’ll often want to leave notes to yourself, perhaps explaining
how certain lines of code work or why you made a particular choice. Code comments are a tool
you can use to write text that the computer should ignore in your code. ActionScript includes
two kinds of comments:
■ Single-line comment: A single-line comment is designated by placing two slashes
anywhere on a line. Everything after the slashes up to the end of that line is ignored by the
computer:
Common program elements 43
// This is a comment; it’s ignored by the computer.
var age:Number = 10; // Set the age to 10 by default.
■ Multiline comments: A multiline comment includes a starting comment marker (/*),
then the comment content, and an ending comment marker (*/). Everything between the
starting and ending markers is ignored by the computer, regardless of how many lines the
comment spans:
/*
This might be a really long description, perhaps describing what
a particular function is used for or explaining a section of code.
In any case, these lines are all ignored by the computer.
*/
Another common use of comments is to temporarily “turn off” one or more lines of code—
for example, if you’re testing out a different way of doing something, or trying to figure out
why certain ActionScript code isn’t working the way you expect.
Flow control
Many times in a program, you will want to repeat certain actions, perform only certain
actions and not others, perform alternative actions depending on certain conditions, and so
on. Flow control is the control over which actions are performed. There are several types of
flow control elements available in ActionScript.
■ Functions: Functions are like shortcuts—they provide a way to group a series of actions
under a single name, and can be used to perform calculations. Functions are particularly
important for handling events, but are also used as a general tool for grouping a series of
instructions. For more on functions, see “Functions” on page 122.
■ Loops: Loop structures let you designate a set of instructions that the computer will
perform a set number of times or until some condition changes. Often loops are used to
manipulate several related items, using a variable whose value changes each time the
computer works through the loop. For more on loops, see “Looping” on page 119.
■ Conditional statements: Conditional statements provide a way to designate certain
instructions that are carried out only under certain circumstances or to provide alternative
sets of instructions for different conditions. The most common type of conditional
statement is the if statement. The if statement checks a value or expression in its
parentheses. If the value is true, the lines of code in curly braces are carried out;
otherwise, they are ignored. For example:
if (age < 20)
{
// show special teenager-targeted content
}
44 Getting started with ActionScript
The if statement’s companion, the else statement, lets you designate alternative
instructions to be performed if the condition is not true:
if (username == "admin")
{
// do some administrator-only things, like showing extra options
}
else
{
// do some non-administrator things
}
For more on conditional statements, see “Conditionals” on page 117.
Example: Animation portfolio piece
This example is designed to give you a first opportunity to see how you can piece together bits
of ActionScript into a complete, if not ActionScript-heavy, application. The animation
portfolio piece is an example of how you could take an existing linear animation (for example,
a piece created for a client) and add some minor interactive elements appropriate for
incorporating that animation into an online portfolio. The interactive behavior that we’ll add
to the animation will include two buttons the viewer can click: one to start the animation,
and one to navigate to a separate URL (such as the portfolio menu or the author’s home
page).
The process of creating this piece can be divided into these main sections:
1. Prepare the FLA file for adding ActionScript and interactive elements.
2. Create and add the buttons.
3. Write the ActionScript code.
4. Test the application.
Preparing to add interactivity
Before we can add interactive elements to our animation, it’s helpful to set up the FLA file by
creating some places to add our new content. This includes creating actual space on the Stage
where buttons can be placed, and also creating “space” in the FLA file for keeping different
items separate.
Example: Animation portfolio piece 45
To set up your FLA for adding interactive elements:
1. If you don’t already have a linear animation to which you’ll be adding interactivity, create
a new FLA file with a simple animation such as a single motion tween or shape tween.
Otherwise, open the FLA file containing the animation that you’re showcasing in the
project, and save it with a new name to create a new working file.
2. Decide where on the screen you’ll want the two buttons to appear (one to start the
animation and one to link to the author portfolio or home page). If necessary, clear or add
some space on the Stage for this new content. If the animation doesn’t already have one,
you might want to create a splash screen on the first frame (you’ll probably want to shift
the animation over so it starts on Frame 2 or later).
3. Add a new layer, above the other layers in the Timeline, and rename it buttons. This will
be the layer where you’ll add the buttons.
4. Add a new layer, above the buttons layer, and name it actions. This will be where you’ll
add ActionScript code to your application.
Creating and adding buttons
Next we’ll need to actually create and position the buttons that will form the center of our
interactive application.
To create and add buttons to the FLA:
1. Using the drawing tools, create the visual appearance of your first button (the “play”
button) on the buttons layer. For example, you might draw a horizontal oval with text on
top of it.
2. Using the Selection tool, select all the graphic parts of the single button.
3. From the main menu, choose Modify > Convert To Symbol.
4. In the dialog box, choose Button as the symbol type, give the symbol a name, and click OK.
5. With the button selected, in the Property inspector give the button the instance name
playButton.
6. Repeat steps 1 through 5 to create the button that will take the viewer to the author’s home
page. Name this button homeButton.
Writing the code
The ActionScript code for this application can be divided into three sets of functionality,
although it will all be entered in the same place. The three things the code needs to do are:
■ Stop the playhead as soon as the SWF file loads (when the playhead enters Frame 1).
46 Getting started with ActionScript
■ Listen for an event to start the SWF file playing when the user clicks the play button.
■ Listen for an event to send the browser to the appropriate URL when the user clicks the
author home page button.
To create code to stop the playhead when it enters Frame 1:
1. Select the keyframe on Frame 1 of the actions layer.
2. To open the Actions panel, from the main menu, choose Window > Actions.
3. In the Script pane, enter the following code:
stop();
To write code to start the animation when the play button is clicked:
1. At the end of the code entered in the previous steps, add two empty lines.
2. Enter the following code at the bottom of the script:
function startMovie(event:MouseEvent):void
{
this.play();
}
This code defines a function called startMovie(). When startMovie() is called, it
causes the main timeline to start playing.
3. On the line following the code added in the previous step, enter this line of code:
playButton.addEventListener(MouseEvent.CLICK, startMovie);
This line of code registers the startMovie() function as a listener for playButton’s
click event. In other words, it makes it so that whenever the button named playButton
is clicked, the startMovie() function is called.
To write code to send the browser to a URL when the home page button is
clicked:
1. At the end of the code entered in the previous steps, add two empty lines.
2. Enter this code at the bottom of the script:
function gotoAuthorPage(event:MouseEvent):void
{
var targetURL:URLRequest = new URLRequest("http://example.com/");
navigateToURL(targetURL);
}
This code defines a function called gotoAuthorPage(). This function first creates a
URLRequest instance representing the URL http://example.com/, and then passes that
URL to the navigateToURL() function, causing the user’s browser to open that URL.
3. On the line following the code added in the previous step, enter this line of code:
homeButton.addEventListener(MouseEvent.CLICK, gotoAuthorPage);
Building applications with ActionScript 47
This line of code registers the gotoAuthorPage() function as a listener for homeButton’s
click event. In other words, it makes it so that whenever the button named homeButton
is clicked, the gotoAuthorPage() function is called.
Testing the application
At this point, the application should be completely functional. Let’s test it to make sure that’s
the case.
To test the application:
1. From the main menu, choose Control > Test Movie. Flash creates the SWF file and opens
it in a Flash Player window.
2. Try both the buttons to make sure they do what you expect them to.
3. If the buttons don’t work, here are some things to check for:
■ Do the buttons both have distinct instance names?
■ Do the addEventListener() method calls use the same names as the buttons’
instance names?
■ Are the correct event names used in the addEventListener() method calls?
■ Is the correct parameter specified for each of the functions? (Both should have a single
parameter with the data type MouseEvent.)
All of these and most other possible mistakes should give an error message either when
you choose the Test Movie command or when you click the button. Look in the Compiler
Errors panel for compiler errors (the ones that happen when you first choose Test Movie),
and check the Output panel for run-time errors (errors which happen while the SWF is
playing—such as when you click a button).
Building applications with ActionScript
The process of writing ActionScript to build an application involves more than just knowing
the syntax and the names of the classes you’ll use. While most of the information in this
manual is geared towards those two topics (syntax and using ActionScript classes), you’ll also
want to know some information such as what programs can be used for writing ActionScript,
how ActionScript code can be organized and included in an application, and what steps you
should follow in developing an ActionScript application.
48 Getting started with ActionScript
Options for organizing your code
You can use ActionScript 3.0 code to power everything from simple graphics animations to
complex client-server transaction processing systems. Depending on the type of application
you’re building, you may prefer to use one or more of these different ways of including
ActionScript in your project.
Storing code in frames in a Flash timeline
In the Flash authoring environment, you can add ActionScript code to any frame in a
timeline. This code will be executed while the movie is playing back, when the playhead
enters that frame.
Placing ActionScript code in frames provides a simple way to add behaviors to applications
built in the Flash authoring tool. You can add code to any frame in the main timeline or to
any frame in the timeline of any MovieClip symbol. However, this flexibility comes with a
cost. When you build larger applications, it becomes easy to lose track of which frames
contain which scripts. This can make the application more difficult to maintain over time.
Many developers simplify the organization of their ActionScript code in the Flash authoring
tool by placing code only in the first frame of a timeline, or on a specific layer in the Flash
document. This makes it easier to locate and maintain the code in your Flash FLA files.
However, in order to use the same code in another Flash project, you must copy and paste the
code into the new file.
If you want to be able to use your ActionScript code in other Flash projects in the future, you
will want to store your code in external ActionScript files (text files with the .as extension).
Storing code in ActionScript files
If your project involves significant ActionScript code, the best way to organize your code is in
separate ActionScript source files (text files with the .as extension). An ActionScript file can be
structured in one of two ways, depending on how you intend to use it in your application.
■ Unstructured ActionScript code: Lines of ActionScript code, including statements or
function definitions, written as though they were entered directly in a timeline script,
MXML file, and so on.
Building applications with ActionScript 49
ActionScript written in this way can be accessed using the include statement in
ActionScript, or the <mx:Script> tag in Adobe Flex MXML. The ActionScript include
statement causes the contents of an external ActionScript file to be inserted at a specific
location and within a given scope in a script, as if it were entered there directly. In the Flex
MXML language, the <mx:Script> tag lets you specify a source attribute that identifies
an external ActionScript file to be loaded at that point in the application. For example, the
following tag will load an external ActionScript file named Box.as:
<mx:Script source=“Box.as” />
■ ActionScript class definition: A definition of an ActionScript class, including its method
and property definitions.
When you define a class, you can access the ActionScript code in the class by creating an
instance of the class and using its properties, methods, and events, just as you would with
any of the built-in ActionScript classes. This requires two parts:
■ Use the import statement to specify the full name of the class, so the ActionScript
compiler knows where to find it. For example, if you want to use the MovieClip class
in ActionScript, you first need to import that class using its full name, including
package and class:
import flash.display.MovieClip;
Alternatively, you can import the package that contains the MovieClip class, which is
equivalent to writing separate import statements for each class in the package:
import flash.display.*;
The only exceptions to the rule that a class must be imported if you refer to that class
in your code are the top-level classes, which are not defined in a package.
■ Write code which specifically refers to the class name (usually declaring a variable with
that class as its data type, and creating an instance of the class to store in the variable).
By referring to another class name in ActionScript code, you tell the compiler to load
the definition of that class. For example, given an external class called Box, this
statement causes a new instance of the Box class to be created:
var smallBox:Box = new Box(10,20);
When the compiler comes across the reference to the Box class for the first time, it
searches the loaded source code to locate the Box class definition.
NOTE
In Flash, for scripts attached to frames on the Timeline, the built-in classes (in the
flash.* packages) are automatically imported. However, when you write your own
classes, or if you’re working with Flash authoring components (the fl.* packages)
or if you’re working in Flex, you will need to explicitly import any class in order to
write code that creates instances of that class.
50 Getting started with ActionScript
Choosing the right tool
Depending on the needs of your project and the resources you have available to you, you may
want to use one of several tools (or multiple tools in conjunction with each other) for writing
and editing your ActionScript code.
Flash authoring tool
In addition to its graphics and animation creation capabilities, Adobe Flash CS3 Professional
includes tools for working with ActionScript code, either attached to elements in a FLA file or
in external ActionScript-only files. The Flash authoring tool is ideal for projects that involve
significant animation or video or where you want to create most of the graphic assets yourself,
particularly projects with minimal user interaction or functionality requiring ActionScript.
Another reason you may choose to use the Flash authoring tool to develop your ActionScript
project is if you prefer to create visual assets and write code in the same application. You may
also want to use Flash authoring if you want to use pre-built user interface components, but
smaller SWF size or easier visual skinning are key priorities for your project.
Adobe Flash CS3 Professional includes two tools for writing ActionScript code:
■ Actions panel: Available when working in a FLA file, this panel allows you to write
ActionScript code attached to frames on a timeline.
■ Script window: The Script window is a dedicated text editor for working with
ActionScript (.as) code files.
Flex Builder
Adobe Flex Builder is the premier tool for creating projects with the Flex framework. In
addition to its visual layout and MXML editing tools, Flex Builder also includes a full-
featured ActionScript editor, so it can be used to create Flex or ActionScript-only projects.
Flex applications have several benefits, including a rich set of pre-built user interface controls,
flexible dynamic layout controls, and built-in mechanisms for working with external data
sources and linking external data to user interface elements. However, because of the
additional code required to provide these features, Flex applications can have a larger SWF file
size and can’t be completely re-skinned as easily as their Flash counterparts.
Use Flex Builder if you are creating full-featured, data-driven rich Internet applications with
Flex, and you want to edit ActionScript code, edit MXML code, and lay out your application
visually, all within a single tool.
Building applications with ActionScript 51
Third-party ActionScript editor
Because ActionScript (.as) files are stored as simple text files, any program that is capable of
editing plain text files can be used to write ActionScript files. In addition to Adobe’s
ActionScript products, several third-party text editing programs with ActionScript-specific
capabilities have been created. You can write an MXML file or ActionScript classes using any
text editor program. You can then create a SWF application (either a Flex or an ActionScript-
only application) from those files using the Flex SDK, which includes the Flex framework
classes as well as the Flex compiler. Alternatively, many developers use a third-party
ActionScript editor for writing ActionScript classes, in combination with the Flash authoring
tool for creating graphical content.
You might choose to use a third-party ActionScript editor if:
■ You prefer to write ActionScript code in a separate program in conjunction with designing
visual elements in Flash.
■ You use an application for non-ActionScript programming (such as creating HTML pages
or building applications in another programming language), and you want to use the same
application for your ActionScript coding as well.
■ You want to create ActionScript-only or Flex projects using the Flex SDK without the
expense of Flash or Flex Builder.
Some of the notable code editors providing ActionScript-specific support include:
■ Adobe Dreamweaver® CS3
■ ASDT
■ FDT
■ FlashDevelop
■ PrimalScript
■ SE|PY
■ XCode (with ActionScript template and code-hint files)
The ActionScript development process
No matter whether your ActionScript project is large or small, using a process to design and
develop your application will help you work more efficiently and effectively. The following
steps describe a basic development process for building an application that uses ActionScript
3.0:
1. Design your application.
You should describe your application in some way before you start building it.
52 Getting started with ActionScript
2. Compose your ActionScript 3.0 code.
You can create ActionScript code using Flash, Flex Builder, Dreamweaver, or a text editor.
3. Create a Flash or Flex application file to run your code.
In the Flash authoring tool, this involves creating a new FLA file, setting up the publish
settings, adding user interface components to the application, and referencing the
ActionScript code. In the Flex development environment, creating a new application file
involves defining the application and adding user interface components using MXML,
and referencing the ActionScript code.
4. Publish and test your ActionScript application.
This involves running your application from within the Flash authoring or Flex
development environment, and making sure it does everything you intended.
Note that you don’t necessarily have to follow these steps in order, or completely finish one
step before working on another. For example, you might design one screen of your application
(step 1), and then create the graphics, buttons, and so forth (step 3), before writing
ActionScript code (step 2) and testing (step 4). Or you might design part of it, and then add
one button or interface element at a time, writing ActionScript for each one and testing it as
it’s built. Although it’s helpful to remember these four stages of the development process, in
real-world development it’s usually more effective to move back and forth among the stages as
appropriate.
Creating your own classes 53
Creating your own classes
The process of creating classes for use in your projects can seem daunting. However, the more
difficult part of creating a class is the task of designing the class—identifying the methods,
properties, and events that it will include.
Strategies for designing a class
The topic of object-oriented design is a complex one; entire careers have been devoted to the
academic study and professional practice of this discipline. Nevertheless, here are a few
suggested approaches that can help you get started.
1. Think about the role that the instances of this class will play in the application. Generally,
objects serve one of these three roles:
■ Value object: These objects serve primarily as containers of data—that is, they likely
have several properties and fewer methods (or sometimes no methods). They are
generally code representations of clearly defined items, such as a Song class
(representing a single real-world song) or Playlist class (representing a conceptual
group of songs) in a music player application.
■ Display object: These are objects that actually appear on the screen. Examples include
user-interface elements like a drop-down list or status readout, graphical elements like
creatures in a video game, and so forth.
■ Application structure: These objects play a broad range of supporting roles in the logic
or processing performed by applications. Examples include an object that performs
certain calculations in a biology simulation; one that is responsible for synchronizing
values between a dial control and a volume readout in a music player application; one
that manages the rules in a video game; or one that loads a saved picture in a drawing
application.
2. Decide the specific functionality that the class will need. The different types of
functionality often become the methods of the class.
3. If the class is intended to serve as a value object, decide the data that the instances will
include. These items are good candidates for properties.
4. Since your class is being designed specifically for your project, what’s most important is that
you provide the functionality that your application needs. It might help to answer these
questions for yourself:
54 Getting started with ActionScript
■ What pieces of information will your application be storing, tracking, and
manipulating? Deciding this helps you identify any value objects and properties you
may want.
■ What sets of actions will need to be performed—for example, when the application
first loads, when a particular button is clicked, when a movie stops playing, and so
forth? These are good candidates for methods (or properties, if the “actions” just
involve changing individual values).
■ For any given action, what information will the class need to know in order to perform
that action? Those pieces of information become the parameters of the method.
■ As the application proceeds to do its work, what things will change in your class that
other parts of your application will need to know about? These are good candidates for
events.
5. If there is an existing object that is similar to the object you need, except that it’s lacking
some additional functionality you want to add, consider creating a subclass (a class which
builds on the functionality of an existing class, rather than defining all of its own
functionality). For example, if you want to create a class that will be a visual object on the
screen, you’ll want to use the behavior of one of the existing display objects (for example,
Sprite or MovieClip) as a basis for your class. In that case, MovieClip (or Sprite) would be
the base class, and your class would extend that class. For more information about creating
a subclass, see “Inheritance” on page 162.
Writing the code for a class
Once you have a design plan for your class, or at least some idea of what information it will
need to keep track of and what actions it will need to carry out, the actual syntax of writing a
class is fairly straightforward.
Here are the minimum steps to create your own ActionScript class:
1. Open a new text document, in an ActionScript-specific program such as Flex Builder or
Flash, in a general programming tool such as Dreamweaver, or in any program that allows
you to work with plain text documents.
2. Enter a class statement to define the name of the class. To do this, enter the words public
class, and then the class’s name, followed by opening and closing curly braces that will
surround the contents of the class (the method and property definitions). For example:
public class MyClass
{
}
Creating your own classes 55
The word public indicates that the class can be accessed from any other code. For other
alternatives, see “Access control namespace attributes” on page 144.
3. Type a package statement to indicate the name of the package in which your class will be
found. The syntax is the word package, followed by the full package name, followed by
opening and closing curly braces (which will surround the class statement block). For
example, we’d change the code in the previous step to this:
package mypackage
{
public class MyClass
{
}
}
4. Define each property in the class using the var statement within the class body; the syntax
is the same as you use to declare any variable (with the addition of the public modifier).
For example, adding these lines between the opening and closing curly braces of the class
definition will create properties named textVariable, numericVariable, and
dateVariable:
public var textVariable:String = "some default value";
public var numericVariable:Number = 17;
public var dateVariable:Date;
5. Define each method in the class using the same syntax that’s used to define a function. For
example:
■ To create a myMethod() method, enter:
public function myMethod(param1:String, param2:Number):void
{
// do something with parameters
}
■ To create a constructor (the special method that is called as part of the process of
creating an instance of a class), create a method whose name matches exactly the name
of the class:
public function MyClass()
{
// do stuff to set initial values for properties
// and otherwise set up the object
textVariable = "Hello there!";
dateVariable = new Date(2001, 5, 11);
}
If you don’t include a constructor method in your class, the compiler will
automatically create an empty constructor (one with no parameters and no
statements) in your class.
56 Getting started with ActionScript
There are a few more class elements that you can define.These elements are more involved.
■ Accessors are a special cross between a method and a property. When you write the code to
define the class, you write the accessor like a method so you can perform multiple actions
(rather than just reading or assigning a value, which is all you can do when you define a
property). However, when you create an instance of your class, you treat the accessor like a
property—using just the name to read or assign the value. For more information, see “Get
and set accessor methods” on page 153.
■ Events in ActionScript aren’t defined using a specific syntax. Instead, you define events in
your class by using the functionality of the EventDispatcher class to keep track of event
listeners and notify them of events. For more on creating events in your own classes, see
Chapter 10, “Handling events,” on page 313.
Suggestions for organizing your classes
Unlike previous ActionScript versions, ActionScript 3.0 does not have the one file, one class
restriction that limits you to using only one class per file. Using ActionScript 3.0, you can save
the source code for more than one class in a single .as file. In some cases, it might seem more
convenient to pack multiple classes into a single source file, but in general, this is considered a
bad programming practice, for a couple of reasons:
■ It is difficult to reuse individual classes if they are packed together into a single large file.
■ It is difficult to locate the source code for a specific class when its filename does not
correspond to the class name.
For these reasons, Adobe recommends that you always save the source code for each
individual class in its own file, and give the file the same name as the class.
Example: Creating a basic application
You can create external ActionScript source files with an .as extension using Flash, Flex
Builder, Dreamweaver, or any text editor.
ActionScript 3.0 can be used within a number of application development environments,
including the Flash authoring and Flex Builder tools.
This section walks through the steps in creating and enhancing a simple ActionScript 3.0
application using the Flash authoring tool or the Flex Builder 2 tool. The application you’ll
build presents a simple pattern for using external ActionScript 3.0 class files in Flash and Flex
applications. That pattern will apply to all of the other sample applications in this manual.
Example: Creating a basic application 57
Designing your ActionScript application
You should have some idea about the application you want to build before you start building it.
The representation of your design can be as simple as the name of the application and a brief
statement of its purpose, or as complicated as a set of requirements documents containing
numerous Unified Modeling Language (UML) diagrams. This manual doesn’t discuss the
discipline of software design in detail, but it’s important to keep in mind that application
design is an essential step in the development of ActionScript applications.
Our first example of an ActionScript application will be a standard “Hello World”
application, so its design is very simple:
■ The application will be called HelloWorld.
■ It will display a single text field containing the words “Hello World!”
■ In order to be easily reused, it will use a single object-oriented class, named Greeter, which
can be used from within a Flash document or a Flex application.
■ After you create a basic version of the application, you will add new functionality to have
the user enter a user name and have the application check the name against a list of known
users.
With that concise definition in place, you can start building the application itself.
Creating the HelloWorld project and the Greeter class
The design statement for the Hello World application said that its code should be easy to
reuse. With this goal in mind, the application uses a single object-oriented class, named
Greeter, which is used from within an application that you create in Flex Builder or the Flash
authoring tool.
To create the Greeter class in the Flash authoring tool:
1. In the Flash authoring tool, select File > New.
2. In the New Document dialog box, select ActionScript file, and click OK.
A new ActionScript editing window is displayed.
3. Select File > Save. Select a folder to contain your application, name the ActionScript file
Greeter.as, and then click OK.
Continue with “Adding code to the Greeter class” on page 58.
58 Getting started with ActionScript
Adding code to the Greeter class
The Greeter class defines an object, Greeter, that you will be able to use in your HelloWorld
application.
To add code to the Greeter class:
1. Type the following code into the new file:
package
{
public class Greeter
{
public function sayHello():String
{
var greeting:String;
greeting = "Hello World!";
return greeting;
}
}
}
The Greeter class includes a single sayHello() method, which returns a string that says
“Hello” to the user name that is given.
2. Select File > Save to save this ActionScript file.
The Greeter class is now ready to be used in a Flash or Flex application.
Creating an application that uses your ActionScript
code
The Greeter class that you have built defines a self-contained set of software functions, but it
does not represent a complete application. To use the class, you need to create a Flash
document or Flex application.
The HelloWorld application creates an new instance of the Greeter class. Here’s how to attach
the Greeter class to your application.
To create an ActionScript application using the Flash authoring tool:
1. Select File > New.
2. In the New Document dialog box, select Flash Document, and click OK.
A new Flash window is displayed.
3. Select File > Save. Select the same folder that contains the Greeter.as class file, name the
Flash document HelloWorld.fla, and click OK.
Example: Creating a basic application 59
4. In the Flash Tools palette, select the Text tool, and drag across the Stage to define a new
text field, approximately 300 pixels wide and 100 pixels high.
5. In the Properties window, with the text field still selected on the Stage, type mainText as
the instance name of the text field.
6. Click the first frame of the main timeline.
7. In the Actions panel, type the following script:
var myGreeter:Greeter = new Greeter();
mainText.text = myGreeter.sayHello("Bob");
8. Save the file.
Continue with “Publishing and testing your ActionScript application” on page 59.
Publishing and testing your ActionScript application
Software development is an iterative process. You write some code, try to compile it, and edit
the code until it compiles cleanly. You run the compiled application, test it to see if it fulfills
the intended design, and if it doesn’t, you edit the code again until it does. The Flash and Flex
Builder development environments offer a number of ways to publish, test, and debug your
applications.
Here are the basic steps for testing the HelloWorld application in each environment.
To publish and test an ActionScript application using the Flash authoring tool:
1. Publish your application and watch for compilation errors. In the Flash authoring tool,
select Control > Test Movie to compile your ActionScript code and run the HelloWorld
application.
2. If any errors or warnings are displayed in the Output window when you test your
application, fix the causes of these errors in the HelloWorld.fla or HelloWorld.as files, and
then try testing the application again.
3. If there are no compilation errors, you will see a Flash Player window showing the Hello
World application. The “Hello, Bob” text is displayed.
You have just created a simple but complete object-oriented application that uses ActionScript
3.0. Continue with “Enhancing the HelloWorld application” on page 60.
60 Getting started with ActionScript
Enhancing the HelloWorld application
To make the application a little more interesting, you’ll now make it ask for and validate a user
name against a predefined list of names.
First, you will update the Greeter class to add new functionality. Then you will update the
Flex or Flash application to use the new functionality.
To update the Greeter.as file:
1. Open the Greeter.as file.
2. Change the contents of the file to the following (new and changed lines are shown in
boldface):
package
{
public class Greeter
{
/**
* Defines the names that should receive a proper greeting.
*/
public static var validNames:Array = ["Sammy", "Frank", "Dean"];
/**
* Builds a greeting string using the given name.
*/
public function sayHello(userName:String = ""):String
{
var greeting:String;
if (userName == "")
{
greeting = "Hello. Please type your user name, and then press
the Enter key.";
}
else if (validName(userName))
{
greeting = "Hello, " + userName + ".";
}
else
{
greeting = "Sorry, " + userName + ", you are not on the list.";
}
return greeting;
}
/**
* Checks whether a name is in the validNames list.
*/
public static function validName(inputName:String = ""):Boolean
{
Example: Creating a basic application 61
if (validNames.indexOf(inputName) > -1)
{
return true;
}
else
{
return false;
}
}
}
}
The Greeter class now has a number of new features:
■ The validNames array lists valid user names. The array is initialized to a list of three
names when the Greeter class is loaded.
■ The sayHello() method now accepts a user name and changes the greeting based on
some conditions. If the userName is an empty string (""), the greeting property is set
to prompt the user for a name. If the user name is valid, the greeting becomes "Hello,
userName.” Finally, if either of those two conditions are not met, the greeting
variable is set to "Sorry, userName, you are not on the list."
■ The validName() method returns true if the inputName is found in the validNames
array, and false if it is not found. The statement validNames.indexOf(inputName)
checks each of the strings in the validNames array against the inputName string. The
Array.indexOf() method returns the index position of the first instance of an object
in an array, or the value -1 if the object is not found in the array.
Next you will edit the Flash or Flex file that references this ActionScript class.
To modify the application using the Flash authoring tool:
1. Open the HelloWorld.fla file.
2. Modify the script in Frame 1 so that an empty string ("") is passed to the Greeter class’s
sayHello() method:
var myGreeter:Greeter = new Greeter();
mainText.text = myGreeter.sayHello("");
3. Select the Text tool in the Tools palette, and then create two new text fields on the Stage,
side-by-side and directly under the existing mainText text field.
4. In the first new text field, type the text User Name: to serve as a label.
5. Select the other new text field, and in the Property inspector, select InputText as the type
of text field. Type textIn as the instance name.
6. Click the first frame of the main timeline.
7. In the Actions panel, add the following lines to the end of the existing script:
62 Getting started with ActionScript
mainText.border = true;
textIn.border = true;
textIn.addEventListener(KeyboardEvent.KEY_UP, keyPressed);
function keyPressed(event:Event):void
{
if (event.keyCode == Keyboard.ENTER)
{
mainText.text = myGreeter.sayHello(textIn.text);
}
}
The new code adds the following functionality:
■ The first two lines simply define borders for two text fields.
■ An input text field, such as the textIn field, has a set of events that it can dispatch.
The addEventListener() method lets you define a function that runs when a type of
event occurs. In this case, that event is the pressing of the Enter key on the keyboard.
■ The keyPressed() custom function calls the sayHello() method of the myGreeter
object, passing the text from the textIn text field as a parameter. That method returns
a string greeting based on the value passed in. The returned string is then assigned to
the text property of the mainText text field.
The complete script for Frame 1 is the following:
mainText.border = true;
textIn.border = true;
var myGreeter:Greeter = new Greeter();
mainText.text = myGreeter.sayHello("");
textIn.addEventListener(KeyboardEvent.KEY_UP, keyPressed);
function keyPressed(event:Event):void
{
if (event.keyCode == Keyboard.ENTER)
{
mainText.text = myGreeter.sayHello(textIn.text);
}
}
8. Save the file.
9. Select Control > Test Movie to run the application.
When you run the application, you will be prompted to enter a user name. If it is valid
(Sammy, Frank, or Dean), the application displays the “hello” confirmation message.
Running subsequent examples 63
Running subsequent examples
Now that you’ve developed and run the “Hello World” ActionScript 3.0 application, you
should have the basic knowledge you need to run the other code examples presented in this
manual.
Testing in-chapter example code listings
As you’re working through this manual, you may want to try out the example code listings
that are used to illustrate the various topics. That testing may involve displaying the value of
variables at certain points in the program, or it may involve viewing or interacting with on-
screen content. For testing visual content or interaction, the necessary elements will be
described before or within the code listing—you’ll just need to create a document with the
elements as described in order to test the code. In the case where you want to view the value of
a variable at a given point in the program, there are a few ways you can accomplish this. One
way is to use a debugger, such as the ones built into Flex Builder and Flash. For simple testing,
however, it might be easiest to just print variable values out to some place where you can view
them.
The following steps will help you create a Flash document that you can use for testing a code
listing and viewing variable values:
To create a Flash document for testing in-chapter examples:
1. Create a new Flash document and save it on your hard drive.
2. To display test values in a text field on the Stage, activate the Text tool and create a new
Dynamic text field on the Stage. A wide, tall text field with the Line type set to Multiline
and the border turned on will be most useful. In the Property inspector, give the text field
an instance name (for example “outputText”). To write values into the text field, you will
add code that calls the appendText() method to the example code (described below).
3. Alternatively, you can add a trace() function call to the code listing (as described below)
to view the results of the example.
4. To test a given example, copy the code listing into the Actions panel; if necessary, add a
trace() function call or add a value to the text field using its appendText() method.
5. From the main menu, choose Control > Test Movie to create a SWF file and view the
results.
Since this approach is for viewing values of variables, there are two ways that you can easily
view the values of variables as you’re trying out the examples: write values into a text field
instance on the Stage, or use the trace() function to print values to the Output panel.
64 Getting started with ActionScript
■ The trace() function: The ActionScript trace() function writes the values of any
parameters passed to it (either variables or literal expressions) to the Output panel. Many
of the example listings in this manual already include a trace() function call, so for those
listings you’ll only need to copy the code into your document and test the project. If you
want to use trace() to test the value of a variable in a code listing that doesn’t already
include it, just add a trace() call to the code listing, passing the variable as a parameter.
For example, if you encountered a code listing such as this one in the chapter,
var albumName:String = "Three for the money";
you could copy the code into the Actions panel, then add a call to the trace() function
such as this one to test the outcome of the code listing:
var albumName:String = "Three for the money";
trace("albumName =", albumName);
When you run the program, this line will print out:
albumName = Three for the money
Each trace() function call can take multiple parameters, which are all strung together as
a single printed line. A line break is added to the end of each trace() function call, so
separate trace() calls print out on separate lines.
■ A text field on the Stage: If you prefer not to use the trace() function, you can add a
Dynamic text field to the Stage using the Text tool, and write out values to that text field
to view the results of a code listing. The appendText() method of the TextField class can
be used to add a String value to the end of the text field’s contents. To access the text field
using ActionScript, you must give it an instance name in the Property inspector. For
instance, if your text field has the instance name outputText, the following code could be
used to check the value of the variable albumName:
var albumName:String = "Three for the money";
outputText.appendText("albumName = ");
outputText.appendText(albumName);
This code would write the following text to the text field named outputText:
albumName = Three for the money
As the example shows, the appendText() method will add the text to the same line as the
previous contents, so multiple values can be added to the same line of text using multiple
appendText() calls. To force the text to the next line you can append a newline character
("n"):
outputText.appendText("n"); // adds a line break to the text field
Running subsequent examples 65
Unlike the trace() function, the appendText() method only accepts one value as a
parameter. That value must be a string (either a String instance or a string literal). To print
out the value of a non-string variable, you must first convert the value to a String. The
easiest way to do that is to call the object’s toString() method:
var albumYear:int = 1999;
outputText.appendText("albumYear = ");
outputText.appendText(albumYear.toString());
Working with end-of-chapter examples
Like this chapter, most chapters in this manual include a significant end-of-chapter example
that ties together many of the concepts discussed in the chapter. However, unlike the Hello
World example in this chapter, those examples will not be presented in a step-by-step tutorial
format. The relevant ActionScript 3.0 code in each example will be highlighted and discussed,
but instructions about running the examples in specific development environments won’t be
provided. However, the example files distributed with this manual will include all of the files
you need to compile and run the examples easily in your chosen development environment.
66 Getting started with ActionScript
67
3
CHAPTER 3
ActionScript language
and syntax
ActionScript 3.0 comprises both the core ActionScript language and the Adobe Flash Player
Application Programming Interface (API). The core language is the part of ActionScript that
implements the draft ECMAScript (ECMA-262), Edition 4 draft language specification. The
Flash Player API provides programmatic access to Flash Player.
This chapter provides a brief introduction to the core ActionScript language and syntax. After
reading this chapter, you should have a basic understanding of how to work with data types
and variables, how to use proper syntax, and how to control the flow of data in your program.
Contents
Language overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Objects and classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Packages and namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Conditionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Looping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .119
Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
68 ActionScript language and syntax
Language overview
Objects lie at the heart of the ActionScript 3.0 language—they are its fundamental building
blocks. Every variable you declare, every function you write, and every class instance you
create is an object. You can think of an ActionScript 3.0 program as a group of objects that
carry out tasks, respond to events, and communicate with one another.
Programmers familiar with object-oriented programming (OOP) in Java or C++ may think of
objects as modules that contain two kinds of members: data stored in member variables or
properties, and behavior accessible through methods. The ECMAScript edition 4 draft, the
standard upon which ActionScript 3.0 is based, defines objects in a similar but slightly
different way. In the ECMAScript draft, objects are simply collections of properties. These
properties are containers that can hold not only data, but also functions or other objects. If a
function is attached to an object in this way, it is called a method.
While the ECMAScript draft definition may seem a little odd to programmers with a Java or
C++ background, in practice, defining object types with ActionScript 3.0 classes is very
similar to the way classes are defined in Java or C++. The distinction between the two
definitions of object is important when discussing the ActionScript object model and other
advanced topics, but in most other situations the term properties means class member variables
as opposed to methods. The ActionScript 3.0 Language and Components Reference, for example,
uses the term properties to mean variables or getter-setter properties. It uses the term methods
to mean functions that are part of a class.
One subtle difference between classes in ActionScript and classes in Java or C++ is that in
ActionScript, classes are not just abstract entities. ActionScript classes are represented by class
objects that store the class’s properties and methods. This allows for techniques that may seem
alien to Java and C++ programmers, such as including statements or executable code at the
top level of a class or package.
Another difference between ActionScript classes and Java or C++ classes is that every
ActionScript class has something called a prototype object. In previous versions of ActionScript,
prototype objects, linked together into prototype chains, served collectively as the foundation
of the entire class inheritance hierarchy. In ActionScript 3.0, however, prototype objects play
only a small role in the inheritance system. The prototype object can still be useful, however,
as an alternative to static properties and methods if you want to share a property and its value
among all the instances of a class.
Objects and classes 69
In the past, advanced ActionScript programmers could directly manipulate the prototype
chain with special built-in language elements. Now that the language provides a more mature
implementation of a class-based programming interface, many of these special language
elements, such as __proto__ and __resolve, are no longer part of the language. Moreover,
optimizations of the internal inheritance mechanism that provide significant Flash Player
performance improvements preclude direct access to the inheritance mechanism.
Objects and classes
In ActionScript 3.0, every object is defined by a class. A class can be thought of as a template
or a blueprint for a type of object. Class definitions can include variables and constants, which
hold data values, and methods, which are functions that encapsulate behavior bound to the
class. The values stored in properties can be primitive values or other objects. Primitive values
are numbers, strings, or Boolean values.
ActionScript contains a number of built-in classes that are part of the core language. Some of
these built-in classes, such as Number, Boolean and String, represent the primitive values
available in ActionScript. Others, such as the Array, Math, and XML classes, define more
complex objects that are part of the ECMAScript standard.
All classes, whether built-in or user-defined, derive from the Object class. For programmers
with previous ActionScript experience, it is important to note that the Object data type is no
longer the default data type, even though all other classes still derive from it. In ActionScript
2.0, the following two lines of code were equivalent because the lack of a type annotation
meant that a variable would be of type Object:
var someObj:Object;
var someObj;
ActionScript 3.0, however, introduces the concept of untyped variables, which can be
designated in the following two ways:
var someObj:*;
var someObj;
An untyped variable is not the same as a variable of type Object. The key difference is that
untyped variables can hold the special value undefined, while a variable of type Object
cannot hold that value.
You can define your own classes using the class keyword. You can declare class properties in
three ways: constants can be defined with the const keyword, variables are defined with the
var keyword, and getter and setter properties are defined by using the get and set attributes
in a method declaration. You can declare methods with the function keyword.
70 ActionScript language and syntax
You create an instance of a class by using the new operator. The following example creates an
instance of the Date class called myBirthday.
var myBirthday:Date = new Date();
Packages and namespaces
Packages and namespaces are related concepts. Packages allow you to bundle class definitions
together in a way that facilitates code sharing and minimizes naming conflicts. Namespaces
allow you to control the visibility of identifiers, such as property and method names, and can
be applied to code whether it resides inside or outside a package. Packages let you organize
your class files, and namespaces let you manage the visibility of individual properties and
methods.
Packages
Packages in ActionScript 3.0 are implemented with namespaces, but are not synonymous with
them. When you declare a package, you are implicitly creating a special type of namespace
that is guaranteed to be known at compile time. Namespaces, when created explicitly, are not
necessarily known at compile time.
The following example uses the package directive to create a simple package containing one
class:
package samples
{
public class SampleCode
{
public var sampleGreeting:String;
public function sampleFunction()
{
trace(sampleGreeting + " from sampleFunction()");
}
}
}
The name of the class in this example is SampleCode. Because the class is inside the samples
package, the compiler automatically qualifies the class name at compile time into its fully
qualified name: samples.SampleCode. The compiler also qualifies the names of any properties
or methods, so that sampleGreeting and sampleFunction() become
samples.SampleCode.sampleGreeting and samples.SampleCode.sampleFunction(),
respectively.
Packages and namespaces 71
Many developers, especially those with Java programming backgrounds, may choose to place
only classes at the top level of a package. ActionScript 3.0, however, supports not only classes
at the top level of a package, but also variables, functions, and even statements. One advanced
use of this feature is to define a namespace at the top level of a package so that it will be
available to all classes in that package. Note, however, that only two access specifiers, public
and internal, are allowed at the top level of a package. Unlike Java, which allows you to
declare nested classes as private, ActionScript 3.0 supports neither nested nor private classes.
In many other ways, however, ActionScript 3.0 packages are similar to packages in the Java
programming language. As you can see in the previous example, fully qualified package
references are expressed using the dot operator (.), just as they are in Java. You can use
packages to organize your code into an intuitive hierarchical structure for use by other
programmers. This facilitates code sharing by allowing you to create your own package to
share with others, and to use packages created by others in your code.
The use of packages also helps to ensure that the identifier names that you use are unique and
do not conflict with other identifier names. In fact, some would argue that this is the primary
benefit of packages. For example, two programmers who wish to share their code with each
other may have each created a class called SampleCode. Without packages, this would create a
name conflict, and the only resolution would be to rename one of the classes. With packages,
however, the name conflict is easily avoided by placing one, or preferably both, of the classes
in packages with unique names.
You can also include embedded dots in your package name to create nested packages. This
allows you to create a hierarchical organization of packages. A good example of this is the
flash.xml package provided by the Flash Player API. The flash.xml package is nested inside the
flash package.
The flash.xml package contains the legacy XML parser that was used in previous versions of
ActionScript. One reason that it now resides in the flash.xml package is that the name of the
legacy XML class conflicts with the name of the new XML class that implements the XML for
ECMAScript (E4X) specification functionality available in ActionScript 3.0.
Although moving the legacy XML class into a package is a good first step, most users of the
legacy XML classes will import the flash.xml package, which will generate the same name
conflict unless you remember to always use the fully qualified name of the legacy XML class
(flash.xml.XML). To avoid this situation, the legacy XML class is now named
XMLDocument, as the following example shows:
72 ActionScript language and syntax
package flash.xml
{
class XMLDocument {}
class XMLNode {}
class XMLSocket {}
}
Most of the Flash Player API is organized under the flash package. For example, the
flash.display package contains the display list API, and the flash.events package contains the
new event model.
Creating packages
ActionScript 3.0 provides significant flexibility in the way you organize your packages, classes,
and source files. Previous versions of ActionScript allowed only one class per source file and
required that the name of the source file match the name of the class. ActionScript 3.0 allows
you to include multiple classes in one source file, but only one class in each file can be made
available to code that is external to that file. In other words, only one class in each file can be
declared inside a package declaration. You must declare any additional classes outside your
package definition, which makes those classes invisible to code outside that source file. The
name of the class declared inside the package definition must match the name of the source file.
ActionScript 3.0 also provides more flexibility in the way you declare packages. In previous
versions of ActionScript, packages merely represented directories in which you placed source
files, and you didn’t declare packages with the package statement, but rather included the
package name as part of the fully qualified class name in your class declaration. Although
packages still represent directories in ActionScript 3.0, packages can contain more than just
classes. In ActionScript 3.0, you use the package statement to declare a package, which means
that you can also declare variables, functions, and namespaces at the top level of a package.
You can even include executable statements at the top level of a package. If you do declare
variables, functions, or namespaces at the top level of a package, the only attributes available
at that level are public and internal, and only one package-level declaration per file can use
the public attribute, whether that declaration is a class, variable, function, or namespace.
Packages are useful for organizing your code and for preventing name conflicts. You should
not confuse the concept of packages with the unrelated concept of class inheritance. Two
classes that reside in the same package will have a namespace in common, but are not
necessarily related to each other in any other way. Likewise, a nested package may have no
semantic relationship to its parent package.
Packages and namespaces 73
Importing packages
If you want to use a class that is inside a package, you must import either the package or the
specific class. This differs from ActionScript 2.0, where importing classes was optional.
For example, consider the SampleCode class example from earlier in this chapter. If the class
resides in a package named samples, you must use one of the following import statements
before using the SampleCode class:
import samples.*;
or
import samples.SampleCode;
In general, import statements should be as specific as possible. If you plan to use only the
SampleCode class from the samples package, you should import only the SampleCode class
rather than the entire package to which it belongs. Importing entire packages may lead to
unexpected name conflicts.
You must also place the source code that defines the package or class within your classpath.
The classpath is a user-defined list of local directory paths that determines where the compiler
will search for imported packages and classes. The classpath is sometimes called the build path
or source path.
After you have properly imported the class or package, you can use either the fully qualified
name of the class (samples.SampleCode) or merely the class name by itself (SampleCode).
Fully qualified names are useful when identically named classes, methods, or properties result
in ambiguous code, but can be difficult to manage if used for all identifiers. For example, the
use of the fully qualified name results in verbose code when you instantiate a SampleCode
class instance:
var mySample:samples.SampleCode = new samples.SampleCode();
As the levels of nested packages increase, the readability of your code decreases. In situations
where you are confident that ambiguous identifiers will not be a problem, you can make your
code easier to read by using simple identifiers. For example, instantiating a new instance of the
SampleCode class is much less verbose if you use only the class identifier:
var mySample:SampleCode = new SampleCode();
If you attempt to use identifier names without first importing the appropriate package or
class, the compiler will not be able to find the class definitions. On the other hand, if you do
import a package or class, any attempt to define a name that conflicts with an imported name
will generate an error.
74 ActionScript language and syntax
When a package is created, the default access specifier for all members of that package is
internal, which means that, by default, package members are only visible to other members
of that package. If you want a class to be available to code outside the package, you must
declare that class to be public. For example, the following package contains two classes,
SampleCode and CodeFormatter:
// SampleCode.as file
package samples
{
public class SampleCode {}
}
// CodeFormatter.as file
package samples
{
class CodeFormatter {}
}
The SampleCode class is visible outside the package because it is declared as a public class.
The CodeFormatter class, however, is visible only within the samples package itself. If you
attempt to access the CodeFormatter class outside the samples package, you will generate an
error, as the following example shows:
import samples.SampleCode;
import samples.CodeFormatter;
var mySample:SampleCode = new SampleCode(); // okay, public class
var myFormatter:CodeFormatter = new CodeFormatter(); // error
If you want both classes to be available outside the package, you must declare both classes to
be public. You cannot apply the public attribute to the package declaration.
Fully qualified names are useful for resolving name conflicts that may occur when using
packages. Such a scenario may arise if you import two packages that define classes with the
same identifier. For example, consider the following package, which also has a class named
SampleCode:
package langref.samples
{
public class SampleCode {}
}
If you import both classes, as follows, you will have a name conflict when referring to the
SampleCode class:
import samples.SampleCode;
import langref.samples.SampleCode;
var mySample:SampleCode = new SampleCode(); // name conflict
Packages and namespaces 75
The compiler has no way of knowing which SampleCode class to use. To resolve this conflict,
you must use the fully qualified name of each class, as follows:
var sample1:samples.SampleCode = new samples.SampleCode();
var sample2:langref.samples.SampleCode = new langref.samples.SampleCode();
Namespaces
Namespaces give you control over the visibility of the properties and methods that you create.
Think of the public, private, protected, and internal access control specifiers as built-
in namespaces. If these predefined access control specifiers do not suit your needs, you can
create your own namespaces.
If you are familiar with XML namespaces, much of this discussion will not be new to you,
although the syntax and details of the ActionScript implementation are slightly different from
those of XML. If you have never worked with namespaces before, the concept itself is
straightforward, but the implementation has specific terminology that you will need to learn.
To understand how namespaces work, it helps to know that the name of a property or method
always contains two parts: an identifier and a namespace. The identifier is what you generally
think of as a name. For example, the identifiers in the following class definition are
sampleGreeting and sampleFunction():
class SampleCode
{
var sampleGreeting:String;
function sampleFunction () {
trace(sampleGreeting + " from sampleFunction()");
}
}
Whenever definitions are not preceded by a namespace attribute, their names are qualified by
the default internal namespace, which means they are visible only to callers in the same
package. If the compiler is set to strict mode, the compiler issues a warning that the internal
namespace applies to any identifier without a namespace attribute. To ensure that an
identifier is available everywhere, you must specifically precede the identifier name with the
public attribute. In the previous example code, both sampleGreeting and
sampleFunction() have a namespace value of internal.
NOTE
Programmers with a C++ background often confuse the import statement with
#include. The #include directive is necessary in C++ because C++ compilers process
one file at a time, and will not look in other files for class definitions unless a header file is
explicitly included. ActionScript 3.0 has an include directive, but it is not designed to
import classes and packages. To import classes or packages in ActionScript 3.0, you
must use the import statement and place the source file that contains the package in
the class path.
76 ActionScript language and syntax
There are three basic steps to follow when using namespaces. First, you must define the
namespace using the namespace keyword. For example, the following code defines the
version1 namespace:
namespace version1;
Second, you apply your namespace by using it instead of an access control specifier in a
property or method declaration. The following example places a function named
myFunction() into the version1 namespace:
version1 function myFunction() {}
Third, once you’ve applied the namespace, you can reference it with the use directive or by
qualifying the name of an identifier with a namespace. The following example references the
myFunction() function through the use directive:
use namespace version1;
myFunction();
You can also use a qualified name to reference the myFunction() function, as the following
example shows:
version1::myFunction();
Defining namespaces
Namespaces contain one value, the Uniform Resource Identifier (URI), which is sometimes
called the namespace name. A URI allows you to ensure that your namespace definition is
unique.
You create a namespace by declaring a namespace definition in one of two ways. You can
either define a namespace with an explicit URI, as you would define an XML namespace, or
you can omit the URI. The following example shows how a namespace can be defined using a
URI:
namespace flash_proxy = “http://www.adobe.com/flash/proxy”;
The URI serves as a unique identification string for that namespace. If you omit the URI, as
in the following example, the compiler will create a unique internal identification string in
place of the URI. You do not have access to this internal identification string.
namespace flash_proxy;
Once you define a namespace, with or without a URI, that namespace cannot be redefined in
the same scope. An attempt to define a namespace that has been defined earlier in the same
scope results in a compiler error.
Packages and namespaces 77
If a namespace is defined within a package or a class, the namespace may not be visible to code
outside that package or class unless the appropriate access control specifier is used. For
example, the following code shows the flash_proxy namespace defined within the flash.utils
package. In the following example, the lack of an access control specifier means that the
flash_proxy namespace would be visible only to code within the flash.utils package and
would not be visible to any code outside the package:
package flash.utils
{
namespace flash_proxy;
}
The following code uses the public attribute to make the flash_proxy namespace visible to
code outside the package:
package flash.utils
{
public namespace flash_proxy;
}
Applying namespaces
Applying a namespace means placing a definition into a namespace. Definitions that can be
placed into namespaces include functions, variables, and constants (you cannot place a class
into a custom namespace).
Consider, for example, a function declared using the public access control namespace. Using
the public attribute in a function definition places the function into the public namespace,
which makes the function available to all code. Once you have defined a namespace, you can
use the namespace that you defined the same way you would use the public attribute, and
the definition will be available to code that can reference your custom namespace. For
example, if you define a namespace example1, you can add a method called myFunction()
using example1 as an attribute, as the following example shows:
namespace example1;
class someClass
{
example1 myFunction() {}
}
78 ActionScript language and syntax
Declaring the myFunction() method using the namespace example1 as an attribute means
that the method belongs to the example1 namespace.
You should bear in mind the following when applying namespaces:
■ You can apply only one namespace to each declaration.
■ There is no way to apply a namespace attribute to more than one definition at a time. In
other words, if you want to apply your namespace to ten different functions, you must
add your namespace as an attribute to each of the ten function definitions.
■ If you apply a namespace, you cannot also specify an access control specifier because
namespaces and access control specifiers are mutually exclusive. In other words, you
cannot declare a function or property as public, private, protected, or internal in
addition to applying your namespace.
Referencing namespaces
There is no need to explicitly reference a namespace when you use a method or property
declared with any of the access control namespaces, such as public, private, protected,
and internal. This is because access to these special namespaces is controlled by context. For
example, definitions placed into the private namespace are automatically available to code
within the same class. For namespaces that you define, however, such context sensitivity does
not exist. In order to use a method or property that you have placed into a custom namespace,
you must reference the namespace.
You can reference namespaces with the use namespace directive or you can qualify the name
with the namespace using the name qualifier (::) punctuator. Referencing a namespace with
the use namespace directive “opens” the namespace, so that it can apply to any identifiers
that are not qualified. For example, if you have defined the example1 namespace, you can
access names in that namespace by using use namespace example1:
use namespace example1;
myFunction();
You can open more than one namespace at a time. Once you open a namespace with use
namespace, it remains open throughout the block of code in which it was opened. There is no
way to explicitly close a namespace.
Having more than one open namespace, however, increases the likelihood of name conflicts.
If you prefer not to open a namespace, you can avoid the use namespace directive by
qualifying the method or property name with the namespace and the name qualifier
punctuator. For example, the following code shows how you can qualify the name
myFunction() with the example1 namespace:
example1::myFunction();
Packages and namespaces 79
Using namespaces
You can find a real-world example of a namespace that is used to prevent name conflicts in the
flash.utils.Proxy class that is part of the Flash Player API. The Proxy class, which is the
replacement for the Object.__resolve property from ActionScript 2.0, allows you to
intercept references to undefined properties or methods before an error occurs. All of the
methods of the Proxy class reside in the flash_proxy namespace in order to prevent name
conflicts.
To better understand how the flash_proxy namespace is used, you need to understand how
to use the Proxy class. The functionality of the Proxy class is available only to classes that
inherit from it. In other words, if you want to use the methods of the Proxy class on an object,
the object’s class definition must extend the Proxy class. For example, if you want to intercept
attempts to call an undefined method, you would extend the Proxy class and then override the
callProperty() method of the Proxy class.
You may recall that implementing namespaces is usually a three-step process of defining,
applying, and then referencing a namespace. Because you never explicitly call any of the Proxy
class methods, however, the flash_proxy namespace is only defined and applied, but never
referenced. The Flash Player API defines the flash_proxy namespace and applies it in the
Proxy class. Your code only needs to apply the flash_proxy namespace to classes that extend
the Proxy class.
The flash_proxy namespace is defined in the flash.utils package in a manner similar to the
following:
package flash.utils
{
public namespace flash_proxy;
}
The namespace is applied to the methods of the Proxy class as shown in the following excerpt
from the Proxy class:
public class Proxy
{
flash_proxy function callProperty(name:*, ... rest):*
flash_proxy function deleteProperty(name:*):Boolean
...
}
As the following code shows, you must first import both the Proxy class and the flash_proxy
namespace. You must then declare your class such that it extends the Proxy class (you must
also add the dynamic attribute if you are compiling in strict mode). When you override the
callProperty() method, you must use the flash_proxy namespace.
80 ActionScript language and syntax
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
dynamic class MyProxy extends Proxy
{
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
}
If you create an instance of the MyProxy class and call an undefined method, such as the
testing() method called in the following example, your Proxy object intercepts the method
call and executes the statements inside the overridden callProperty() method (in this case,
a simple trace() statement).
var mySample:MyProxy = new MyProxy();
mySample.testing(); // method call intercepted: testing
There are two advantages to having the methods of the Proxy class inside the flash_proxy
namespace. First, having a separate namespace reduces clutter in the public interface of any
class that extends the Proxy class. (There are about a dozen methods in the Proxy class that
you can override, all of which are not designed to be called directly. Placing all of them in the
public namespace could be confusing.) Second, use of the flash_proxy namespace avoids
name conflicts in case your Proxy subclass contains instance methods with names that match
any of the Proxy class methods. For example, you may want to name one of your own
methods callProperty(). The following code is acceptable, because your version of the
callProperty() method is in a different namespace:
dynamic class MyProxy extends Proxy
{
public function callProperty() {}
flash_proxy override function callProperty(name:*, ...rest):*
{
trace("method call intercepted: " + name);
}
}
Namespaces can also be helpful when you want to provide access to methods or properties in
a way that cannot be accomplished with the four access control specifiers (public, private,
internal, and protected). For example, you may have a few utility methods that are spread
out across several packages. You want these methods available to all of your packages, but you
don’t want the methods to be public. To accomplish this, you can create a new namespace and
use it as your own special access control specifier.
Packages and namespaces 81
The following example uses a user-defined namespace to group together two functions that
reside in different packages. By grouping them into the same namespace, you can make both
functions visible to a class or package through a single use namespace statement.
This example uses four files to demonstrate the technique. All of the files must be within your
classpath. The first file, myInternal.as, is used to define the myInternal namespace. Because
the file is in a package named example, you must place the file into a folder named example.
The namespace is marked as public so that it can be imported into other packages.
// myInternal.as in folder example
package example
{
public namespace myInternal = "http://www.adobe.com/2006/actionscript/
examples";
}
The second and third files, Utility.as and Helper.as, define the classes that contain methods
that should be available to other packages. The Utility class is in the example.alpha package,
which means that the file should be placed inside a folder named alpha that is a subfolder of
the example folder. The Helper class is in the example.beta package, which means that the file
should be placed inside a folder named beta that is also a subfolder of the example folder.
Both of these packages, example.alpha and example.beta, must import the namespace before
using it.
// Utility.as in the example/alpha folder
package example.alpha
{
import example.myInternal;
public class Utility
{
private static var _taskCounter:int = 0;
public static function someTask()
{
_taskCounter++;
}
myInternal static function get taskCounter():int
{
return _taskCounter;
}
}
}
82 ActionScript language and syntax
// Helper.as in the example/beta folder
package example.beta
{
import example.myInternal;
public class Helper
{
private static var _timeStamp:Date;
public static function someTask()
{
_timeStamp = new Date();
}
myInternal static function get lastCalled():Date
{
return _timeStamp;
}
}
}
The fourth file, NamespaceUseCase.as, is the main application class, and should be a sibling
to the example folder. In Adobe Flash CS3 Professional, this class would be used as the
document class for the FLA. The NamespaceUseCase class also imports the myInternal
namespace and uses it to call the two static methods that reside in the other packages. The
example uses static methods only to simplify the code. Both static and instance methods can
be placed in the myInternal namespace.
// NamespaceUseCase.as
package
{
import flash.display.MovieClip;
import example.myInternal; // import namespace
import example.alpha.Utility; // import Utility class
import example.beta.Helper; // import Helper class
public class NamespaceUseCase extends MovieClip
{
public function NamespaceUseCase()
{
use namespace myInternal;
Utility.someTask();
Utility.someTask();
trace(Utility.taskCounter); // 2
Variables 83
Helper.someTask();
trace(Helper.lastCalled); // [time someTask() was last called]
}
}
}
Variables
Variables allow you to store values that you use in your program. To declare a variable, you
must use the var statement with the variable name. In ActionScript 2.0, use of the var
statement is only required if you use type annotations. In ActionScript 3.0, use of the var
statement is always required. For example, the following line of ActionScript declares a
variable named i:
var i;
If you omit the var statement when declaring a variable, you will get a compiler error in strict
mode and run-time error in standard mode. For example, the following line of code will result
in an error if the variable i has not been previously defined:
i; // error if i was not previously defined
To associate a variable with a data type, you must do so when you declare the variable.
Declaring a variable without designating the variable’s type is legal, but will generate a
compiler warning in strict mode. You designate a variable’s type by appending the variable
name with a colon (:), followed by the variable’s type. For example, the following code
declares a variable i that is of type int:
var i:int;
You can assign a value to a variable using the assignment operator (=). For example, the
following code declares a variable i and assigns the value 20 to it:
var i:int;
i = 20;
You may find it more convenient to assign a value to a variable at the same time that you
declare the variable, as in the following example:
var i:int = 20;
The technique of assigning a value to a variable at the time it is declared is commonly used
not only when assigning primitive values such as integers and strings, but also when creating
an array or instantiating an instance of a class. The following example shows an array that is
declared and assigned a value using one line of code.
var numArray:Array = ["zero", "one", "two"];
84 ActionScript language and syntax
You can create an instance of a class by using the new operator. The following example creates
an instance of a named CustomClass, and assigns a reference to the newly created class
instance to the variable named customItem:
var customItem:CustomClass = new CustomClass();
If you have more than one variable to declare, you can declare them all on one line of code by
using the comma operator (,) to separate the variables. For example, the following code
declares three variables on one line of code:
var a:int, b:int, c:int;
You can also assign values to each of the variables on the same line of code. For example, the
following code declares three variables (a, b, and c) and assigns each a value:
var a:int = 10, b:int = 20, c:int = 30;
Although you can use the comma operator to group variable declarations into one statement,
doing so may reduce the readability of your code.
Understanding variable scope
The scope of a variable is the area of your code where the variable can be accessed by a lexical
reference. A global variable is one that is defined in all areas of your code, whereas a local
variable is one that is defined in only one part of your code. In ActionScript 3.0, variables are
always assigned the scope of the function or class in which they are declared. A global variable
is a variable that you define outside of any function or class definition. For example, the
following code creates a global variable strGlobal by declaring it outside of any function.
The example shows that a global variable is available both inside and outside the function
definition.
var strGlobal:String = "Global";
function scopeTest()
{
trace(strGlobal); // Global
}
scopeTest();
trace(strGlobal); // Global
You declare a local variable by declaring the variable inside a function definition. The smallest
area of code for which you can define a local variable is a function definition. A local variable
declared within a function will exist only in that function. For example, if you declare a
variable named str2 within a function named localScope(), that variable will not be
available outside the function.
function localScope()
{
var strLocal:String = "local";
Variables 85
}
localScope();
trace(strLocal); // error because strLocal is not defined globally
If the variable name you use for your local variable is already declared as a global variable, the
local definition hides (or shadows) the global definition while the local variable is in scope.
The global variable will still exist outside of the function. For example, the following code
creates a global string variable named str1, and then creates a local variable of the same name
inside the scopeTest() function. The trace statement inside the function outputs the local
value of the variable, but the trace statement outside the function outputs the global value of
the variable.
var str1:String = "Global";
function scopeTest ()
{
var str1:String = "Local";
trace(str1); // Local
}
scopeTest();
trace(str1); // Global
ActionScript variables, unlike variables in C++ and Java, do not have block-level scope. A
block of code is any group of statements between an opening curly brace ( { ) and a closing
curly brace ( } ). In some programming languages, such as C++ and Java, variables declared
inside a block of code are not available outside that block of code. This restriction of scope is
called block-level scope, and does not exist in ActionScript. If you declare a variable inside a
block of code, that variable will be available not only in that block of code, but also in any
other parts of the function to which the code block belongs. For example, the following
function contains variables that are defined in various block scopes. All the variables are
available throughout the function.
function blockTest (testArray:Array)
{
var numElements:int = testArray.length;
if (numElements > 0)
{
var elemStr:String = "Element #";
for (var i:int = 0; i < numElements; i++)
{
var valueStr:String = i + ": " + testArray[i];
trace(elemStr + valueStr);
}
trace(elemStr, valueStr, i); // all still defined
}
trace(elemStr, valueStr, i); // all defined if numElements > 0
}
blockTest(["Earth", "Moon", "Sun"]);
86 ActionScript language and syntax
An interesting implication of the lack of block-level scope is that you can read or write to a
variable before it is declared, as long as it is declared before the function ends. This is because
of a technique called hoisting, which means that the compiler moves all variable declarations
to the top of the function. For example, the following code compiles even though the initial
trace() function for the num variable happens before the num variable is declared:
trace(num); // NaN
var num:Number = 10;
trace(num); // 10
The compiler will not, however, hoist any assignment statements. This explains why the
initial trace() of num results in NaN (not a number), which is the default value for variables
of the Number data type. This means that you can assign values to variables even before they
are declared, as shown in the following example:
num = 5;
trace(num); // 5
var num:Number = 10;
trace(num); // 10
Default values
A default value is the value that a variable contains before you set its value. You initialize a
variable when you set its value for the first time. If you declare a variable, but do not set its
value, that variable is uninitialized. The value of an uninitialized variable depends on its data
type. The following table describes the default values of variables, organized by data type:
For variables of type Number, the default value is NaN (not a number), which is a special value
defined by the IEEE-754 standard to mean a value that does not represent a number.
Data type Default value
Boolean false
int 0
Number NaN
Object null
String null
uint 0
Not declared (equivalent to type annotation *) undefined
All other classes, including user-defined classes. null
Data types 87
If you declare a variable, but do not declare its data type, the default data type * will apply,
which actually means that the variable is untyped. If you also do not initialize an untyped
variable with a value, its default value is undefined.
For data types other than Boolean, Number, int, and uint, the default value of any
uninitialized variable is null. This applies to all the classes defined by the Flash Player API, as
well as any custom classes that you create.
The value null is not a valid value for variables of type Boolean, Number, int, or uint. If you
attempt to assign a value of null to a such a variable, the value is converted to the default
value for that data type. For variables of type Object, you can assign a value of null. If you
attempt to assign the value undefined to a variable of type Object, the value is converted to
null.
For variables of type Number, there is a special top-level function named isNaN() that returns
the Boolean value true if the variable is not a number, and false otherwise.
Data types
A data type defines a set of values. For example, the Boolean data type is the set of exactly two
values: true and false. In addition to the Boolean data type, ActionScript 3.0 defines several
more commonly used data types, such as String, Number, and Array. You can define your own
data types by using classes or interfaces to define a custom set of values. All values in
ActionScript 3.0, whether they are primitive or complex, are objects.
A primitive value is a value that belongs to one of the following data types: Boolean, int,
Number, String, and uint. Working with primitive values is usually faster than working with
complex values, because ActionScript stores primitive values in a special way that makes
memory and speed optimizations possible.
A complex value is a value that is not a primitive value. Data types that define sets of complex
values include Array, Date, Error, Function, RegExp, XML, and XMLList.
NOTE
For readers interested in the technical details, ActionScript stores primitive values internally
as immutable objects. The fact that they are stored as immutable objects means that
passing by reference is effectively the same as passing by value. This cuts down on memory
usage and increases execution speed, because references are usually significantly smaller
than the values themselves.
88 ActionScript language and syntax
Many programming languages distinguish between primitive values and their wrapper
objects. Java, for example, has an int primitive and the java.lang.Integer class that wraps it.
Java primitives are not objects, but their wrappers are, which makes primitives useful for some
operations, and wrapper objects better suited for other operations. In ActionScript 3.0,
primitive values and their wrapper objects are, for practical purposes, indistinguishable. All
values, even primitive values, are objects. Flash Player treats these primitive types as special
cases that behave like objects but that don’t require the normal overhead associated with
creating objects. This means that the following two lines of code are equivalent:
var someInt:int = 3;
var someInt:int = new int(3);
All the primitive and complex data types listed above are defined by the ActionScript 3.0 core
classes. The core classes allow you to create objects using literal values instead of using the new
operator. For example, you can create an array using a literal value or the Array class
constructor, as follows:
var someArray:Array = [1, 2, 3]; // literal value
var someArray:Array = new Array(1,2,3); // Array constructor
Type checking
Type checking can occur at either compile time or run time. Statically typed languages, such
as C++ and Java, do type checking at compile time. Dynamically typed languages, such as
Smalltalk and Python, handle type checking at run time. As a dynamically typed language,
ActionScript 3.0 has run-time type checking, but also supports compile-time type checking
with a special compiler mode called strict mode. In strict mode, type checking occurs at both
compile time and run time, but in standard mode, type checking occurs only at run time.
Dynamically typed languages offer tremendous flexibility when you structure your code, but
at the cost of allowing type errors to manifest at run time. Statically typed languages report
type errors at compile time, but at the cost of requiring that type information be known at
compile time.
Compile-time type checking
Compile-time type checking is often favored in larger projects because as the size of a project
grows, data type flexibility usually becomes less important than catching type errors as early as
possible. This is why, by default, the ActionScript compiler in Adobe Flash CS3 Professional
and Adobe Flex Builder 2 is set to run in strict mode.
Data types 89
In order to provide compile-time type checking, the compiler needs to know the data type
information for the variables or expressions in your code. To explicitly declare a data type for a
variable, add the colon operator (:) followed by the data type as a suffix to the variable name.
To associate a data type with a parameter, use the colon operator followed by the data type.
For example, the following code adds data type information to the xParam parameter, and
declares a variable myParam with an explicit data type:
function runtimeTest(xParam:String)
{
trace(xParam);
}
var myParam:String = “hello”;
runtimeTest(myParam);
In strict mode, the ActionScript compiler reports type mismatches as compiler errors. For
example, the following code declares a function parameter xParam, of type Object, but later
attempts to assign values of type String and Number to that parameter. This produces a
compiler error in strict mode.
function dynamicTest(xParam:Object)
{
if (xParam is String)
{
var myStr:String = xParam; // compiler error in strict mode
trace("String: " + myStr);
}
else if (xParam is Number)
{
var myNum:Number = xParam; // compiler error in strict mode
trace("Number: " + myNum);
}
}
Even in strict mode, however, you can selectively opt of out compile-time type checking by
leaving the right side of an assignment statement untyped. You can mark a variable or
expression as untyped by either omitting a type annotation, or using the special asterisk (*)
type annotation. For example, if the xParam parameter in the previous example is modified so
that it no longer has a type annotation, the code will compile in strict mode:
function dynamicTest(xParam)
{
if (xParam is String)
{
var myStr:String = xParam;
trace("String: " + myStr);
}
else if (xParam is Number)
{
90 ActionScript language and syntax
var myNum:Number = xParam;
trace("Number: " + myNum);
}
}
dynamicTest(100)
dynamicTest("one hundred");
Run-time type checking
Run-time type checking occurs in ActionScript 3.0 whether you compile in strict mode or
standard mode. Consider a situation in which the value 3 is passed as an argument to a
function that expects an array. In strict mode, the compiler will generate an error, because the
value 3 is not compatible with the data type Array. If you disable strict mode, and run in
standard mode, the compiler does not complain about the type mismatch, but run-time type
checking by Flash Player results in a run-time error.
The following example shows a function named typeTest() that expects an Array argument
but is passed a value of 3. This causes a run-time error in standard mode, because the value 3
is not a member of the parameter’s declared data type (Array).
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum:Number = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0 standard mode
There may also be situations where you get a run-time type error even when you are operating
in strict mode. This is possible if you use strict mode, but opt out of compile-time type
checking by using an untyped variable. When you use an untyped variable, you are not
eliminating type checking but rather deferring it until run time. For example, if the myNum
variable in the previous example does not have a declared data type, the compiler cannot
detect the type mismatch, but Flash Player will generate a run-time error because it compares
the run-time value of myNum, which is set to 3 as a result of the assignment statement, with the
type of xParam, which is set to the Array data type.
function typeTest(xParam:Array)
{
trace(xParam);
}
var myNum = 3;
typeTest(myNum);
// run-time error in ActionScript 3.0
Data types 91
Run-time type checking also allows more flexible use of inheritance than does compile-time
checking. By deferring type checking to run time, standard mode allows you to reference
properties of a subclass even if you upcast. An upcast occurs when you use a base class to
declare the type of a class instance but use a subclass to instantiate it. For example, you can
create a class named ClassBase that can be extended (classes with the final attribute cannot
be extended):
class ClassBase
{
}
You can subsequently create a subclass of ClassBase named ClassExtender, which has one
property named someString, as follows:
class ClassExtender extends ClassBase
{
var someString:String;
}
Using both classes, you can create a class instance that is declared using the ClassBase data
type, but instantiated using the ClassExtender constructor. An upcast is considered a safe
operation, because the base class does not contain any properties or methods that are not in
the subclass.
var myClass:ClassBase = new ClassExtender();
A subclass, however, does contain properties or methods that its base class does not. For
example, the ClassExtender class contains the someString property, which does not exist in
the ClassBase class. In ActionScript 3.0 standard mode, you can reference this property using
the myClass instance without generating a compile-time error, as shown in the following
example:
var myClass:ClassBase = new ClassExtender();
myClass.someString = "hello";
// no error in ActionScript 3.0 standard mode
The is operator
The is operator, which is new for ActionScript 3.0, allows you to test whether a variable or
expression is a member of a given data type. In previous versions of ActionScript, the
instanceof operator provided this functionality, but in ActionScript 3.0 the instanceof
operator should not be used to test for data type membership. The is operator should be used
instead of the instanceof operator for manual type checking, because the expression x
instanceof y merely checks the prototype chain of x for the existence of y (and in
ActionScript 3.0, the prototype chain does not provide a complete picture of the inheritance
hierarchy).
92 ActionScript language and syntax
The is operator examines the proper inheritance hierarchy and can be used to check not only
whether an object is an instance of a particular class, but also whether an object is an instance
of a class that implements a particular interface. The following example creates an instance of
the Sprite class named mySprite and uses the is operator to test whether mySprite is an
instance of the Sprite and DisplayObject classes, and whether it implements the
IEventDispatcher interface:
var mySprite:Sprite = new Sprite();
trace(mySprite is Sprite); // true
trace(mySprite is DisplayObject); // true
trace(mySprite is IEventDispatcher); // true
The is operator checks the inheritance hierarchy and properly reports that mySprite is
compatible with the Sprite and DisplayObject classes (the Sprite class is a subclass of the
DisplayObject class). The is operator also checks whether mySprite inherits from any classes
that implement the IEventDispatcher interface. Because the Sprite class inherits from the
EventDispatcher class, which implements the IEventDispatcher interface, the is operator
correctly reports that mySprite implements the same interface.
The following example shows the same tests from the previous example, but with instanceof
instead of the is operator. The instanceof operator correctly identifies that mySprite is an
instance of Sprite or DisplayObject, but it returns false when used to test whether mySprite
implements the IEventDispatcher interface.
trace(mySprite instanceof Sprite); // true
trace(mySprite instanceof DisplayObject); // true
trace(mySprite instanceof IEventDispatcher); // false
The as operator
The as operator, which is new in ActionScript 3.0, also allows you to check whether an
expression is a member of a given data type. Unlike the is operator, however, the as operator
does not return a Boolean value. Rather, the as operator returns the value of the expression
instead of true, and null instead of false. The following example shows the results of using
the as operator instead of the is operator in the simple case of checking whether a Sprite
instance is a member of the DisplayObject, IEventDispatcher, and Number data types.
var mySprite:Sprite = new Sprite();
trace(mySprite as Sprite); // [object Sprite]
trace(mySprite as DisplayObject); // [object Sprite]
trace(mySprite as IEventDispatcher); // [object Sprite]
trace(mySprite as Number); // null
When you use the as operator, the operand on the right must be a data type. An attempt to
use an expression other than a data type as the operand on the right will result in an error.
Data types 93
Dynamic classes
A dynamic class defines an object that can be altered at run time by adding or changing
properties and methods. A class that is not dynamic, such as the String class, is a sealed class.
You cannot add properties or methods to a sealed class at run time.
You create dynamic classes by using the dynamic attribute when you declare a class. For
example, the following code creates a dynamic class named Protean:
dynamic class Protean
{
private var privateGreeting:String = "hi";
public var publicGreeting:String = "hello";
function Protean()
{
trace("Protean instance created");
}
}
If you subsequently instantiate an instance of the Protean class, you can add properties or
methods to it outside the class definition. For example, the following code creates an instance
of the Protean class and adds a property named aString and a property named aNumber to
the instance:
var myProtean:Protean = new Protean();
myProtean.aString = "testing";
myProtean.aNumber = 3;
trace(myProtean.aString, myProtean.aNumber); // testing 3
Properties that you add to an instance of a dynamic class are run-time entities, so any type
checking is done at run time. You cannot add a type annotation to a property that you add in
this manner.
You can also add a method to the myProtean instance by defining a function and attaching
the function to a property of the myProtean instance. The following code moves the trace
statement into a method named traceProtean():
var myProtean:Protean = new Protean();
myProtean.aString = "testing";
myProtean.aNumber = 3;
myProtean.traceProtean = function ()
{
trace(this.aString, this.aNumber);
};
myProtean.traceProtean(); // testing 3
94 ActionScript language and syntax
Methods created in this way, however, do not have access to any private properties or methods
of the Protean class. Moreover, even references to public properties or methods of the
Protean class must be qualified with either the this keyword or the class name. The
following example shows the traceProtean() method attempting to access the private and
public variables of the Protean class.
myProtean.traceProtean = function ()
{
trace(myProtean.privateGreeting); // undefined
trace(myProtean.publicGreeting); // hello
};
myProtean.traceProtean();
Data type descriptions
The primitive data types include Boolean, int, Null, Number, String, uint, and void. The
ActionScript core classes also define the following complex data types: Object, Array, Date,
Error, Function, RegExp, XML, and XMLList.
Boolean data type
The Boolean data type comprises two values: true and false. No other values are valid for
variables of Boolean type. The default value of a Boolean variable that has been declared but
not initialized is false.
int data type
The int data type is stored internally as a 32-bit integer and comprises the set of integers from
-2,147,483,648 (-231) to 2,147,483,647 (231 - 1), inclusive. Previous versions of
ActionScript offered only the Number data type, which was used for both integers and
floating-point numbers. In ActionScript 3.0, you now have access to low-level machine types
for 32-bit signed and unsigned integers. If your variable will not use floating-point numbers,
using the int data type instead of the Number data type should be faster and more efficient.
For integer values outside the range of the minimum and maximum int values, use the
Number data type, which can handle values between positive and negative
9,007,199,254,740,992 (53-bit integer values). The default value for variables that are of the
data type int is 0.
Data types 95
Null data type
The Null data type contains only one value, null. This is the default value for the String data
type and all classes that define complex data types, including the Object class. None of the
other primitive data types, such as Boolean, Number, int and uint, contain the value null.
Flash Player will convert the value null to the appropriate default value if you attempt to
assign null to variables of type Boolean, Number, int, or uint. You cannot use this data type
as a type annotation.
Number data type
In ActionScript 3.0, the Number data type can represent integers, unsigned integers, and
floating-point numbers. However, to maximize performance, you should use the Number
data type only for integer values larger than the 32-bit int and uint types can store or for
floating-point numbers. To store a floating-point number, include a decimal point in the
number. If you omit a decimal point, the number will be stored as an integer.
The Number data type uses the 64-bit double-precision format as specified by the IEEE
Standard for Binary Floating-Point Arithmetic (IEEE-754). This standard dictates how
floating-point numbers are stored using the 64 available bits. One bit is used to designate
whether the number is positive or negative. Eleven bits are used for the exponent, which is
stored as base 2. The remaining 52 bits are used to store the significand (also called the
mantissa), which is the number that is raised to the power indicated by the exponent.
By using some of its bits to store an exponent, the Number data type can store floating-point
numbers significantly larger than if it used all of its bits for the significand. For example, if the
Number data type used all 64 bits to store the significand, it could store a number as large as
265 - 1. By using 11 bits to store an exponent, the Number data type can raise its significand
to a power of 21023.
The maximum and minimum values that the Number type can represent are stored in static
properties of the Number class called Number.MAX_VALUE and Number.MIN_VALUE.
Number.MAX_VALUE == 1.79769313486231e+308
Number.MIN_VALUE == 4.940656458412467e-324
Although this range of numbers is enormous, the cost of this range is precision. The Number
data type uses 52 bits to store the significand, with the result that numbers that require more
than 52 bits to represent precisely, such as the fraction 1/3, are only approximations. If your
application requires absolute precision with decimal numbers, you need to use software that
implements decimal floating-point arithmetic as opposed to binary floating-point arithmetic.
96 ActionScript language and syntax
When you store integer values with the Number data type, only the 52 bits of the significand
are used. The Number data type uses these 52 bits and a special hidden bit to represent
integers from -9,007,199,254,740,992 (-253) to 9,007,199,254,740,992 (253).
Flash Player uses the NaN value not only as the default value for variables of type Number, but
also as the result of any operation that should return a number but does not. For example, if
you attempt to calculate the square root of a negative number, the result will be NaN. Other
special Number values include positive infinity and negative infinity.
String data type
The String data type represents a sequence of 16-bit characters. Strings are stored internally as
Unicode characters, using the UTF-16 format. Strings are immutable values, just as they are
in the Java programming language. An operation on a String value returns a new instance of
the string. The default value for a variable declared with the String data type is null. The
value null is not the same as the empty string (""), even though they both represent the
absence of any characters.
uint data type
The uint data type is stored internally as a 32-bit unsigned integer and comprises the set of
integers from 0 to 4,294,967,295 (232 - 1), inclusive. Use the uint data type for special
circumstances that call for non-negative integers. For example, you must use the uint data
type to represent pixel color values, because the int data type has an internal sign bit that is
not appropriate for handling color values. For integer values larger than the maximum uint
value, use the Number data type, which can handle 53-bit integer values. The default value
for variables that are of data type uint is 0.
void data type
The void data type contains only one value, undefined. In previous versions of ActionScript,
undefined was the default value for instances of the Object class. In ActionScript 3.0, the
default value for Object instances is null. If you attempt to assign the value undefined to an
instance of the Object class, Flash Player will convert the value to null. You can only assign a
value of undefined to variables that are untyped. Untyped variables are variables that either
lack any type annotation, or use the asterisk (*) symbol for type annotation. You can use void
only as a return type annotation.
NOTE
The result of division by 0 is only NaN if the divisor is also 0. Division by 0 produces
infinity when the dividend is positive or -infinity when the dividend is negative.
Data types 97
Object data type
The Object data type is defined by the Object class. The Object class serves as the base class
for all class definitions in ActionScript. The ActionScript 3.0 version of the Object data type
differs from that of previous versions in three ways. First, the Object data type is no longer the
default data type assigned to variables with no type annotation. Second, the Object data type
no longer includes the value undefined, which used to be the default value of Object
instances. Third, in ActionScript 3.0, the default value for instances of the Object class is
null.
In previous versions of ActionScript, a variable with no type annotation was automatically
assigned the Object data type. This is no longer true in ActionScript 3.0, which now includes
the idea of a truly untyped variable. Variables with no type annotation are now considered
untyped. If you prefer to make it clear to readers of your code that your intention is to leave a
variable untyped, you can use the new asterisk (*) symbol for the type annotation, which is
equivalent to omitting a type annotation. The following example shows two equivalent
statements, both of which declare an untyped variable x:
var x
var x:*
Only untyped variables can hold the value undefined. If you attempt to assign the value
undefined to a variable that has a data type, Flash Player will convert the value undefined to
the default value of that data type. For instances of the Object data type, the default value is
null, which means that Flash Player will convert the value undefined to null if you attempt
to assign undefined to an Object instance.
Type conversions
A type conversion is said to occur when a value is transformed into a value of a different data
type. Type conversions can be either implicit or explicit. Implicit conversion, which is also
called coercion, is sometimes performed by Flash Player at run time. For example, if the value
2 is assigned to a variable of the Boolean data type, Flash Player converts the value 2 to the
Boolean value true before assigning the value to the variable. Explicit conversion, which is
also called casting, occurs when your code instructs the compiler to treat a variable of one data
type as if it belongs to a different data type. When primitive values are involved, casting
actually converts values from one data type to another. To cast an object to a different type,
you wrap the object name in parentheses and precede it with the name of the new type. For
example, the following code takes a Boolean value and casts it to an integer:
var myBoolean:Boolean = true;
var myINT:int = int(myBoolean);
trace(myINT); // 1
98 ActionScript language and syntax
Implicit conversions
Implicit conversions happen at run time in a number of contexts:
■ In assignment statements
■ When values are passed as function arguments
■ When values are returned from functions
■ In expressions using certain operators, such as the addition (+) operator
For user-defined types, implicit conversions succeed when the value to be converted is an
instance of the destination class or a class that derives from the destination class. If an implicit
conversion is unsuccessful, an error occurs. For example, the following code contains a
successful implicit conversion and an unsuccessful implicit conversion:
class A {}
class B extends A {}
var objA:A = new A();
var objB:B = new B();
var arr:Array = new Array();
objA = objB; // Conversion succeeds.
objB = arr; // Conversion fails.
For primitive types, implicit conversions are handled by calling the same internal conversion
algorithms that are called by the explicit conversion functions. The following sections discuss
these primitive type conversions in detail.
Explicit conversions
It’s helpful to use explicit conversions, or casting, when you compile in strict mode, because
there may be times when you do not want a type mismatch to generate a compile-time error.
This may be the case when you know that coercion will convert your values correctly at run
time. For example, when working with data received from a form, you may want to rely on
coercion to convert certain string values to numeric values. The following code generates a
compile-time error even though the code would run correctly in standard mode:
var quantityField:String = "3";
var quantity:int = quantityField; // compile time error in strict mode
If you want to continue using strict mode, but would like the string converted to an integer,
you can use explicit conversion, as follows:
var quantityField:String = "3";
var quantity:int = int(quantityField); // Explicit conversion succeeds.
Data types 99
Casting to int, uint, and Number
You can cast any data type into one of the three number types: int, uint, and Number. If Flash
Player is unable to convert the number for some reason, the default value of 0 is assigned for
the int and uint data types, and the default value of NaN is assigned for the Number data type.
If you convert a Boolean value to a number, true becomes the value 1 and false becomes the
value 0.
var myBoolean:Boolean = true;
var myUINT:uint = uint(myBoolean);
var myINT:int = int(myBoolean);
var myNum:Number = Number(myBoolean);
trace(myUINT, myINT, myNum); // 1 1 1
myBoolean = false;
myUINT = uint(myBoolean);
myINT = int(myBoolean);
myNum = Number(myBoolean);
trace(myUINT, myINT, myNum); // 0 0 0
String values that contain only digits can be successfully converted into one of the number
types. The number types can also convert strings that look like negative numbers or strings
that represent a hexadecimal value (for example, 0x1A). The conversion process ignores
leading and trailing white space characters in the string value. You can also cast strings that
look like floating-point numbers using Number(). The inclusion of a decimal point causes
uint() and int() to return an integer, truncating the decimal and the characters following it.
For example, the following string values can be cast into numbers:
trace(uint("5")); // 5
trace(uint("-5")); // 4294967291. It wraps around from MAX_VALUE
trace(uint(" 27 ")); // 27
trace(uint("3.7")); // 3
trace(int("3.7")); // 3
trace(int("0x1A")); // 26
trace(Number("3.7")); // 3.7
String values that contain non-numeric characters return 0 when cast with int() or uint()
and NaN when cast with Number(). The conversion process ignores leading and trailing white
space, but returns 0 or NaN if a string has white space separating two numbers.
trace(uint("5a")); // 0
trace(uint("ten")); // 0
trace(uint("17 63")); // 0
100 ActionScript language and syntax
In ActionScript 3.0, the Number() function no longer supports octal, or base 8, numbers. If
you supply a string with a leading zero to the ActionScript 2.0 Number() function, the
number is interpreted as an octal number, and converted to its decimal equivalent. This is not
true with the Number() function in ActionScript 3.0, which instead ignores the leading zero.
For example, the following code generates different output when compiled using different
versions of ActionScript:
trace(Number("044"));
// ActionScript 3.0 44
// ActionScript 2.0 36
Casting is not necessary when a value of one numeric type is assigned to a variable of a
different numeric type. Even in strict mode, the numeric types are implicitly converted to the
other numeric types. This means that in some cases, unexpected values may result when the
range of a type is exceeded. The following examples all compile in strict mode, though some
will generate unexpected values:
var myUInt:uint = -3; // Assign int/Number value to uint variable
trace(myUInt); // 4294967293
var myNum:Number = sampleUINT; // Assign int/uint value to Number variable
trace(myNum) // 4294967293
var myInt:int = uint.MAX_VALUE + 1; // Assign Number value to uint variable
trace(myInt); // 0
myInt = int.MAX_VALUE + 1; // Assign uint/Number value to int variable
trace(myInt); // -2147483648
The following table summarizes the results of casting to the Number, int, or uint data type
from other data types.
Data type or value Result of conversion to Number, int or uint
Boolean If the value is true, 1; otherwise, 0.
Date The internal representation of the Date object, which is the number
of milliseconds since midnight January 1, 1970, universal time.
null 0
Object If the instance is null and converted to Number, NaN; otherwise, 0.
String A number if Flash Player can convert the string to a number;
otherwise, NaN if converted to Number, or 0 if converted to int or uint.
undefined If converted to Number, NaN; if converted to int or uint, 0.
Data types 101
Casting to Boolean
Casting to Boolean from any of the numeric data types (uint, int, and Number) results in
false if the numeric value is 0, and true otherwise. For the Number data type, the value NaN
also results in false. The following example shows the results of casting the numbers -1, 0,
and 1:
var myNum:Number;
for (myNum = -1; myNum<2; myNum++)
{
trace("Boolean(" + myNum +") is " + Boolean(myNum));
}
The output from the example shows that, of the three numbers, only 0 returns a value of
false:
Boolean(-1) is true
Boolean(0) is false
Boolean(1) is true
Casting to Boolean from a String value returns false if the string is either null or an empty
string (""). Otherwise, it returns true.
var str1:String; // Uninitialized string is null.
trace(Boolean(str1)); // false
var str2:String = ""; // empty string
trace(Boolean(str2)); // false
var str3:String = " "; // white space only
trace(Boolean(str3)); // true
Casting to Boolean from an instance of the Object class returns false if the instance is null;
otherwise, it returns true:
var myObj:Object; // Uninitialized object is null.
trace(Boolean(myObj)); // false
myObj = new Object(); // instantiate
trace(Boolean(myObj)); // true
102 ActionScript language and syntax
Boolean variables get special treatment in strict mode in that you can assign values of any data
type to a Boolean variable without casting. Implicit coercion from all data types to the
Boolean data type occurs even in strict mode. In other words, unlike almost all other data
types, casting to Boolean is not necessary to avoid strict mode errors. The following examples
all compile in strict mode and behave as expected at run time:
var myObj:Object = new Object(); // instantiate
var bool:Boolean = myObj;
trace(bool); // true
bool = "random string";
trace(bool); // true
bool = new Array();
trace(bool); // true
bool = NaN;
trace(bool); // false
The following table summarizes the results of casting to the Boolean data type from other data
types:
Casting to String
Casting to the String data type from any of the numeric data types returns a string
representation of the number. Casting to the String data type from a Boolean value returns the
string “true” if the value is true, and returns the string “false” if the value is false.
Casting to the String data type from an instance of the Object class returns the string “null”
if the instance is null. Otherwise, casting to the String type from the Object class returns the
string “[object Object]”.
Casting to String from an instance of the Array class returns a string comprising a comma-
delimited list of all the array elements. For example, the following cast to the String data type
returns one string containing all three elements of the array:
var myArray:Array = ["primary", "secondary", "tertiary"];
trace(String(myArray)); // primary,secondary,tertiary
Data type or value Result of conversion to Boolean
String false if the value is null or the empty string (""); true otherwise.
null false
Number, int or uint false if the value is NaN or 0; true otherwise.
Object false if the instance is null; true otherwise.
Syntax 103
Casting to String from an instance of the Date class returns a string representation of the date
that the instance contains. For example, the following example returns a string representation
of the Date class instance (the output shows result for Pacific Daylight Time):
var myDate:Date = new Date(2005,6,1);
trace(String(myDate)); // Fri Jul 1 00:00:00 GMT-0700 2005
The following table summarizes the results of casting to the String data type from other data
types.
Syntax
The syntax of a language defines a set of rules that must be followed when writing executable
code.
Case sensitivity
ActionScript 3.0 is a case-sensitive language. Identifiers that differ only in case are considered
different identifiers. For example, the following code creates two different variables:
var num1:int;
var Num1:int;
Dot syntax
The dot operator (.) provides a way to access the properties and methods of an object. Using
dot syntax, you can refer to a class property or method by using an instance name, followed by
the dot operator and name of the property or method. For example, consider the following
class definition:
class DotExample
{
Data type or value Result of conversion to string
Array A string comprising all array elements.
Boolean "true" or "false"
Date A string representation of the Date object.
null "null"
Number, int or uint A string representation of the number.
Object If the instance is null, "null"; otherwise, "[object Object]”.
104 ActionScript language and syntax
public var prop1:String;
public function method1():void {}
}
Using dot syntax, you can access the prop1 property and the method1() method by using the
instance name created in the following code:
var myDotEx:DotExample = new DotExample();
myDotEx.prop1 = “hello”;
myDotEx.method1();
You can use dot syntax when you define packages. You use the dot operator to refer to nested
packages. For example, the EventDispatcher class resides in a package named events that is
nested within the package named flash. You can refer to the events package using the
following expression:
flash.events
You can also refer to the EventDispatcher class using this expression:
flash.events.EventDispatcher
Slash syntax
Slash syntax is not supported in ActionScript 3.0. Slash syntax was used in earlier versions of
ActionScript to indicate the path of a movie clip or variable.
Literals
A literal is a value that appears directly in your code. The following examples are all literals:
17
"hello"
-3
9.4
null
undefined
true
false
Literals can also be grouped to form compound literals. Array literals are enclosed in bracket
characters ([]) and use the comma to separate array elements.
Syntax 105
An array literal can be used to initialize an array. The following examples show two arrays that
are initialized using array literals. You can use the new statement and pass the compound
literal as a parameter to the Array class constructor, but you can also assign literal values
directly when instantiating instances of the following ActionScript core classes: Object, Array,
String, Number, int, uint, XML, XMLList and Boolean.
// Use new statement.
var myStrings:Array = new Array(["alpha", "beta", "gamma"]);
var myNums:Array = new Array([1,2,3,5,8]);
// Assign literal directly.
var myStrings:Array = ["alpha", "beta", "gamma"];
var myNums:Array = [1,2,3,5,8];
Literals can also be used to initialize a generic object. A generic object is an instance of the
Object class. Object literals are enclosed in curly braces ({}) and use the comma to separate
object properties. Each property is declared with the colon character (:), which separates the
name of the property from the value of the property.
You can create a generic object using the new statement, and pass the object literal as a
parameter to the Object class constructor, or you can assign the object literal directly to the
instance you are declaring. The following example creates a new generic object and initializes
the object with three properties (propA, propB, and propC), each with values set to 1, 2, and
3, respectively:
// Use new statement.
var myObject:Object = new Object({propA:1, propB:2, propC:3});
// Assign literal directly.
var myObject:Object = {propA:1, propB:2, propC:3};
For more information, see “Basics of strings” on page 204, “Basics of regular expressions”
on page 286, and “Initializing XML variables” on page 354.
Semicolons
You can use the semicolon character (;) to terminate a statement. Alternatively, if you omit
the semicolon character, the compiler will assume that each line of code represents a single
statement. Because many programmers are accustomed to using the semicolon to denote the
end of a statement, your code may be easier to read if you consistently use semicolons to
terminate your statements.
Using a semicolon to terminate a statement allows you to place more than one statement on a
single line, but this may make your code more difficult to read.
106 ActionScript language and syntax
Parentheses
You can use parentheses (()) in three ways in ActionScript 3.0. First, you can use parentheses
to change the order of operations in an expression. Operations that are grouped inside
parentheses are always executed first. For example, parentheses are used to alter the order of
operations in the following code:
trace(2 + 3 * 4); // 14
trace( (2 + 3) * 4); // 20
Second, you can use parentheses with the comma operator (,) to evaluate a series of
expressions and return the result of the final expression, as shown in the following example:
var a:int = 2;
var b:int = 3;
trace((a++, b++, a+b)); // 7
Third, you can use parentheses to pass one or more parameters to functions or methods, as
shown in the following example, which passes a String value to the trace() function:
trace("hello"); // hello
Comments
ActionScript 3.0 code supports two types of comments: single-line comments and multiline
comments. These commenting mechanisms are similar to the commenting mechanisms in
C++ and Java. The compiler will ignore text that is marked as a comment.
Single-line comments begin with two forward slash characters (//) and continue until the end
of the line. For example, the following code contains a single-line comment:
var someNumber:Number = 3; // a single line comment
Multiline comments begin with a forward slash and asterisk (/*) and end with an asterisk and
forward slash (*/).
/* This is multiline comment that can span
more than one line of code. */
Syntax 107
Keywords and reserved words
Reserved words are words that you cannot use as identifiers in your code because the words are
reserved for use by ActionScript. Reserved words include lexical keywords, which are removed
from the program namespace by the compiler. The compiler will report an error if you use a
lexical keyword as an identifier. The following table lists ActionScript 3.0 lexical keywords.
There is a small set of keywords, called syntactic keywords, that can be used as identifiers, but
that have special meaning in certain contexts. The following table lists ActionScript 3.0
syntactic keywords.
There are also several identifiers that are sometimes referred to as future reserved words. These
identifiers are not reserved by ActionScript 3.0, though some of them may be treated as
keywords by software that incorporates ActionScript 3.0. You might be able to use many of
these identifiers in your code, but Adobe recommends that you do not use them because they
may appear as keywords in a subsequent version of the language.
as break case catch
class const continue default
delete do else extends
false finally for function
if implements import in
instanceof interface internal is
native new null package
private protected public return
super switch this throw
to true try typeof
use var void while
with
each get set namespace
include dynamic final native
override static
abstract boolean byte cast
char debugger double enum
export float goto intrinsic
108 ActionScript language and syntax
Constants
ActionScript 3.0 supports the const statement, which you can use to create constants.
Constants are properties with a fixed value that cannot be altered. You can assign a value to a
constant only once, and the assignment must occur in close proximity to the declaration of
the constant. For example, if a constant is declared as a member of a class, you can assign a
value to that constant only as part of the declaration or inside the class constructor.
The following code declares two constants. The first constant, MINIMUM, has a value assigned
as part of the declaration statement. The second constant, MAXIMUM, has a value assigned in
the constructor.
class A
{
public const MINIMUM:int = 0;
public const MAXIMUM:int;
public function A()
{
MAXIMUM = 10;
}
}
var a:A = new A();
trace(a.MINIMUM); // 0
trace(a.MAXIMUM); // 10
An error results if you attempt to assign an initial value to a constant in any other way. For
example, if you attempt to set the initial value of MAXIMUM outside the class, a run-time error
will occur.
class A
{
public const MINIMUM:int = 0;
public const MAXIMUM:int;
}
var a:A = new A();
a["MAXIMUM"] = 10; // run-time error
long prototype short synchronized
throws to transient type
virtual volatile
Operators 109
The Flash Player API defines a wide range of constants for your use. By convention, constants
in ActionScript use all capital letters, with words separated by the underscore character (_).
For example, the MouseEvent class definition uses this naming convention for its constants,
each of which represents an event related to mouse input:
package flash.events
{
public class MouseEvent extends Event
{
public static const CLICK:String = "click";
public static const DOUBLE_CLICK:String = "doubleClick";
public static const MOUSE_DOWN:String = "mouseDown";
public static const MOUSE_MOVE:String = "mouseMove";
...
}
}
Operators
Operators are special functions that take one or more operands and return a value. An operand
is a value—usually a literal, a variable, or an expression—that an operator uses as input. For
example, in the following code, the addition (+) and multiplication (*) operators are used
with three literal operands (2, 3, and 4) to return a value. This value is then used by the
assignment (=) operator to assign the returned value, 14, to the variable sumNumber.
var sumNumber:uint = 2 + 3 * 4; // uint = 14
Operators can be unary, binary, or ternary. A unary operator takes one operand. For example,
the increment (++) operator is a unary operator, because it takes only one operand. A binary
operator takes two operands. For example, the division (/) operator takes two operands. A
ternary operator takes three operands. For example, the conditional (?:) operator takes three
operands.
Some operators are overloaded, which means that they behave differently depending on the
type or quantity of operands passed to them. The addition (+) operator is an example of an
overloaded operator that behaves differently depending on the data type of the operands. If
both operands are numbers, the addition operator returns the sum of the values. If both
operands are strings, the addition operator returns the concatenation of the two operands.
The following example code shows how the operator behaves differently depending on the
operands:
trace(5 + 5); // 10
trace("5" + "5"); // 55
110 ActionScript language and syntax
Operators can also behave differently based on the number of operands supplied. The
subtraction (-) operator is both a unary and binary operator. When supplied with only one
operand, the subtraction operator negates the operand and returns the result. When supplied
with two operands, the subtraction operator returns the difference between the operands. The
following example shows the subtraction operator used first as a unary operator, and then as a
binary operator.
trace(-3); // -3
trace(7-2); // 5
Operator precedence and associativity
Operator precedence and associativity determine the order in which operators are processed.
Although it may seem natural to those familiar with arithmetic that the compiler processes the
multiplication (*) operator before the addition (+) operator, the compiler needs explicit
instructions about which operators to process first. Such instructions are collectively referred
to as operator precedence. ActionScript defines a default operator precedence that you can alter
using the parentheses (()) operator. For example, the following code alters the default
precedence in the previous example to force the compiler to process the addition operator
before the multiplication operator:
var sumNumber:uint = (2 + 3) * 4; // uint == 20
You may encounter situations in which two or more operators of the same precedence appear
in the same expression. In these cases, the compiler uses the rules of associativity to determine
which operator to process first. All of the binary operators, except the assignment operators,
are left-associative, which means that operators on the left are processed before operators on
the right. The assignment operators and the conditional (?:) operator are right-associative,
which means that the operators on the right are processed before operators on the left.
For example, consider the less-than (<) and greater-than (>) operators, which have the same
precedence. If both operators are used in the same expression, the operator on the left is
processed first because both operators are left-associative. This means that the following two
statements produce the same output:
trace(3 > 2 < 1); // false
trace((3 > 2) < 1); // false
The greater-than operator is processed first, which results in a value of true, because the
operand 3 is greater than the operand 2. The value true is then passed to the less-than
operator along with the operand 1. The following code represents this intermediate state:
trace((true) < 1);
Operators 111
The less-than operator converts the value true to the numeric value 1 and compares that
numeric value to the second operand 1 to return the value false (the value 1 is not less than
1).
trace(1 < 1); // false
You can alter the default left associativity with the parentheses operator. You can instruct the
compiler to process the less-than operator first by enclosing that operator and its operands in
parentheses. The following example uses the parentheses operator to produce a different
output using the same numbers as the previous example:
trace(3 > (2 < 1)); // true
The less-than operator is processed first, which results in a value of false because the operand
2 is not less than the operand 1. The value false is then passed to the greater-than operator
along with the operand 3. The following code represents this intermediate state:
trace(3 > (false));
The greater-than operator converts the value false to the numeric value 0 and compares that
numeric value to the other operand 3 to return true (the value 3 is greater than 0).
trace(3 > 0); // true
The following table lists the operators for ActionScript 3.0 in order of decreasing precedence.
Each row of the table contains operators of the same precedence. Each row of operators has
higher precedence than the row appearing below it in the table.
Group Operators
Primary [] {x:y} () f(x) new x.y x[y] <></> @ :: ..
Postfix x++ x--
Unary ++x --x + - ~ ! delete typeof void
Multiplicative * / %
Additive + -
Bitwise shift << >> >>>
Relational < > <= >= as in instanceof is
Equality == != === !==
Bitwise AND &
Bitwise XOR ^
Bitwise OR |
Logical AND &&
Logical OR ||
112 ActionScript language and syntax
Primary operators
The primary operators include those used for creating Array and Object literals, grouping
expressions, calling functions, instantiating class instances, and accessing properties.
All the primary operators, as listed in the following table, have equal precedence. Operators
that are part of the E4X specification are indicated by the (E4X) notation.
Postfix operators
The postfix operators take one operator and either increment or decrement the value.
Although these operators are unary operators, they are classified separately from the rest of the
unary operators because of their higher precedence and special behavior. When a postfix
operator is used as part of a larger expression, the expression’s value is returned before the
postfix operator is processed. For example, the following code shows how the value of the
expression xNum++ is returned before the value is incremented:
var xNum:Number = 0;
trace(xNum++); // 0
trace(xNum); // 1
Conditional ?:
Assignment = *= /= %= += -= <<= >>= >>>= &= ^= |=
Comma ,
Operator Operation performed
[] Initializes an array
{x:y} Initializes an object
() Groups expressions
f(x) Calls a function
new Calls a constructor
x.y x[y] Accesses a property
<></> Initializes an XMLList object (E4X)
@ Accesses an attribute (E4X)
:: Qualifies a name (E4X)
.. Accesses a descendant XML element (E4X)
Group Operators
Operators 113
All the postfix operators, as listed in the following table, have equal precedence:
Unary operators
The unary operators take one operand. The increment (++) and decrement (--) operators in
this group are prefix operators, which means that they appear before the operand in an
expression. The prefix operators differ from their postfix counterparts in that the increment or
decrement operation is completed before the value of the overall expression is returned. For
example, the following code shows how the value of the expression ++xNum is returned after
the value is incremented:
var xNum:Number = 0;
trace(++xNum); // 1
trace(xNum); // 1
All the unary operators, as listed in the following table, have equal precedence:
Operator Operation performed
++ Increments (postfix)
-- Decrements (postfix)
Operator Operation performed
++ Increments (prefix)
-- Decrements (prefix)
+ Unary +
- Unary - (negation)
! Logical NOT
~ Bitwise NOT
delete Deletes a property
typeof Returns type information
void Returns undefined value
114 ActionScript language and syntax
Multiplicative operators
The multiplicative operators take two operands and perform multiplication, division, or
modulo calculations.
All the multiplicative operators, as listed in the following table, have equal precedence:
Additive operators
The additive operators take two operands and perform addition or subtraction calculations.
All the additive operators, as listed in the following table, have equal precedence:
Bitwise shift operators
The bitwise shift operators take two operands and shift the bits of the first operand to the
extent specified by the second operand. All the bitwise shift operators, as listed in the
following table, have equal precedence:
Operator Operation performed
* Multiplication
/ Division
% Modulo
Operator Operation performed
+ Addition
- Subtraction
Operator Operation performed
<< Bitwise left shift
>> Bitwise right shift
>>> Bitwise unsigned right shift
Operators 115
Relational operators
The relational operators take two operands, compare their values, and return a Boolean value.
All the relational operators, as listed in the following table, have equal precedence:
Equality operators
The equality operators take two operands, compare their values, and return a Boolean value.
All the equality operators, as listed in the following table, have equal precedence:
Bitwise logical operators
The bitwise logical operators take two operands and perform bit-level logical operations. The
bitwise logical operators differ in precedence and are listed in the following table in order of
decreasing precedence:
Operator Operation performed
< Less than
> Greater than
<= Less than or equal to
>= Greater than or equal to
as Checks data type
in Checks for object properties
instanceof Checks prototype chain
is Checks data type
Operator Operation performed
== Equality
!= Inequality
=== Strict equality
!== Strict inequality
Operator Operation performed
& Bitwise AND
^ Bitwise XOR
| Bitwise OR
116 ActionScript language and syntax
Logical operators
The logical operators take two operands and return a Boolean result. The logical operators
differ in precedence and are listed in the following table in order of decreasing precedence:
Conditional operator
The conditional operator is a ternary operator, which means that it takes three operands. The
conditional operator is a shorthand method of applying the if..else conditional statement.
Assignment operators
The assignment operators take two operands and assign a value to one operand, based on the
value of the other operand. All the assignment operators, as listed in the following table, have
equal precedence:
Operator Operation performed
&& Logical AND
|| Logical OR
Operator Operation performed
?: Conditional
Operator Operation performed
= Assignment
*= Multiplication assignment
/= Division assignment
%= Modulo assignment
+= Addition assignment
-= Subtraction assignment
<<= Bitwise left shift assignment
>>= Bitwise right shift assignment
>>>= Bitwise unsigned right shift assignment
&= Bitwise AND assignment
^= Bitwise XOR assignment
|= Bitwise OR assignment
Conditionals 117
Conditionals
ActionScript 3.0 provides three basic conditional statements that you can use to control
program flow.
if..else
The if..else conditional statement allows you to test a condition and execute a block of
code if that condition exists, or execute an alternative block of code if the condition does not
exist. For example, the following code tests whether the value of x exceeds 20, generates a
trace() function if it does, or generates a different trace() function if it does not:
if (x > 20)
{
trace("x is > 20");
}
else
{
trace("x is <= 20");
}
If you do not want to execute an alternative block of code, you can use the if statement
without the else statement.
if..else if
You can test for more than one condition using the if..else if conditional statement. For
example, the following code not only tests whether the value of x exceeds 20, but also tests
whether the value of x is negative:
if (x > 20)
{
trace("x is > 20");
}
else if (x < 0)
{
trace("x is negative");
}
If an if or else statement is followed by only one statement, the statement does not need to
be enclosed in braces. For example, the following code does not use braces:
if (x > 0)
trace("x is positive”);
else if (x < 0)
118 ActionScript language and syntax
trace("x is negative");
else
trace("x is 0");
However, Adobe recommends that you always use braces, because unexpected behavior can
occur if statements are later added to a conditional statement that lacks braces. For example,
in the following code the value of positiveNums increases by 1 whether or not the condition
evaluates to true:
var x:int;
var positiveNums:int = 0;
if (x > 0)
trace("x is positive");
positiveNums++;
trace(positiveNums); // 1
switch
The switch statement is useful if you have several execution paths that depend on the same
condition expression. It provides functionality similar to a long series of if..else if
statements, but is somewhat easier to read. Instead of testing a condition for a Boolean value,
the switch statement evaluates an expression and uses the result to determine which block of
code to execute. Blocks of code begin with a case statement and end with a break statement.
For example, the following switch statement prints the day of the week, based on the day
number returned by the Date.getDay() method:
var someDate:Date = new Date();
var dayNum:uint = someDate.getDay();
switch(dayNum)
{
case 0:
trace("Sunday");
break;
case 1:
trace("Monday");
break;
case 2:
trace("Tuesday");
break;
case 3:
trace("Wednesday");
break;
case 4:
trace("Thursday");
break;
Looping 119
case 5:
trace("Friday");
break;
case 6:
trace("Saturday");
break;
default:
trace("Out of range");
break;
}
Looping
Looping statements allow you to perform a specific block of code repeatedly using a series of
values or variables. Adobe recommends that you always enclose the block of code in braces
({}). Although you can omit the braces if the block of code contains only one statement, this
practice is not recommended for the same reason that it is not recommended for conditionals:
it increases the likelihood that statements added later will be inadvertently excluded from the
block of code. If you later add a statement that you want to include in the block of code, but
forget to add the necessary braces, the statement will not be executed as part of the loop.
for
The for loop allows you to iterate through a variable for a specific range of values. You must
supply three expressions in a for statement: a variable that is set to an initial value, a
conditional statement that determines when the looping ends, and an expression that changes
the value of the variable with each loop. For example, the following code loops five times. The
value of the variable i starts at 0 and ends at 4, and the output will be the numbers 0 through
4, each on its own line.
var i:int;
for (i = 0; i < 5; i++)
{
trace(i);
}
for..in
The for..in loop iterates through the properties of an object, or the elements of an array. For
example, you can use a for..in loop to iterate through the properties of a generic object
(object properties are not kept in any particular order, so properties may appear in a seemingly
random order):
120 ActionScript language and syntax
var myObj:Object = {x:20, y:30};
for (var i:String in myObj)
{
trace(i + ": " + myObj[i]);
}
// output:
// x: 20
// y: 30
You can also iterate through the elements of an array:
var myArray:Array = ["one", "two", "three"];
for (var i:String in myArray)
{
trace(myArray[i]);
}
// output:
// one
// two
// three
What you cannot do is iterate through the properties of an object if it is an instance of a user-
defined class, unless the class is a dynamic class. Even with instances of dynamic classes, you
will be able to iterate only through properties that are added dynamically.
for each..in
The for each..in loop iterates through the items of a collection, which can be tags in an
XML or XMLList object, the values held by object properties, or the elements of an array. For
example, as the following excerpt shows, you can use a for each..in loop to iterate through
the properties of a generic object, but unlike the for..in loop, the iterator variable in a for
each..in loop contains the value held by the property instead of the name of the property:
var myObj:Object = {x:20, y:30};
for each (var num in myObj)
{
trace(num);
}
// output:
// 20
// 30
You can iterate through an XML or XMLList object, as the following example shows:
var myXML:XML = <users>
<fname>Jane</fname>
<fname>Susan</fname>
<fname>John</fname>
</users>;
Looping 121
for each (var item in myXML.fname)
{
trace(item);
}
/* output
Jane
Susan
John
*/
You can also iterate through the elements of an array, as this example shows:
var myArray:Array = ["one", "two", "three"];
for each (var item in myArray)
{
trace(item);
}
// output:
// one
// two
// three
You cannot iterate through the properties of an object if the object is an instance of a sealed
class. Even for instances of dynamic classes, you cannot iterate through any fixed properties,
which are properties defined as part of the class definition.
while
The while loop is like an if statement that repeats as long as the condition is true. For
example, the following code produces the same output as the for loop example:
var i:int = 0;
while (i < 5)
{
trace(i);
i++;
}
One disadvantage of using a while loop instead of a for loop is that infinite loops are easier
to write with while loops. The for loop example code does not compile if you omit the
expression that increments the counter variable, but the while loop example does compile if
you omit that step. Without the expression that increments i, the loop becomes an infinite
loop.
122 ActionScript language and syntax
do..while
The do..while loop is a while loop that guarantees that the code block is executed at least
once, because the condition is checked after the code block is executed. The following code
shows a simple example of a do..while loop that generates output even though the condition
is not met:
var i:int = 5;
do
{
trace(i);
i++;
} while (i < 5);
// output: 5
Functions
Functions are blocks of code that carry out specific tasks and can be reused in your program.
There are two types of functions in ActionScript 3.0: methods and function closures. Whether a
function is a called a method or a function closure depends on the context in which the
function is defined. A function is called a method if you define it as part of a class definition
or attach it to an instance of an object. A function is called a function closure if it is defined in
any other way.
Functions have always been extremely important in ActionScript. In ActionScript 1.0, for
example, the class keyword did not exist, so “classes” were defined by constructor functions.
Although the class keyword has since been added to the language, a solid understanding of
functions is still important if you want to take full advantage of what the language has to offer.
This can be a challenge for programmers who expect ActionScript functions to behave
similarly to functions in languages such as C++ or Java. Although basic function definition
and invocation should not present a challenge to experienced programmers, some of the more
advanced features of ActionScript functions require some explanation.
Basic function concepts
This section discusses basic function definition and invocation techniques.
Functions 123
Calling functions
You call a function by using its identifier followed by the parentheses operator (()). You use
the parentheses operator to enclose any function parameters you want to send to the function.
For example, the trace() function, which is a top-level function in the Flash Player API, is
used throughout this book:
trace(“Use trace to help debug your script”);
If you are calling a function with no parameters, you must use an empty pair of parentheses.
For example, you can use the Math.random() method, which takes no parameters, to
generate a random number:
var randomNum:Number = Math.random();
Defining your own functions
There are two ways to define a function in ActionScript 3.0: you can use a function statement
or a function expression. The technique you choose depends on whether you prefer a more
static or dynamic programming style. Define your functions with function statements if you
prefer static, or strict mode, programming. Define your functions with function expressions if
you have a specific need to do so. Function expressions are more often used in dynamic, or
standard mode, programming.
Function statements
Function statements are the preferred technique for defining functions in strict mode. A
function statement begins with the function keyword, followed by:
■ The function name
■ The parameters, in a comma-delimited list enclosed in parentheses
■ The function body—that is, the ActionScript code to be executed when the function is
invoked, enclosed in curly braces
For example, the following code creates a function that defines a parameter and then invokes
the function using the string “hello” as the parameter value:
function traceParameter(aParam:String)
{
trace(aParam);
}
traceParameter("hello"); // hello
124 ActionScript language and syntax
Function expressions
The second way to declare a function is to use an assignment statement with a function
expression, which is also sometimes called a function literal or an anonymous function. This
is a more verbose method that is widely used in earlier versions of ActionScript.
An assignment statement with a function expression begins with the var keyword, followed by:
■ The function name
■ The colon operator (:)
■ The Function class to indicate the data type
■ The assignment operator (=)
■ The function keyword
■ The parameters, in a comma-delimited list enclosed in parentheses
■ The function body—that is, the ActionScript code to be executed when the function is
invoked, enclosed in curly braces
For example, the following code declares the traceParameter function using a function
expression:
var traceParameter:Function = function (aParam:String)
{
trace(aParam);
};
traceParameter("hello"); // hello
Notice that you do not specify a function name, as you do in a function statement. Another
important difference between function expressions and function statements is that a function
expression is an expression rather than a statement. This means that a function expression
cannot stand on its own as a function statement can. A function expression can be used only
as a part of a statement, usually an assignment statement. The following example shows a
function expression assigned to an array element:
var traceArray:Array = new Array();
traceArray[0] = function (aParam:String)
{
trace(aParam);
};
traceArray[0]("hello");
Functions 125
Choosing between statements and expressions
As a general rule, use a function statement unless specific circumstances call for the use of an
expression. Function statements are less verbose, and they provide a more consistent
experience between strict mode and standard mode than function expressions.
Function statements are easier to read than assignment statements that contain function
expressions. Function statements make your code more concise; they are less confusing than
function expressions, which require you to use both the var and function keywords.
Function statements provide a more consistent experience between the two compiler modes in
that you can use dot syntax in both strict and standard mode to invoke a method declared
using a function statement. This is not necessarily true for methods declared with a function
expression. For example, the following code defines a class named Example with two
methods: methodExpression(), which is declared with a function expression, and
methodStatement(), which is declared with a function statement. In strict mode, you cannot
use dot syntax to invoke the methodExpression() method.
class Example
{
var methodExpression = function() {}
function methodStatement() {}
}
var myEx:Example = new Example();
myEx.methodExpression(); // error in strict mode; okay in standard mode
myEx.methodStatement(); // okay in strict and standard modes
Function expressions are considered better suited to programming that focuses on run-time,
or dynamic, behavior. If you prefer to use strict mode, but also need to call a method declared
with a function expression, you can use either of two techniques. First, you can call the
method using square brackets ([]) instead of the dot (.) operator. The following method call
succeeds in both strict mode and standard mode:
myExample["methodLiteral"]();
Second, you can declare the entire class as a dynamic class. Although this allows you to call the
method using the dot operator, the downside is that you sacrifice some strict mode
functionality for all instances of that class. For example, the compiler does not generate an
error if you attempt to access an undefined property on an instance of a dynamic class.
There are some circumstances in which function expressions are useful. One common use of
function expressions is for functions that are used only once and then discarded. Another less
common use is for attaching a function to a prototype property. For more information, see
“The prototype object” on page 177.
126 ActionScript language and syntax
There are two subtle differences between function statements and function expressions that
you should take into account when choosing which technique to use. The first difference is
that function expressions do not exist independently as objects with regard to memory
management and garbage collection. In other words, when you assign a function expression to
another object, such as an array element or an object property, you create the only reference to
that function expression in your code. If the array or object to which your function expression
is attached goes out of scope or is otherwise no longer available, you will no longer have access
to the function expression. If the array or object is deleted, the memory that the function
expression uses will become eligible for garbage collection, which means that the memory is
eligible to be reclaimed and reused for other purposes.
The following example shows that for a function expression, once the property to which the
expression is assigned is deleted, the function is no longer available. The class Test is dynamic,
which means that you can add a property named functionExp that holds a function
expression. The functionExp() function can be called with the dot operator, but once the
functionExp property is deleted, the function is no longer accessible.
dynamic class Test {}
var myTest:Test = new Test();
// function expression
myTest.functionExp = function () { trace("Function expression") };
myTest.functionExp(); // Function expression
delete myTest.functionExp;
myTest.functionExp(); // error
If, on the other hand, the function is first defined with a function statement, it exists as its
own object and continues to exist even after you delete the property to which it is attached.
The delete operator only works on properties of objects, so even a call to delete the function
stateFunc() itself does not work.
dynamic class Test {}
var myTest:Test = new Test();
// function statement
function stateFunc() { trace("Function statement") }
myTest.statement = stateFunc;
myTest.statement(); // Function statement
delete myTest.statement;
delete stateFunc; // no effect
stateFunc(); // Function statement
myTest.statement(); // error
Functions 127
The second difference between function statements and function expressions is that function
statements exist throughout the scope in which they are defined, including in statements that
appear before the function statement. Function expressions, by contrast, are defined only for
subsequent statements. For example, the following code successfully calls the scopeTest()
function before it is defined:
statementTest(); // statementTest
function statementTest():void
{
trace("statementTest");
}
Function expressions are not available before they are defined, so the following code results in
a run-time error:
expressionTest(); // run-time error
var expressionTest:Function = function ()
{
trace("expressionTest");
}
Returning values from functions
To return a value from your function, use the return statement followed by the expression or
literal value that you want to return. For example, the following code returns an expression
representing the parameter:
function doubleNum(baseNum:int):int
{
return (baseNum * 2);
}
Notice that the return statement terminates the function, so that any statements below a
return statement will not be executed, as follows:
function doubleNum(baseNum:int):int {
return (baseNum * 2);
trace("after return"); // This trace statement will not be executed.
}
In strict mode, you must return a value of the appropriate type if you choose to specify a
return type. For example, the following code generates an error in strict mode, because it does
not return a valid value:
function doubleNum(baseNum:int):int
{
trace("after return");
}
128 ActionScript language and syntax
Nested functions
You can nest functions, which means that functions can be declared within other functions. A
nested function is available only within its parent function unless a reference to the function is
passed to external code. For example, the following code declares two nested functions inside
the getNameAndVersion() function:
function getNameAndVersion():String
{
function getVersion():String
{
return "9";
}
function getProductName():String
{
return "Flash Player";
}
return (getProductName() + " " + getVersion());
}
trace(getNameAndVersion()); // Flash Player 9
When nested functions are passed to external code, they are passed as function closures, which
means that the function retains any definitions that are in scope when the function is defined.
For more information, see “Function closures” on page 136.
Function parameters
ActionScript 3.0 provides some functionality for function parameters that may seem novel for
programmers new to the language. Although the idea of passing parameters by value or
reference should be familiar to most programmers, the arguments object and the ... (rest)
parameter may be new to many of you.
Functions 129
Passing arguments by value or by reference
In many programming languages, it’s important to understand the distinction between
passing arguments by value or by reference; the distinction can affect the way code is
designed.
To be passed by value means that the value of the argument is copied into a local variable for
use within the function. To be passed by reference means that only a reference to the
argument is passed instead of the actual value. No copy of the actual argument is made.
Instead, a reference to the variable passed as an argument is created and assigned to a local
variable for use within the function. As a reference to a variable outside the function, the local
variable gives you the ability to change the value of the original variable.
In ActionScript 3.0, all arguments are passed by reference, because all values are stored as
objects. However, objects that belong to the primitive data types, which includes Boolean,
Number, int, uint, and String, have special operators that make them behave as if they were
passed by value. For example, the following code creates a function named
passPrimitives() that defines two parameters named xParam and yParam, both of type int.
These parameters are similar to local variables declared inside the body of the
passPrimitives() function. When the function is called with the arguments xValue and
yValue, the parameters xParam and yParam are initialized with references to the int objects
represented by xValue and yValue. Because the arguments are primitives, they behave as if
passed by value. Although xParam and yParam initially contain only references to the xValue
and yValue objects, any changes to the variables within the function body generate new
copies of the values in memory.
function passPrimitives(xParam:int, yParam:int):void
{
xParam++;
yParam++;
trace(xParam, yParam);
}
var xValue:int = 10;
var yValue:int = 15;
trace(xValue, yValue); // 10 15
passPrimitives(xValue, yValue); // 11 16
trace(xValue, yValue); // 10 15
Within the passPrimitives() function, the values of xParam and yParam are incremented,
but this does not affect the values of xValue and yValue, as shown in the last trace
statement. This would be true even if the parameters were named identically to the variables,
xValue and yValue, because the xValue and yValue inside the function would point to new
locations in memory that exist separately from the variables of the same name outside the
function.
130 ActionScript language and syntax
All other objects—that is, objects that do not belong to the primitive data types—are always
passed by reference, which gives you ability to change the value of the original variable. For
example, the following code creates an object named objVar with two properties, x and y.
The object is passed as an argument to the passByRef() function. Because the object is not a
primitive type, the object is not only passed by reference, but also stays a reference. This
means that changes made to the parameters within the function will affect the object
properties outside the function.
function passByRef(objParam:Object):void
{
objParam.x++;
objParam.y++;
trace(objParam.x, objParam.y);
}
var objVar:Object = {x:10, y:15};
trace(objVar.x, objVar.y); // 10 15
passByRef(objVar); // 11 16
trace(objVar.x, objVar.y); // 11 16
The objParam parameter references the same object as the global objVar variable. As you can
see from the trace statements in the example, changes to the x and y properties of the
objParam object are reflected in the objVar object.
Default parameter values
New in ActionScript 3.0 is the ability to declare default parameter values for a function. If a
call to a function with default parameter values omits a parameter with default values, the
value specified in the function definition for that parameter is used. All parameters with
default values must be placed at the end of the parameter list. The values assigned as default
values must be compile-time constants. The existence of a default value for a parameter
effectively makes that parameter an optional parameter. A parameter without a default value is
considered a required parameter.
For example, the following code creates a function with three parameters, two of which have
default values. When the function is called with only one parameter, the default values for the
parameters are used.
function defaultValues(x:int, y:int = 3, z:int = 5):void
{
trace(x, y, z);
}
defaultValues(1); // 1 3 5
Functions 131
The arguments object
When parameters are passed to a function, you can use the arguments object to access
information about the parameters passed to your function. Some important aspects of the
arguments object include the following:
■ The arguments object is an array that includes all the parameters passed to the function.
■ The arguments.length property reports the number of parameters passed to the
function.
■ The arguments.callee property provides a reference to the function itself, which is
useful for recursive calls to function expressions.
ActionScript 3.0 allows function calls to include more parameters than those defined in the
function definition, but will generate a compiler error in strict mode if the number of
parameters is less than the number of required parameters. You can use the array aspect of the
arguments object to access any parameter passed to the function, whether or not that
parameter is defined in the function definition. The following example uses the arguments
array along with the arguments.length property to trace all the parameters passed to the
traceArgArray() function:
function traceArgArray(x:int):void
{
for (var i:uint = 0; i < arguments.length; i++)
{
trace(arguments[i]);
}
}
traceArgArray(1, 2, 3);
// output:
// 1
// 2
// 3
NOTE
The arguments object is not available if any parameter is named arguments or if you use
the ... (rest) parameter.
132 ActionScript language and syntax
The arguments.callee property is often used in anonymous functions to create recursion.
You can use it to add flexibility to your code. If the name of a recursive function changes over
the course of your development cycle, you need not worry about changing the recursive call in
your function body if you use arguments.callee instead of the function name. The
arguments.callee property is used in the following function expression to enable recursion:
var factorial:Function = function (x:uint)
{
if(x == 0)
{
return 1;
}
else
{
return (x * arguments.callee(x - 1));
}
}
trace(factorial(5)); // 120
If you use the ... (rest) parameter in your function declaration, the arguments object will not
be available to you. Instead, you must access the parameters using the parameter names that
you declared for them.
You should also be careful to avoid using the string “arguments” as a parameter name,
because it will shadow the arguments object. For example, if the function traceArgArray()
is rewritten so that an arguments parameter is added, the references to arguments in the
function body refer to the parameter rather than the arguments object. The following code
produces no output:
function traceArgArray(x:int, arguments:int):void
{
for (var i:uint = 0; i < arguments.length; i++)
{
trace(arguments[i]);
}
}
traceArgArray(1, 2, 3);
// no output
The arguments object in previous versions of ActionScript also contained a property named
caller, which is a reference to the function that called the current function. The caller
property is not present in ActionScript 3.0, but if you need a reference to the calling function,
you can alter the calling function so that it passes an extra parameter that is a reference to
itself.
Functions 133
The ... (rest) parameter
ActionScript 3.0 introduces a new parameter declaration called the ... (rest) parameter. This
parameter allows you to specify an array parameter that accepts any number of comma-
delimited arguments. The parameter can have any name that is not a reserved word. This
parameter declaration must be the last parameter specified. Use of this parameter makes the
arguments object unavailable. Although the ... (rest) parameter gives you the same
functionality as the arguments array and arguments.length property, it does not provide
functionality similar to that provided by arguments.callee. You should ensure that you do
not need to use arguments.callee before using the ... (rest) parameter.
The following example rewrites the traceArgArray() function using the ... (rest) parameter
instead of the arguments object:
function traceArgArray(... args):void
{
for (var i:uint = 0; i < args.length; i++)
{
trace(args[i]);
}
}
traceArgArray(1, 2, 3);
// output:
// 1
// 2
// 3
The ... (rest) parameter can also be used with other parameters, as long as it is the last
parameter listed. The following example modifies the traceArgArray() function so that its
first parameter, x, is of type int, and the second parameter uses the ... (rest) parameter. The
output skips the first value, because the first parameter is no longer part of the array created by
the ... (rest) parameter.
function traceArgArray(x: int, ... args)
{
for (var i:uint = 0; i < args.length; i++)
{
trace(args[i]);
}
}
traceArgArray(1, 2, 3);
// output:
// 2
// 3
134 ActionScript language and syntax
Functions as objects
Functions in ActionScript 3.0 are objects. When you create a function, you are creating an
object that can not only be passed as a parameter to another function, but also have properties
and methods attached to it.
Functions passed as arguments to another function are passed by reference and not by value.
When you pass a function as an argument, you use only the identifier and not the parentheses
operator that you use to call the method. For example, the following code passes a function
named clickListener() as an argument to the addEventListener() method:
addEventListener(MouseEvent.CLICK, clickListener);
The Array.sort() method also defines a parameter that accepts a function. For an example
of a custom sort function that is used as an argument to the Array.sort() function, see
“Sorting an array” on page 229.
Although it may seem strange to programmers new to ActionScript, functions can have
properties and methods, just as any other object can. In fact, every function has a read-only
property named length that stores the number of parameters defined for the function. This is
different from the arguments.length property, which reports the number of arguments sent
to the function. Recall that in ActionScript, the number of arguments sent to a function can
exceed the number of parameters defined for that function. The following example, which
compiles only in standard mode because strict mode requires an exact match between the
number of arguments passed and the number of parameters defined, shows the difference
between the two properties:
function traceLength(x:uint, y:uint):void
{
trace("arguments received: " + arguments.length);
trace("arguments expected: " + traceLength.length);
}
traceLength(3, 5, 7, 11);
/* output:
arguments received: 4
arguments expected: 2 */
Functions 135
You can define your own function properties by defining them outside your function body.
Function properties can serve as quasi-static properties that allow you to save the state of a
variable related to the function. For example, you may want to track the number of times a
particular function is called. Such functionality could be useful if you are writing a game and
want to track the number of times a user uses a specific command, although you could also
use a static class property for this. The following code creates a function property outside the
function declaration and increments the property each time the function is called:
someFunction.counter = 0;
function someFunction():void
{
someFunction.counter++;
}
someFunction();
someFunction();
trace(someFunction.counter); // 2
Function scope
A function’s scope determines not only where in a program that function can be called, but
also what definitions the function can access. The same scope rules that apply to variable
identifiers apply to function identifiers. A function declared in the global scope is available
throughout your code. For example, ActionScript 3.0 contains global functions, such as
isNaN() and parseInt(), that are available anywhere in your code. A nested function—a
function declared within another function—can be used anywhere in the function in which it
was declared.
The scope chain
Any time a function begins execution, a number of objects and properties are created. First, a
special object called an activation object is created that stores the parameters and any local
variables or functions declared in the function body. You cannot access the activation object
directly, because it is an internal mechanism. Second, a scope chain is created that contains an
ordered list of objects that Flash Player checks for identifier declarations. Every function that
executes has a scope chain that is stored in an internal property. For a nested function, the
scope chain starts with its own activation object, followed by its parent function’s activation
object. The chain continues in this manner until it reaches the global object. The global
object is created when an ActionScript program begins, and contains all global variables and
functions.
136 ActionScript language and syntax
Function closures
A function closure is an object that contains a snapshot of a function and its lexical
environment. A function’s lexical environment includes all the variables, properties, methods,
and objects in the function’s scope chain, along with their values. Function closures are
created any time a function is executed apart from an object or a class. The fact that function
closures retain the scope in which they were defined creates interesting results when a function
is passed as an argument or a return value into a different scope.
For example, the following code creates two functions: foo(), which returns a nested
function named rectArea() that calculates the area of a rectangle, and bar(), which calls
foo() and stores the returned function closure in a variable named myProduct. Even though
the bar() function defines its own local variable x (with a value of 2), when the function
closure myProduct() is called, it retains the variable x (with a value of 40) defined in function
foo(). The bar() function therefore returns the value 160 instead of 8.
function foo():Function
{
var x:int = 40;
function rectArea(y:int):int // function closure defined
{
return x * y
}
return rectArea;
}
function bar():void
{
var x:int = 2;
var y:int = 4;
var myProduct:Function = foo();
trace(myProduct(4)); // function closure called
}
bar(); // 160
Methods behave similarly in that they also retain information about the lexical environment
in which they were created. This characteristic is most noticeable when a method is extracted
from its instance, which creates a bound method. The main difference between a function
closure and a bound method is that the value of the this keyword in a bound method always
refers to the instance to which it was originally attached, whereas in a function closure the
value of the this keyword can change. For more information, see “Bound methods”
on page 154.
137
4
CHAPTER 4
Object-oriented
programming in ActionScript
This chapter describes the elements of ActionScript that support object-oriented
programming (OOP). The chapter does not describe general OOP principles such as object
design, abstraction, encapsulation, inheritance, and polymorphism. The chapter focuses on
how to apply these principles using ActionScript 3.0.
Because of ActionScript’s roots as a scripting language, ActionScript 3.0 OOP support is
optional. This affords programmers flexibility in choosing the best approach for projects of
varying scope and complexity. For small tasks, you may find that using ActionScript with a
procedural programming paradigm is all you need. For larger projects, applying OOP
principles can make your code easier to understand, maintain, and extend.
Contents
Basics of object-oriented programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
Example: GeometricShapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
138 Object-oriented programming in ActionScript
Basics of object-oriented programming
Introduction to object-oriented programming
Object-oriented programming (OOP) is a way of organizing the code in a program by
grouping it into objects—individual elements that include information (data values) and
functionality. Using an object-oriented approach to organizing a program allows you to group
particular pieces of information (for example, music information like album title, track title,
or artist name) together with common functionality or actions associated with that
information (such as “add track to playlist” or “play all songs by this artist”). These items are
combined into a single item, an object (for example, an “Album” or “MusicTrack”). Being able
to bundle these values and functions together provides several benefits, including only
needing to keep track of a single variable rather than multiple ones, organizing related
functionality together, and being able to structure programs in ways that more closely match
the real world.
Common object-oriented programming tasks
In practice, object-oriented programming has two parts. One part is the strategies and
techniques for designing a program (often called object-oriented design). This is a broad subject
and is not discussed in this chapter. The other part of OOP is the actual programming
structures that are available in a given programming language to build a program using an
object-oriented approach. This chapter covers the following common tasks in OOP:
■ Defining classes
■ Creating properties, methods, and get and set accessors (accessor methods)
■ Controlling access to classes, properties, methods, and accessors
■ Creating static properties and methods
■ Creating enumeration-like structures
■ Defining and using interfaces
■ Working with inheritance, including overriding class elements
Basics of object-oriented programming 139
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Attribute: A characteristic assigned to a class element (such as a property or method) in
the class definition. Attributes are commonly used to define whether the property or
method will be available for access by code in other parts of the program. For example,
private and public are attributes. A private method can be called only by code within
the class, while a public method can be called by any code in the program.
■ Class: The definition of the structure and behavior of objects of a certain type (like a
template or blueprint for objects of that data type).
■ Class hierarchy: The structure of multiple related classes, specifying which classes inherit
functionality from other classes.
■ Constructor: A special method you can define in a class, which is called when an instance
of the class is created. A constructor is commonly used to specify default values or
otherwise perform setup operations for the object.
■ Data type: The type of information that a particular variable can store. In general, data
type means the same thing as class.
■ Dot operator: The period sign (.), which in ActionScript (and many other programming
languages) is used to indicate that a name refers to a child element of an object (such as a
property or method). For instance, in the expression myObject.myProperty, the dot
operator indicates that the term myProperty is referring to some value that is an element
of the object named myObject.
■ Enumeration: A set of related constant values, grouped together for convenience as
properties of a single class.
■ Inheritance: The OOP mechanism that allows one class definition to include all the
functionality of a different class definition (and generally add to that functionality).
■ Instance: An actual object created in a program.
■ Namespace: Essentially a custom attribute, allowing more refined control over which code
can access other code.
140 Object-oriented programming in ActionScript
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because the code listings in this chapter deal primarily with defining and
manipulating data types, testing the examples will involve creating an instance of the class
being defined, manipulating that instance using its properties or methods, and then viewing
the values of the that instance’s properties. For viewing those values, you’ll want to write
values into a text field instance on the Stage, or use the trace() function to print values to
the Output panel. These techniques are described in detail in “Testing in-chapter example
code listings” on page 63.
Classes
A class is an abstract representation of an object. A class stores information about the types of
data that an object can hold and the behaviors that an object can exhibit. The usefulness of
such an abstraction may not be apparent when you write small scripts that contain only a few
objects interacting with one another. As the scope of a program grows, however, and the
number of objects that must be managed increases, you may find that classes allow you to
better control how objects are created and how they interact with one another.
As far back as ActionScript 1.0, ActionScript programmers could use Function objects to
create constructs that resembled classes. ActionScript 2.0 added formal support for classes
with keywords such as class and extends. ActionScript 3.0 not only continues to support
the keywords introduced in ActionScript 2.0, but also adds some new capabilities, such as
enhanced access control with the protected and internal attributes, and better control over
inheritance with the final and override keywords.
If you have ever created classes in programming languages like Java, C++, or C#, you will find
that ActionScript provides a familiar experience. ActionScript shares many of the same
keywords and attribute names, such as class, extends, and public, all of which are
discussed in the following sections.
NOTE
In this chapter, the term property means any member of an object or class, including
variables, constants, and methods. In addition, although the terms class and static are
often used interchangeably, in this chapter these terms are distinct. For example, in this
chapter the phrase class properties refers to all the members of a class, rather than only
the static members.
Classes 141
Class definitions
ActionScript 3.0 class definitions use syntax that is similar to that used in ActionScript 2.0
class definitions. Proper syntax for a class definition calls for the class keyword followed by
the class name. The class body, which is enclosed by curly braces ({}), follows the class name.
For example, the following code creates a class named Shape that contains one variable,
named visible:
public class Shape
{
var visible:Boolean = true;
}
One significant syntax change involves class definitions that are inside a package. In
ActionScript 2.0, if a class is inside a package, the package name must be included in the class
declaration. In ActionScript 3.0, which introduces the package statement, the package name
must be included in the package declaration instead of in the class declaration. For example,
the following class declarations show how the BitmapData class, which is part of the
flash.display package, is defined in ActionScript 2.0 and ActionScript 3.0:
// ActionScript 2.0
class flash.display.BitmapData {}
// ActionScript 3.0
package flash.display
{
public class BitmapData {}
}
Class attributes
ActionScript 3.0 allows you to modify class definitions using one of the following four
attributes:
Attribute Definition
dynamic Allow properties to be added to instances at run time.
final Must not be extended by another class.
internal (default) Visible to references inside the current package.
public Visible to references everywhere.
142 Object-oriented programming in ActionScript
For each of these attributes, except for internal, you must explicitly include the attribute to
get the associated behavior. For example, if you do not include the dynamic attribute when
defining a class, you will not be able to add properties to a class instance at run time. You
explicitly assign an attribute by placing it at the beginning of the class definition, as the
following code demonstrates:
dynamic class Shape {}
Notice that the list does not include an attribute named abstract. This is because abstract
classes are not supported in ActionScript 3.0. Notice also that the list does not include
attributes named private and protected. These attributes have meaning only inside a class
definition, and cannot be applied to classes themselves. If you do not want a class to be
publicly visible outside a package, place the class inside a package and mark the class with the
internal attribute. Alternatively, you can omit both the internal and public attributes,
and the compiler will automatically add the internal attribute for you. If you do not want a
class to be visible outside the source file in which it is defined, place the class at the bottom of
your source file, below the closing curly brace of the package definition.
Classes 143
Class body
The class body, which is enclosed by curly braces, is used to define the variables, constants,
and methods of your class. The following example shows the declaration for the Accessibility
class in the Adobe Flash Player API:
public final class Accessibility
{
public static function get active():Boolean;
public static function updateProperties():void;
}
You can also define a namespace inside a class body. The following example shows how a
namespace can be defined within a class body and used as an attribute of a method in that
class:
public class SampleClass
{
public namespace sampleNamespace;
sampleNamespace function doSomething():void;
}
ActionScript 3.0 allows you to include not only definitions in a class body, but also
statements. Statements that are inside a class body, but outside a method definition, are
executed exactly once—when the class definition is first encountered and the associated class
object is created. The following example includes a call to an external function, hello(), and
a trace statement that outputs a confirmation message when the class is defined:
function hello():String
{
trace("hola");
}
class SampleClass
{
hello();
trace("class created");
}
// output when class is created
hola
class created
In contrast to previous versions of ActionScript, in ActionScript 3.0 it is permissible to define
a static property and an instance property with the same name in the same class body. For
example, the following code declares a static variable named message and an instance variable
of the same name:
class StaticTest
{
static var message:String = "static variable";
var message:String = "instance variable";
144 Object-oriented programming in ActionScript
}
// In your script
var myST:StaticTest = new StaticTest();
trace(StaticTest.message); // output: static variable
trace(myST.message); // output: instance variable
Class property attributes
In discussions of the ActionScript object model, the term property means anything that can be
a member of a class, including variables, constants, and methods. This differs from the way
the term is used in the ActionScript 3.0 Language and Components Reference, where the term is
used more narrowly and includes only class members that are variables or are defined by a
getter or setter method. In ActionScript 3.0, there is a set of attributes that can be used with
any property of a class. The following table lists this set of attributes.
Access control namespace attributes
ActionScript 3.0 provides four special attributes that control access to properties defined
inside a class: public, private, protected, and internal.
The public attribute makes a property visible anywhere in your script. For example, to make
a method available to code outside its package, you must declare the method with the public
attribute. This is true for any property, whether it is declared using the var, const, or
function keywords.
The private attribute makes a property visible only to callers within the property’s defining
class. This behavior differs from that of the private attribute in ActionScript 2.0, which
allowed a subclass to access a private property in a superclass. Another significant change in
behavior has to do with run-time access. In ActionScript 2.0, the private keyword
prohibited access only at compile time and was easily circumvented at run time. In
ActionScript 3.0, this is no longer true. Properties that are marked as private are unavailable
at both compile time and run time.
Attribute Definition
internal (default) Visible to references inside the same package.
private Visible to references in the same class.
protected Visible to references in the same class and derived classes.
public Visible to references everywhere.
static Specifies that a property belongs to the class, as opposed to
instances of the class.
UserDefinedNamespace Custom namespace name defined by user.
Classes 145
For example, the following code creates a simple class named PrivateExample with one private
variable, and then attempts to access the private variable from outside the class. In
ActionScript 2.0, compile-time access was prohibited, but the prohibition was easily
circumvented by using the property access operator ([]), which does the property lookup at
run time rather than at compile time.
class PrivateExample
{
private var privVar:String = "private variable";
}
var myExample:PrivateExample = new PrivateExample();
trace(myExample.privVar); // compile-time error in strict mode
trace(myExample["privVar"]); // ActionScript 2.0 allows access, but in
ActionScript 3.0, this is a run-time error.
In ActionScript 3.0, an attempt to access a private property using the dot operator
(myExample.privVar) results in a compile-time error if you are using strict mode. Otherwise,
the error is reported at run time, just as it is when you use the property access operator
(myExample["privVar"]).
The following table summarizes the results of attempting to access a private property that
belongs to a sealed (not dynamic) class:
In classes declared with the dynamic attribute, attempts to access a private variable will not
result in a run-time error. Instead, the variable is simply not visible, so Flash Player returns the
value undefined. A compile-time error occurs, however, if you use the dot operator in strict
mode. The following example is the same as the previous example, except that the
PrivateExample class is declared as a dynamic class:
dynamic class PrivateExample
{
private var privVar:String = "private variable";
}
var myExample:PrivateExample = new PrivateExample();
trace(myExample.privVar); // compile-time error in strict mode
trace(myExample["privVar"]); // output: undefined
Strict mode Standard mode
dot operator (.) compile-time error run-time error
bracket operator ([]) run-time error run-time error
146 Object-oriented programming in ActionScript
Dynamic classes generally return the value undefined instead of generating an error when
code external to a class attempts to access a private property. The following table shows that
an error is generated only when the dot operator is used to access a private property in strict
mode:
The protected attribute, which is new for ActionScript 3.0, makes a property visible to
callers within its own class or in a subclass. In other words, a protected property is available
within its own class or to classes that lie anywhere below it in the inheritance hierarchy. This is
true whether the subclass is in the same package or in a different package.
For those familiar with ActionScript 2.0, this functionality is similar to the private attribute
in ActionScript 2.0. The ActionScript 3.0 protected attribute is also similar to the
protected attribute in Java, but differs in that the Java version also permits access to callers
within the same package. The protected attribute is useful when you have a variable or
method that your subclasses need but that you want to hide from code that is outside the
inheritance chain.
The internal attribute, which is new for ActionScript 3.0, makes a property visible to callers
within its own package. This is the default attribute for code inside a package, and it applies to
any property that does not have any of the following attributes:
■ public
■ private
■ protected
■ a user-defined namespace
The internal attribute is similar to the default access control in Java, although in Java there
is no explicit name for this level of access, and it can be achieved only through the omission of
any other access modifier. The internal attribute is available in ActionScript 3.0 to give you
the option of explicitly signifying your intent to make a property visible only to callers within
its own package.
static attribute
The static attribute, which can be used with properties declared with the var, const, or
function keywords, allows you to attach a property to the class rather than to instances of the
class. Code external to the class must call static properties by using the class name instead of
an instance name.
Strict mode Standard mode
dot operator (.) compile-time error undefined
bracket operator ([]) undefined undefined
Classes 147
Static properties are not inherited by subclasses, but the properties are part of a subclass’s
scope chain. This means that within the body of a subclass, a static variable or method can be
used without referencing the class in which it was defined. For more information, see “Static
properties not inherited” on page 169.
User-defined namespace attributes
As an alternative to the predefined access control attributes, you can create a custom
namespace for use as an attribute. Only one namespace attribute can be used per definition,
and you cannot use a namespace attribute in combination with any of the access control
attributes (public, private, protected, internal). For more information about using
namespaces, see “Namespaces” on page 75.
Variables
Variables can be declared with either the var or const keywords. Variables declared with the
var keyword can have their values changed multiple times throughout the execution of a
script. Variables declared with the const keyword are called constants, and can have values
assigned to them only once. An attempt to assign a new value to an initialized constant results
in an error. For more information, see “Constants” on page 108.
Static variables
Static variables are declared using a combination of the static keyword and either the var or
const statement. Static variables, which are attached to a class rather than an instance of a
class, are useful for storing and sharing information that applies to an entire class of objects.
For example, a static variable is appropriate if you want to keep a tally of the number of times
a class is instantiated or if you want to store the maximum number of class instances that are
allowed.
The following example creates a totalCount variable to track the number of class
instantiations and a MAX_NUM constant to store the maximum number of instantiations. The
totalCount and MAX_NUM variables are static, because they contain values that apply to the
class as a whole rather than to a particular instance.
class StaticVars
{
public static var totalCount:int = 0;
public static const MAX_NUM:uint = 16;
}
148 Object-oriented programming in ActionScript
Code that is external to the StaticVars class and any of its subclasses can reference the
totalCount and MAX_NUM properties only through the class itself. For example, the following
code works:
trace(StaticVars.totalCount); // output: 0
trace(StaticVars.MAX_NUM); // output: 16
You cannot access static variables through an instance of the class, so the following code
returns errors:
var myStaticVars:StaticVars = new StaticVars();
trace(myStaticVars.totalCount); // error
trace(myStaticVars.MAX_NUM); // error
Variables that are declared with both the static and const keywords must be initialized at
the same time as you declare the constant, as the StaticVars class does for MAX_NUM. You
cannot assign a value to MAX_NUM inside the constructor or an instance method. The following
code will generate an error, because it is not a valid way to initialize a static constant:
// !! Error to initialize static constant this way
class StaticVars2
{
public static const UNIQUESORT:uint;
function initializeStatic():void
{
UNIQUESORT = 16;
}
}
Instance variables
Instance variables include properties declared with the var and const keywords, but without
the static keyword. Instance variables, which are attached to class instances rather than to an
entire class, are useful for storing values that are specific to an instance. For example, the Array
class has an instance property named length, which stores the number of array elements that
a particular instance of the Array class holds.
Instance variables, whether declared as var or const, cannot be overridden in a subclass. You
can, however, achieve functionality that is similar to overriding variables by overriding getter
and setter methods. For more information, see “Get and set accessor methods” on page 153.
Methods
Methods are functions that are part of a class definition. Once an instance of the class is
created, a method is bound to that instance. Unlike a function declared outside a class, a
method cannot be used apart from the instance to which it is attached.
Classes 149
Methods are defined using the function keyword. You can use a function statement such as
the following:
public function sampleFunction():String {}
Or you can use a variable to which you assign a function expression, as follows:
public var sampleFunction:Function = function () {}
In most cases you will want to use a function statement instead of a function expression for
the following reasons:
■ Function statements are more concise and easier to read.
■ Function statements allow you to use the override and final keywords. For more
information, see “Overriding methods” on page 167.
■ Function statements create a stronger bond between the identifier—that is, the name of
the function—and the code within the method body. Because the value of a variable can
be changed with an assignment statement, the connection between a variable and its
function expression can be severed at any time. Although you can work around this issue
by declaring the variable with const instead of var, such a technique is not considered a
best practice, because it makes the code hard to read and prevents the use of the override
and final keywords.
One case in which you must use a function expression is when you choose to attach a function
to the prototype object. For more information, see “The prototype object” on page 177.
Constructor methods
Constructor methods, sometimes simply called constructors, are functions that share the same
name as the class in which they are defined. Any code that you include in a constructor
method is executed whenever an instance of the class is created with the new keyword. For
example, the following code defines a simple class named Example that contains a single
property named status. The initial value of the status variable is set inside the constructor
function.
class Example
{
public var status:String;
public function Example()
{
status = "initialized";
}
}
var myExample:Example = new Example();
trace(myExample.status); // output: initialized
150 Object-oriented programming in ActionScript
Constructor methods can only be public, but the use of the public attribute is optional. You
cannot use any of the other access control specifiers, including private, protected, or
internal, on a constructor. You also cannot use a user-defined namespace with a constructor
method.
A constructor can make an explicit call to the constructor of its direct superclass by using the
super() statement. If the superclass constructor is not explicitly called, the compiler
automatically inserts a call before the first statement in the constructor body. You can also call
methods of the superclass by using the super prefix as a reference to the superclass. If you
decide to use both super() and super in the same constructor body, be sure to call super()
first. Otherwise, the super reference will not behave as expected. The super() constructor
should also be called before any throw or return statement.
The following example demonstrates what happens if you attempt to use the super reference
before calling the super() constructor. A new class, ExampleEx, extends the Example class.
The ExampleEx constructor attempts to access the status variable defined in its superclass, but
does so before calling super(). The trace() statement inside the ExampleEx constructor
produces the value null, because the status variable is not available until the super()
constructor executes.
class ExampleEx extends Example
{
public function ExampleEx()
{
trace(super.status);
super();
}
}
var mySample:ExampleEx = new ExampleEx(); // output: null
Although it is legal to use the return statement inside a constructor, it is not permissible to
return a value. In other words, return statements must not have associated expressions or
values. Accordingly, constructor methods are not allowed to return values, which means that
no return type may be specified.
If you do not define a constructor method in your class, the compiler will automatically create
an empty constructor for you. If your class extends another class, the compiler will include a
super() call in the constructor it generates.
Classes 151
Static methods
Static methods, also called class methods, are methods that are declared with the static
keyword. Static methods, which are attached to a class rather than to an instance of a class, are
useful for encapsulating functionality that affects something other than the state of an
individual instance. Because static methods are attached to a class as a whole, static methods
can be accessed only through a class and not through an instance of the class.
Static methods are useful for encapsulating functionality that is not limited to affecting the
state of class instances. In other words, a method should be static if it provides functionality
that does not directly affect the value of a class instance. For example, the Date class has a
static method named parse(), which takes a string and converts it to a number. The method
is static because it does not affect an individual instance of the class. Instead, the parse()
method takes a string that represents a date value, parses the string, and returns a number in a
format compatible with the internal representation of a Date object. This method is not an
instance method, because it does not make sense to apply the method to an instance of the
Date class.
Contrast the static parse() method with one of the instance methods of the Date class, such
as getMonth(). The getMonth() method is an instance method, because it operates directly
on the value of an instance by retrieving a specific component, the month, of a Date instance.
Because static methods are not bound to individual instances, you cannot use the keywords
this or super within the body of a static method. Both the this reference and the super
reference have meaning only within the context of an instance method.
In contrast with some other class-based programming languages, static methods in
ActionScript 3.0 are not inherited. For more information, see “Static properties not inherited”
on page 169.
Instance methods
Instance methods are methods that are declared without the static keyword. Instance
methods, which are attached to instances of a class instead of the class as a whole, are useful
for implementing functionality that affects individual instances of a class. For example, the
Array class contains an instance method named sort(), which operates directly on Array
instances.
152 Object-oriented programming in ActionScript
Within the body of an instance method, both static and instance variables are in scope, which
means that variables defined in the same class can be referenced using a simple identifier. For
example, the following class, CustomArray, extends the Array class. The CustomArray class
defines a static variable named arrayCountTotal to track the total number of class instances,
an instance variable named arrayNumber that tracks the order in which the instances were
created, and an instance method named getPosition() that returns the values of these
variables.
public class CustomArray extends Array
{
public static var arrayCountTotal:int = 0;
public var arrayNumber:int;
public function CustomArray()
{
arrayNumber = ++arrayCountTotal;
}
public function getArrayPosition():String
{
return ("Array " + arrayNumber + " of " + arrayCountTotal);
}
}
Although code external to the class must refer to the arrayCountTotal static variable
through the class object using CustomArray.arrayCountTotal, code that resides inside the
body of the getPosition() method can refer directly to the static arrayCountTotal
variable. This is true even for static variables in superclasses. Though static properties are not
inherited in ActionScript 3.0, static properties in superclasses are in scope. For example, the
Array class has a few static variables, one of which is a constant named DESCENDING. Code
that resides in an Array subclass can refer to the static constant DESCENDING using a simple
identifier:
public class CustomArray extends Array
{
public function testStatic():void
{
trace(DESCENDING); // output: 2
}
}
The value of the this reference within the body of an instance method is a reference to the
instance to which the method is attached. The following code demonstrates that the this
reference points to the instance that contains the method:
class ThisTest
{
Classes 153
function thisValue():ThisTest
{
return this;
}
}
var myTest:ThisTest = new ThisTest();
trace(myTest.thisValue() == myTest); // output: true
Inheritance of instance methods can be controlled with the keywords override and final.
You can use the override attribute to redefine an inherited method, and the final attribute
to prevent subclasses from overriding a method. For more information, see “Overriding
methods” on page 167.
Get and set accessor methods
Get and set accessor functions, also called getters and setters, allow you to adhere to the
programming principles of information hiding and encapsulation while providing an easy-to-
use programming interface for the classes that you create. Get and set functions allow you to
keep your class properties private to the class, but allow users of your class to access those
properties as if they were accessing a class variable instead of calling a class method.
The advantage of this approach is that it allows you to avoid the traditional accessor functions
with unwieldy names, such as getPropertyName() and setPropertyName(). Another
advantage of getters and setters is that you can avoid having two public-facing functions for
each property that allows both read and write access.
The following example class, named GetSet, includes get and set accessor functions named
publicAccess() that provide access to the private variable named privateProperty:
class GetSet
{
private var privateProperty:String;
public function get publicAccess():String
{
return privateProperty;
}
public function set publicAccess(setValue:String):void
{
privateProperty = setValue;
}
}
154 Object-oriented programming in ActionScript
If you attempt to access the property privateProperty directly, an error will result, as
follows:
var myGetSet:GetSet = new GetSet();
trace(myGetSet.privateProperty); // error occurs
Instead, a user of the GetSet class will use something that appears to be a property named
publicAccess, but that is really a pair of get and set accessor functions that operate on the
private property named privateProperty. The following example instantiates the GetSet
class, and then sets the value of the privateProperty using the public accessor named
publicAccess:
var myGetSet:GetSet = new GetSet();
trace(myGetSet.publicAccess); // output: null
myGetSet.publicAccess = "hello";
trace(myGetSet.publicAccess); // output: hello
Getter and setter functions also make it possible to override properties that are inherited from
a superclass, something that is not possible when you use regular class member variables. Class
member variables that are declared using the var keyword cannot be overridden in a subclass.
Properties that are created using getter and setter functions, however, do not have this
restriction. You can use the override attribute on getter and setter functions that are
inherited from a superclass.
Bound methods
A bound method, sometimes called a method closure, is simply a method that is extracted from
its instance. Examples of bound methods include methods that are passed as arguments to a
function or returned as values from a function. New in ActionScript 3.0, a bound method is
similar to a function closure in that it retains its lexical environment even when extracted
from its instance. The key difference, however, between a bound method and a function
closure is that the this reference for a bound method remains linked, or bound, to the
instance that implements the method. In other words, the this reference in a bound method
always points to the original object that implemented the method. For function closures, the
this reference is generic, which means that it points to whatever object the function is
associated with at the time it is invoked.
Classes 155
Understanding bound methods is important if you use the this keyword. Recall that the
this keyword provides a reference to a method’s parent object. Most ActionScript
programmers expect that the this keyword always refers to the object or class that contains
the definition of a method. Without method binding, however, this would not always be true.
In previous versions of ActionScript, for example, the this reference did not always refer to
the instance that implemented the method. When methods are extracted from an instance in
ActionScript 2.0, not only is the this reference not bound to the original instance, but also
the member variables and methods of the instance’s class are not available. This is not a
problem in ActionScript 3.0, because bound methods are automatically created when you
pass a method as a parameter. Bound methods ensure that the this keyword always references
the object or class in which a method is defined.
The following code defines a class named ThisTest, which contains a method named foo()
that defines the bound method, and a method named bar() that returns the bound method.
Code external to the class creates an instance of the ThisTest class, calls the bar() method,
and stores the return value in a variable named myFunc.
class ThisTest
{
private var num:Number = 3;
function foo():void // bound method defined
{
trace("foo's this: " + this);
trace("num: " + num);
}
function bar():Function
{
return foo; // bound method returned
}
}
var myTest:ThisTest = new ThisTest();
var myFunc:Function = myTest.bar();
trace(this); // output: [object global]
myFunc();
/* output:
foo's this: [object ThisTest]
output: num: 3 */
The last two lines of code show that the this reference in the bound method foo() still
points to an instance of ThisTest class, even though the this reference in the line just before
it points to the global object. Moreover, the bound method stored in the myFunc variable still
has access to the member variables of the ThisTest class. If this same code is run in
ActionScript 2.0, the this references would match, and the num variable would be
undefined.
156 Object-oriented programming in ActionScript
One area where the addition of bound methods is most noticeable is with event handlers,
because the addEventListener() method requires that you pass a function or method as an
argument. For more information, see “Listener function defined as a class method”
on page 329.
Enumerations with classes
Enumerations are custom data types that you create to encapsulate a small set of values.
ActionScript 3.0 does not support a specific enumeration facility, unlike C++ with its enum
keyword or Java with its Enumeration interface. You can, however, create enumerations using
classes and static constants. For example, the PrintJob class in the Flash Player API uses an
enumeration named PrintJobOrientation to store the set of values comprising "landscape"
and "portrait", as shown in the following code:
public final class PrintJobOrientation
{
public static const LANDSCAPE:String = "landscape";
public static const PORTRAIT:String = "portrait";
}
By convention, an enumeration class is declared with the final attribute, because there is no
need to extend the class. The class comprises only static members, which means that you do
not create instances of the class. Instead, you access the enumeration values directly through
the class object, as shown in the following code excerpt:
var pj:PrintJob = new PrintJob();
if(pj.start())
{
if (pj.orientation == PrintJobOrientation.PORTRAIT)
{
...
}
...
}
All of the enumeration classes in the Flash Player API contain only variables of type String,
int, or uint. The advantage of using enumerations instead of literal string or number values is
that typographical mistakes are easier to find with enumerations. If you mistype the name of
an enumeration, the ActionScript compiler generates an error. If you use literal values, the
compiler does not complain if you spell a word incorrectly or use the wrong number. In the
previous example, the compiler generates an error if the name of the enumeration constant is
incorrect, as the following excerpt shows:
if (pj.orientation == PrintJobOrientation.PORTRAI) // compiler error
Classes 157
However, the compiler does not generate an error if you misspell a string literal value, as
follows:
if (pj.orientation == "portrai") // no compiler error
A second technique for creating enumerations also involves creating a separate class with static
properties for the enumeration. This technique differs, however, in that each of the static
properties contains an instance of the class instead of a string or integer value. For example,
the following code creates an enumeration class for the days of the week:
public final class Day
{
public static const MONDAY:Day = new Day();
public static const TUESDAY:Day = new Day();
public static const WEDNESDAY:Day = new Day();
public static const THURSDAY:Day = new Day();
public static const FRIDAY:Day = new Day();
public static const SATURDAY:Day = new Day();
public static const SUNDAY:Day = new Day();
}
This technique is not used by the Flash Player API but is used by many developers who prefer
the improved type checking that the technique provides. For example, a method that returns
an enumeration value can restrict the return value to the enumeration data type. The
following code shows not only a function that returns a day of the week, but also a function
call that uses the enumeration type as a type annotation:
function getDay():Day
{
var date:Date = new Date();
var retDay:Day;
switch (date.day)
{
case 0:
retDay = Day.MONDAY;
break;
case 1:
retDay = Day.TUESDAY;
break;
case 2:
retDay = Day.WEDNESDAY;
break;
case 3:
retDay = Day.THURSDAY;
break;
case 4:
retDay = Day.FRIDAY;
break;
case 5:
retDay = Day.SATURDAY;
158 Object-oriented programming in ActionScript
break;
case 6:
retDay = Day.SUNDAY;
break;
}
return retDay;
}
var dayOfWeek:Day = getDay();
You can also enhance the Day class so that it associates an integer with each day of the week,
and provides a toString() method that returns a string representation of the day. You might
want to enhance the Day class in this manner as an exercise.
Embedded asset classes
ActionScript 3.0 uses special classes, called embedded asset classes, to represent embedded
assets. An embedded asset is an asset, such as a sound, image, or font, that is included in a SWF
file at compile time. Embedding an asset instead of loading it dynamically ensures that it will
be available at run time, but at the cost of increased SWF file size.
Using embedded asset classes in Flash
To embed an asset, first place the asset into a FLA file’s library. Next, use the asset’s linkage
property to provide a name for the asset’s embedded asset class. If a class by that name cannot
be found in the classpath, a class is automatically generated for you. You can then create an
instance of the embedded asset class and use any properties and methods defined or inherited
by that class. For example, the following code can be used to play an embedded sound that is
linked to an embedded asset class named PianoMusic:
var piano:PianoMusic = new PianoMusic();
var sndChannel:SoundChannel = piano.play();
Interfaces
An interface is a collection of method declarations that allows unrelated objects to
communicate with one another. For example, the Flash Player API defines the
IEventDispatcher interface, which contains method declarations that a class can use to handle
event objects. The IEventDispatcher interface establishes a standard way for objects to pass
event objects to one another. The following code shows the definition of the
IEventDispatcher interface:
Interfaces 159
public interface IEventDispatcher
{
function addEventListener(type:String, listener:Function,
useCapture:Boolean=false, priority:int=0,
useWeakReference:Boolean = false):void;
function removeEventListener(type:String, listener:Function,
useCapture:Boolean=false):void;
function dispatchEvent(event:Event):Boolean;
function hasEventListener(type:String):Boolean;
function willTrigger(type:String):Boolean;
}
Interfaces are based on the distinction between a method’s interface and its implementation. A
method’s interface includes all the information necessary to invoke that method, including
the name of the method, all of its parameters, and its return type. A method’s implementation
includes not only the interface information, but also the executable statements that carry out
the method’s behavior. An interface definition contains only method interfaces, and any class
that implements the interface is responsible for defining the method implementations.
In the Flash Player API, the EventDispatcher class implements the IEventDispatcher interface
by defining all of the IEventDispatcher interface methods and adding method bodies to each
of the methods. The following code is an excerpt from the EventDispatcher class definition:
public class EventDispatcher implements IEventDispatcher
{
function dispatchEvent(event:Event):Boolean
{
/* implementation statements */
}
...
}
The IEventDispatcher interface serves as a protocol that EventDispatcher instances use to
process event objects and pass them to other objects that have also implemented the
IEventDispatcher interface.
Another way to describe an interface is to say that it defines a data type just as a class does.
Accordingly, an interface can be used as a type annotation, just as a class can. As a data type,
an interface can also be used with operators, such as the is and as operators, that require a
data type. Unlike a class, however, an interface cannot be instantiated. This distinction has led
many programmers to think of interfaces as abstract data types and classes as concrete data
types.
160 Object-oriented programming in ActionScript
Defining an interface
The structure of an interface definition is similar to that of a class definition, except that an
interface can contain only methods with no method bodies. Interfaces cannot include
variables or constants but can include getters and setters. To define an interface, use the
interface keyword. For example, the following interface, IExternalizable, is part of the
flash.utils package in the Flash Player API. The IExternalizable interface defines a protocol for
serializing an object, which means converting an object into a format suitable for storage on a
device or for transport across a network.
public interface IExternalizable
{
function writeExternal(output:IDataOutput):void;
function readExternal(input:IDataInput):void;
}
Note that the IExternalizable interface is declared with the public access control modifier.
Interface definitions may only be modified by the public and internal access control
specifiers. The method declarations inside an interface definition cannot have any access
control specifiers.
The Flash Player API follows a convention in which interface names begin with an uppercase
I, but you can use any legal identifier as an interface name. Interface definitions are often
placed at the top level of a package. Interface definitions cannot be placed inside a class
definition or inside another interface definition.
Interfaces can extend one or more other interfaces. For example, the following interface,
IExample, extends the IExternalizable interface:
public interface IExample extends IExternalizable
{
function extra():void;
}
Any class that implements the IExample interface must include implementations not only for
the extra() method, but also for the writeExternal() and readExternal() methods
inherited from the IExternalizable interface.
Interfaces 161
Implementing an interface in a class
A class is the only ActionScript 3.0 language element that can implement an interface. Use the
implements keyword in a class declaration to implement one or more interfaces. The
following example defines two interfaces, IAlpha and IBeta, and a class, Alpha, that
implements them both:
interface IAlpha
{
function foo(str:String):String;
}
interface IBeta
{
function bar():void;
}
class Alpha implements IAlpha, IBeta
{
public function foo(param:String):String {}
public function bar():void {}
}
In a class that implements an interface, implemented methods must do the following:
■ Use the public access control identifier.
■ Use the same name as the interface method.
■ Have the same number of parameters, each with data types that match the interface
method parameter data types.
■ Use the same return type.
You do have some flexibility, however, in how you name the parameters of methods that you
implement. Although the number of parameters and the data type of each parameter in the
implemented method must match that of the interface method, the parameter names do not
need to match. For example, in the previous example the parameter of the Alpha.foo()
method is named param:
public function foo(param:String):String {}
But the parameter is named str in the IAlpha.foo() interface method:
function foo(str:String):String;
162 Object-oriented programming in ActionScript
You also have some flexibility with default parameter values. An interface definition can
include function declarations with default parameter values. A method that implements such
a function declaration must have a default parameter value that is a member of the same data
type as the value specified in the interface definition, but the actual value does not have to
match. For example, the following code defines an interface that contains a method with a
default parameter value of 3:
interface IGamma
{
function doSomething(param:int = 3):void;
}
The following class definition implements the Igamma interface but uses a different default
parameter value:
class Gamma implements IGamma
{
public function doSomething(param:int = 4):void {}
}
The reason for this flexibility is that the rules for implementing an interface are designed
specifically to ensure data type compatibility, and requiring identical parameter names and
default parameter values is not necessary to achieve that objective.
Inheritance
Inheritance is a form of code reuse that allows programmers to develop new classes that are
based on existing classes. The existing classes are often referred to as base classes or superclasses,
while the new classes are usually called subclasses. A key advantage of inheritance is that it
allows you to reuse code from a base class yet leave the existing code unmodified. Moreover,
inheritance requires no changes to the way that other classes interact with the base class.
Rather than modifying an existing class that may have been thoroughly tested or may already
be in use, using inheritance you can treat that class as an integrated module that you can
extend with additional properties or methods. Accordingly, you use the extends keyword to
indicate that a class inherits from another class.
Inheritance 163
Inheritance also allows you to take advantage of polymorphism in your code. Polymorphism is
the ability to use a single method name for a method that behaves differently when applied to
different data types. A simple example is a base class named Shape with two subclasses named
Circle and Square. The Shape class defines a method named area(), which returns the area of
the shape. If polymorphism is implemented, you can call the area() method on objects of
type Circle and Square and have the correct calculations done for you. Inheritance enables
polymorphism by allowing subclasses to inherit and redefine, or override, methods from the
base class. In the following example, the area() method is redefined by the Circle and Square
classes:
class Shape
{
public function area():Number
{
return NaN;
}
}
class Circle extends Shape
{
private var radius:Number = 1;
override public function area():Number
{
return (Math.PI * (radius * radius));
}
}
class Square extends Shape
{
private var side:Number = 1;
override public function area():Number
{
return (side * side);
}
}
var cir:Circle = new Circle();
trace(cir.area()); // output: 3.141592653589793
var sq:Square = new Square();
trace(sq.area()); // output: 1
Because each class defines a data type, the use of inheritance creates a special relationship
between a base class and a class that extends it. A subclass is guaranteed to possess all the
properties of its base class, which means that an instance of a subclass can always be
substituted for an instance of the base class. For example, if a method defines a parameter of
type Shape, it is legal to pass an argument of type Circle because Circle extends Shape, as in
the following:
164 Object-oriented programming in ActionScript
function draw(shapeToDraw:Shape) {}
var myCircle:Circle = new Circle();
draw(myCircle);
Instance properties and inheritance
An instance property, whether defined with the function, var, or const keywords, is
inherited by all subclasses as long as the property is not declared with the private attribute in
the base class. For example, the Event class in the Flash Player API has a number of subclasses
that inherit properties common to all event objects.
For some types of events, the Event class contains all the properties necessary to define the
event. These types of events do not require instance properties beyond those defined in the
Event class. Examples of such events are the complete event, which occurs when data has
loaded successfully, and the connect event, which occurs when a network connection has
been established.
The following example is an excerpt from the Event class that shows some of the properties
and methods that are inherited by subclasses. Because the properties are inherited, an instance
of any subclass can access these properties.
public class Event
{
public function get type():String;
public function get bubbles():Boolean;
...
public function stopPropagation():void {}
public function stopImmediatePropagation():void {}
public function preventDefault():void {}
public function isDefaultPrevented():Boolean {}
...
}
Other types of events require unique properties not available in the Event class. These events
are defined using subclasses of the Event class so that new properties can be added to the
properties defined in the Event class. An example of such a subclass is the MouseEvent class,
which adds properties unique to events associated with mouse movement or mouse clicks,
such as the mouseMove and click events. The following example is an excerpt from the
MouseEvent class that shows the definition of properties that exist on the subclass but not on
the base class:
public class MouseEvent extends Event
{
public static const CLICK:String = "click";
public static const MOUSE_MOVE:String = "mouseMove";
Inheritance 165
...
public function get stageX():Number {}
public function get stageY():Number {}
...
}
Access control specifiers and inheritance
If a property is declared with the public keyword, the property is visible to code anywhere.
This means that the public keyword, unlike the private, protected, and internal
keywords, places no restrictions on property inheritance.
If a property is declared with private keyword, it is visible only in the class that defines it,
which means that it is not inherited by any subclasses. This behavior is different from previous
versions of ActionScript, where the private keyword behaved more like the ActionScript 3.0
protected keyword.
The protected keyword indicates that a property is visible not only within the class that
defines it, but also to all subclasses. Unlike the protected keyword in the Java programming
language, the protected keyword in ActionScript 3.0 does not make a property visible to all
other classes in the same package. In ActionScript 3.0, only subclasses can access a property
declared with the protected keyword. Moreover, a protected property is visible to a subclass
whether the subclass is in the same package as the base class or in a different package.
To limit the visibility of a property to the package in which it is defined, use the internal
keyword or do not use any access control specifier. The internal access control specifier is
the default access control specifier that applies when one is not specified. A property marked
as internal will be inherited only by a subclass that resides in the same package.
You can use the following example to see how each of the access control specifiers affects
inheritance across package boundaries. The following code defines a main application class
named AccessControl and two other classes named Base and Extender. The Base class is in a
package named foo and the Extender class, which is a subclass of the Base class, is in a package
named bar. The AccessControl class imports only the Extender class and creates an instance of
Extender that attempts to access a variable named str that is defined in the Base class. The
str variable is declared as public so that the code compiles and runs as shown in the
following excerpt:
// Base.as in a folder named foo
package foo
{
public class Base
{
public var str:String = "hello"; // change public on this line
}
166 Object-oriented programming in ActionScript
}
// Extender.as in a folder named bar
package bar
{
import foo.Base;
public class Extender extends Base
{
public function getString():String {
return str;
}
}
}
// main application class in file named ProtectedExample.as
import flash.display.MovieClip;
import bar.Extender;
public class AccessControl extends MovieClip
{
public function AccessControl()
{
var myExt:Extender = new Extender();
trace(myExt.testString); // error if str is not public
trace(myExt.getString()); // error if str is private or internal
}
}
}
To see how the other access control specifiers affect compilation and execution of the
preceding example, change the str variable’s access control specifier to private, protected,
or internal after deleting or commenting out the following line from the AccessControl
class:
trace(myExt.testString); // error if str is not public
Overriding variables not permitted
Properties that are declared with the var or const keywords are inherited but cannot be
overridden. To override a property means to redefine the property in a subclass. The only type
of property that can be overridden are methods—that is, properties declared with the
function keyword. Although you cannot override an instance variable, you can achieve
similar functionality by creating getter and setter methods for the instance variable and
overriding the methods. For more information, see “Overriding getters and setters”
on page 168.
Inheritance 167
Overriding methods
To override a method means to redefine the behavior of an inherited method. Static methods
are not inherited and cannot be overridden. Instance methods, however, are inherited by
subclasses and can be overridden as long as the following two criteria are met:
■ The instance method is not declared with the final keyword in the base class. When used
with an instance method, the final keyword indicates the programmer’s intent to prevent
subclasses from overriding the method.
■ The instance method is not declared with the private access control specifier in the base
class. If a method is marked as private in the base class, there is no need to use the
override keyword when defining an identically named method in the subclass, because
the base class method will not be visible to the subclass.
To override an instance method that meets these criteria, the method definition in the
subclass must use the override keyword and must match the superclass version of the
method in the following ways:
■ The override method must have the same level of access control as the base class method.
Methods marked as internal have the same level of access control as methods that have no
access control specifier.
■ The override method must have the same number of parameters as the base class method.
■ The override method parameters must have the same data type annotations as the
parameters in the base class method.
■ The override method must have the same return type as the base class method.
The names of the parameters in the override method, however, do not have to match the
names of the parameters in the base class, as long as the number of parameters and the data
type of each parameter matches.
The super statement
When overriding a method, programmers often want to add to the behavior of the superclass
method they are overriding instead of completely replacing the behavior. This requires a
mechanism that allows a method in a subclass to call the superclass version of itself. The
super statement provides such a mechanism, in that it contains a reference to the immediate
superclass. The following example defines a class named Base that contains a method named
thanks() and a subclass of the Base class named Extender that overrides the thanks()
method. The Extender.thanks() method uses the super statement to call Base.thanks().
package {
import flash.display.MovieClip;
public class SuperExample extends MovieClip
{
168 Object-oriented programming in ActionScript
public function SuperExample()
{
var myExt:Extender = new Extender()
trace(myExt.thanks()); // output: Mahalo nui loa
}
}
}
class Base {
public function thanks():String
{
return "Mahalo";
}
}
class Extender extends Base
{
override public function thanks():String
{
return super.thanks() + " nui loa";
}
}
Overriding getters and setters
Although you cannot override variables defined in a superclass, you can override getters and
setters. For example, the following code overrides a getter named currentLabel that is
defined in the MovieClip class in the Flash Player API.:
package
{
import flash.display.MovieClip;
public class OverrideExample extends MovieClip
{
public function OverrideExample()
{
trace(currentLabel)
}
override public function get currentLabel():String
{
var str:String = "Override: ";
str += super.currentLabel;
return str;
}
}
}
Inheritance 169
The output of the trace() statement in the OverrideExample class constructor is Override:
null, which shows that the example was able to override the inherited currentLabel
property.
Static properties not inherited
Static properties are not inherited by subclasses. This means that static properties cannot be
accessed through an instance of a subclass. A static property can be accessed only through the
class object on which it is defined. For example, the following code defines a base class named
Base and a subclass that extends Base named Extender. A static variable named test is defined
in the Base class. The code as written in the following excerpt does not compile in strict mode
and generates a run-time error in standard mode.
package {
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // error
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base { }
The only way to access the static variable test is through the class object, as shown in the
following code:
Base.test;
It is permissible, however, to define an instance property using the same name as a static
property. Such an instance property can be defined in the same class as the static property or
in a subclass. For example, the Base class in the preceding example could have an instance
property named test. The following code compiles and executes because the instance
property is inherited by the Extender class. The code would also compile and execute if the
definition of the test instance variable is moved, but not copied, to the Extender class.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
170 Object-oriented programming in ActionScript
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // output: instance
}
}
}
class Base
{
public static var test:String = "static";
public var test:String = "instance";
}
class Extender extends Base {}
Static properties and the scope chain
Although static properties are not inherited, they are within the scope chain of the class that
defines them and any subclass of that class. As such, static properties are said to be in scope of
both the class in which they are defined and any subclasses. This means that a static property
is directly accessible within the body of the class that defines the static property and any
subclass of that class.
The following example modifies the classes defined in the previous example to show that the
static test variable defined in the Base class is in scope of the Extender class. In other words,
the Extender class can access the static test variable without prefixing the variable with the
name of the class that defines test.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base
{
public function Extender()
Advanced topics 171
{
trace(test); // output: static
}
}
If an instance property is defined that uses the same name as a static property in the same class
or a superclass, the instance property has higher precedence in the scope chain. The instance
property is said to shadow the static property, which means that the value of the instance
property is used instead of the value of the static property. For example, the following code
shows that if the Extender class defines an instance variable named test, the trace()
statement uses the value of the instance variable instead of the value of the static variable.:
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base
{
public static var test:String = "static";
}
class Extender extends Base
{
public var test:String = "instance";
public function Extender()
{
trace(test); // output: instance
}
}
Advanced topics
This section begins with a brief history of ActionScript and OOP and continues with a
discussion of the ActionScript 3.0 object model and how it enables the new ActionScript
Virtual Machine (AVM2) to perform significantly faster than previous versions of Flash Player
that contain the old ActionScript Virtual Machine (AVM1).
172 Object-oriented programming in ActionScript
History of ActionScript OOP support
Because ActionScript 3.0 builds upon previous versions of ActionScript, it may be helpful to
understand how the ActionScript object model has evolved. ActionScript began as a simple
scripting mechanism for early versions of the Flash authoring tool. Subsequently,
programmers began building increasingly complex applications with ActionScript. In
response to the needs of such programmers, each subsequent release has added language
features that facilitate the creation of complex applications.
ActionScript 1.0
ActionScript 1.0 refers to the version of the language used in Flash Player 6 and earlier. Even
at this early stage of development, the ActionScript object model was based on the concept of
the object as a fundamental data type. An ActionScript object is a compound data type with a
group of properties. When discussing the object model, the term properties includes everything
that is attached to an object, such as variables, functions, or methods.
Although this first generation of ActionScript does not support the definition of classes with a
class keyword, you can define a class using a special kind of object called a prototype object.
Instead of using a class keyword to create an abstract class definition that you instantiate
into concrete objects, as you do in class-based languages like Java and C++, prototype-based
languages like ActionScript 1.0 use an existing object as a model (or prototype) for other
objects. While objects in a class-based language may point to a class that serves as its template,
objects in a prototype-based language point instead to another object, its prototype, that
serves as its template.
To create a class in ActionScript 1.0, you define a constructor function for that class. In
ActionScript, functions are actual objects, not just abstract definitions. The constructor
function that you create serves as the prototypical object for instances of that class. The
following code creates a class named Shape and defines one property named visible that is
set to true by default:
// base class
function Shape() {}
// Create a property named visible.
Shape.prototype.visible = true;
This constructor function defines a Shape class that you can instantiate with the new operator,
as follows:
myShape = new Shape();
Just as the Shape() constructor function object serves as the prototype for instances of the
Shape class, it can also serve as the prototype for subclasses of Shape—that is, other classes
that extend the Shape class.
Advanced topics 173
The creation of a class that is a subclass of the Shape class is a two-step process. First, create
the class by defining a constructor function for the class, as follows:
// child class
function Circle(id, radius)
{
this.id = id;
this.radius = radius;
}
Second, use the new operator to declare that the Shape class is the prototype for the Circle
class. By default, any class you create uses the Object class as its prototype, which means that
Circle.prototype currently contains a generic object (an instance of the Object class). To
specify that Circle’s prototype is Shape instead of Object, use the following code to change the
value of Circle.prototype so that it contains a Shape object instead of a generic object:
// Make Circle a subclass of Shape.
Circle.prototype = new Shape();
The Shape class and the Circle class are now linked together in an inheritance relationship
that is commonly known as the prototype chain. The diagram illustrates the relationships in a
prototype chain:
The base class at the end of every prototype chain is the Object class. The Object class
contains a static property named Object.prototype that points to the base prototype object
for all objects created in ActionScript 1.0. The next object in our example prototype chain is
the Shape object. This is because the Shape.prototype property was never explicitly set, so it
still holds a generic object (an instance of the Object class). The final link in this chain is the
Circle class, which is linked to its prototype, the Shape class (the Circle.prototype property
holds a Shape object).
If we create an instance of the Circle class, as in the following example, the instance inherits
the prototype chain of the Circle class:
// Create an instance of the Circle class.
myCircle = new Circle();
Object.prototype
Shape.prototype
Circle.prototype
174 Object-oriented programming in ActionScript
Recall that we created a property named visible as a member of the Shape class. In our
example, the visible property does not exist as a part of the myCircle object, only as a
member of the Shape object, yet the following line of code outputs true:
trace(myCircle.visible); // output: true
Flash Player is able to ascertain that the myCircle object inherits the visible property by
walking up the prototype chain. When executing this code, Flash Player first searches through
the properties of the myCircle object for a property named visible, but does not find such
a property. Flash Player looks next in the Circle.prototype object, but still does not find a
property named visible. Continuing up the prototype chain, Flash Player finally finds the
visible property defined on the Shape.prototype object and outputs the value of that
property.
In the interest of simplicity, this section omits many of the details and intricacies of the
prototype chain, and aims instead to provide enough information to help you understand the
ActionScript 3.0 object model.
ActionScript 2.0
ActionScript 2.0 introduced new keywords such as class, extends, public, and private,
that allowed you to define classes in a way that is familiar to anyone who works with class-
based languages like Java and C++. It’s important to understand that the underlying
inheritance mechanism did not change between ActionScript 1.0 and ActionScript 2.0.
ActionScript 2.0 merely added a new syntax for defining classes. The prototype chain works
the same way in both versions of the language.
The new syntax introduced by ActionScript 2.0, shown in the following excerpt, allows you to
define classes in a way that many programmers find more intuitive:
// base class
class Shape
{
var visible:Boolean = true;
}
Note that ActionScript 2.0 also introduced type annotations for use with compile-time type
checking. This allows you to declare that the visible property in the previous example
should contain only a Boolean value. The new extends keyword also simplifies the process of
creating a subclass. In the following example, the two-step process necessary in ActionScript
1.0 is accomplished in one step with the extends keyword:
// child class
class Circle extends Shape
{
var id:Number;
var radius:Number;
Advanced topics 175
function Circle(id, radius)
{
this.id = id;
this.radius = radius;
}
}
The constructor is now declared as part of the class definition, and the class properties id and
radius must also be declared explicitly.
ActionScript 2.0 also added support for the definition of interfaces, which allow you to
further refine your object-oriented programs with formally defined protocols for inter-object
communication.
The ActionScript 3.0 class object
A common object-oriented programming paradigm, most commonly associated with Java and
C++, uses classes to define types of objects. Programming languages that adopt this paradigm
also tend to use classes to construct instances of the data type that the class defines.
ActionScript uses classes for both of these purposes, but its roots as a prototype-based
language add an interesting characteristic. ActionScript creates for each class definition a
special class object that allows sharing of both behavior and state. For many ActionScript
programmers, however, this distinction may have no practical coding implications.
ActionScript 3.0 is designed such that you can create sophisticated object-oriented
ActionScript applications without using, or even understanding, these special class objects.
For advanced programmers who want to take advantage of class objects, this section discusses
the issues in depth.
176 Object-oriented programming in ActionScript
The following diagram shows the structure of a class object that represents a simple class
named A that is defined with the statement class A {}:
Each rectangle in the diagram represents an object. Each object in the diagram has a subscript
character A to represent that it belongs to class A. The class object (CA) contains references to
a number of other important objects. An instance traits object (TA) stores the instance
properties that are defined within a class definition. A class traits object (TCA) represents the
internal type of the class and stores the static properties defined by the class (the subscript
character C stands for “class”). The prototype object (PA) always refers to the class object to
which it was originally attached through the constructor property.
The traits object
The traits object, which is new in ActionScript 3.0, was implemented with performance in
mind. In previous versions of ActionScript, name lookup could be a time-consuming process
as Flash Player walked the prototype chain. In ActionScript 3.0, name lookup is much more
efficient and less time consuming, because inherited properties are copied down from
superclasses into the traits object of subclasses.
The traits object is not directly accessible to programmer code, but its presence can be felt by
the improvements in performance and memory usage. The traits object provides the AVM2
with detailed information about the layout and contents of a class. With such knowledge, the
AVM2 is able to significantly reduce execution time, because it can often generate direct
machine instructions to access properties or call methods directly without a time-consuming
name lookup.
TCA
PACA
TA
Class.prototype Object.prototype
delegate
constructor
delegate
prototype
type
traits
Advanced topics 177
Thanks to the traits object, an object’s memory footprint can be significantly smaller than a
similar object in previous versions of ActionScript. For example, if a class is sealed (that is, the
class is not declared dynamic), an instance of the class does not need a hash table for
dynamically added properties, and can hold little more than a pointer to the traits objects and
some slots for the fixed properties defined in the class. As a result, an object that required 100
bytes of memory in ActionScript 2.0 could require as little as 20 bytes of memory in
ActionScript 3.0.
The prototype object
Every ActionScript class object has a property named prototype, which is a reference to the
class’s prototype object. The prototype object is a legacy of ActionScript’s roots as prototype-
based language. For more information, see “ActionScript 1.0” on page 172.
The prototype property is read-only, which means that it cannot be modified to point to
different objects. This differs from the class prototype property in previous versions of
ActionScript, where the prototype could be reassigned so that it pointed to a different class.
Although the prototype property is read-only, the prototype object that it references is not.
In other words, new properties can be added to the prototype object. Properties added to the
prototype object are shared among all instances of the class.
The prototype chain, which was the only inheritance mechanism in previous versions of
ActionScript, serves only a secondary role in ActionScript 3.0. The primary inheritance
mechanism, fixed property inheritance, is handled internally by the traits object. A fixed
property is a variable or method that is defined as part of a class definition. Fixed property
inheritance is also called class inheritance, because it is the inheritance mechanism that is
associated with keywords such as class, extends, and override.
The prototype chain provides an alternative inheritance mechanism that is more dynamic
than fixed property inheritance. You can add properties to a class’s prototype object not only
as part of the class definition, but also at run time through the class object’s prototype
property. Note, however, that if you set the compiler to strict mode, you may not be able to
access properties added to a prototype object unless you declare a class with the dynamic
keyword.
NOTE
The traits object is an internal implementation detail, and there is no guarantee that it will
not change or even disappear in future versions of ActionScript.
178 Object-oriented programming in ActionScript
A good example of a class with several properties attached to the prototype object is the
Object class. The Object class’s toString() and valueOf() methods are actually functions
assigned to properties of the Object class’s prototype object. The following is an example of
how the declaration of these methods could, in theory, look (the actual implementation
differs slightly because of implementation details):
public dynamic class Object
{
prototype.toString = function()
{
// statements
};
prototype.valueOf = function()
{
// statements
};
}
As mentioned previously, you can attach a property to a class’s prototype object outside the
class definition. For example, the toString() method can also be defined outside the Object
class definition, as follows:
Object.prototype.toString = function()
{
// statements
};
Unlike fixed property inheritance, however, prototype inheritance does not require the
override keyword if you want to redefine a method in a subclass. For example. if you want to
redefine the valueOf() method in a subclass of the Object class, you have three options.
First, you can define a valueOf() method on the subclass’s prototype object inside the class
definition. The following code creates a subclass of Object named Foo and redefines the
valueOf() method on Foo’s prototype object as part of the class definition. Because every
class inherits from Object, it is not necessary to use the extends keyword.
dynamic class Foo
{
prototype.valueOf = function()
{
return "Instance of Foo";
};
}
Second, you can define a valueOf() method on Foo’s prototype object outside the class
definition, as shown in the following code:
Foo.prototype.valueOf = function()
{
Advanced topics 179
return "Instance of Foo";
};
Third, you can define a fixed property named valueOf() as part of the Foo class. This
technique differs from the others in that it mixes fixed property inheritance with prototype
inheritance. Any subclass of Foo that wants to redefine valueOf() must use the override
keyword. The following code shows valueOf() defined as a fixed property in Foo:
class Foo
{
function valueOf():String
{
return "Instance of Foo";
}
}
The AS3 namespace
The existence of two separate inheritance mechanisms, fixed property inheritance and
prototype inheritance, creates an interesting compatibility challenge with respect to the
properties and methods of the core classes. Compatibility with the ECMAScript, Edition 4
draft language specification requires the use of prototype inheritance, which means that the
properties and methods of a core class are defined on the prototype object of that class. On
the other hand, compatibility with the Flash Player API calls for the use of fixed property
inheritance, which means that the properties and methods of a core class are defined in the
class definition using the const, var, and function keywords. Moreover, the use of fixed
properties instead of the prototype versions can lead to significant increases in run-time
performance.
ActionScript 3.0 solves this problem by using both prototype inheritance and fixed property
inheritance for the core classes. Each core class contains two sets of properties and methods.
One set is defined on the prototype object for compatibility with the ECMAScript
specification, and the other set is defined with fixed properties and the AS3 namespace for
compatibility with the Flash Player API.
The AS3 namespace provides a convenient mechanism for choosing between the two sets of
properties and methods. If you do not use the AS3 namespace, an instance of a core class
inherits the properties and methods defined on the core class’s prototype object. If you decide
to use the AS3 namespace, an instance of a core class inherits the AS3 versions because fixed
properties are always preferred over prototype properties. In other words, whenever a fixed
property is available, it is always used instead of an identically named prototype property.
180 Object-oriented programming in ActionScript
You can selectively use the AS3 namespace version of a property or method by qualifying it
with the AS3 namespace. For example, the following code uses the AS3 version of the
Array.pop() method:
var nums:Array = new Array(1, 2, 3);
nums.AS3::pop();
trace(nums); // output: 1,2
Alternatively, you can use the use namespace directive to open the AS3 namespace for all the
definitions within a block of code. For example, the following code uses the use namespace
directive to open the AS3 namespace for both the pop() and push() methods:
use namespace AS3;
var nums:Array = new Array(1, 2, 3);
nums.pop();
nums.push(5);
trace(nums) // output: 1,2,5
ActionScript 3.0 also provides compiler options for each set of properties so that you can
apply the AS3 namespace to your entire program. The -as3 compiler option represents the
AS3 namespace, and the -es compiler option represents the prototype inheritance option (es
stands for ECMAScript). To open the AS3 namespace for your entire program, set the -as3
compiler option to true and the -es compiler option to false. To use the prototype
versions, set the compiler options to the opposite values. The default compiler settings for
Adobe Flex Builder 2 and Adobe Flash CS3 Professional are -as3 = true and -es = false.
If you plan to extend any of the core classes and override any methods, you should understand
how the AS3 namespace can affect how you must declare an overridden method. If you are
using the AS3 namespace, any method override of a core class method must also use the AS3
namespace along with the override attribute. If you are not using the AS3 namespace and
want to redefine a core class method in a subclass, you should not use the AS3 namespace or
the override keyword.
Example: GeometricShapes
The GeometricShapes sample application shows how a number of object-oriented concepts
and features can be applied using ActionScript 3.0, including:
■ Defining classes
■ Extending classes
■ Polymorphism and the override keyword
■ Defining, extending and implementing interfaces
Example: GeometricShapes 181
It also includes a “factory method” that creates class instances, showing how to declare a
return value as an instance of an interface, and use that returned object in a generic way.
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The GeometricShapes application files can be found
in the folder Samples/GeometricShapes. The application consists of the following files:
Defining the GeometricShapes classes
The GeometricShapes application lets the user specify a type of geometric shape and a size. It
then responds with a description of the shape, its area, and distance around its perimeter.
The application user interface is trivial, including a few controls for selecting the type of
shape, setting the size, and displaying the description. The most interesting part of this
application is under the surface, in the structure of the classes and interfaces themselves.
File Description
GeometricShapes.mxml
or
GeometricShapes.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/
geometricshapes/IGeometricShape.as
The base interface defining methods to be
implemented by all GeometricShapes
application classes.
com/example/programmingas3/
geometricshapes/IPolygon.as
An interface defining methods to be
implemented by GeometricShapes application
classes that have multiple sides.
com/example/programmingas3/
geometricshapes/RegularPolygon.as
A type of geometric shape that has sides of
equal length postponed symmetrically around
the shape’s center.
com/example/programmingas3/
geometricshapes/Circle.as
A type of geometric shape that defines a circle.
com/example/programmingas3/
geometricshapes/EquilateralTriangle.as
A subclass of RegularPolygon that defines a
triangle with all sides the same length.
com/example/programmingas3/
geometricshapes/Square.as
A subclass of RegularPolygon defining a
rectangle with all four sides the same length.
com/example/programmingas3/
geometricshapes/
GeometricShapeFactory.as
A class containing a factory method for creating
shapes given a shape type and size.
182 Object-oriented programming in ActionScript
This application deals with geometric shapes, but it doesn’t display them graphically. It
provides a small library of classes and interfaces that will be reused in a later chapter’s example
(see “Example: SpriteArranger” on page 423). The SpriteArranger example displays the shapes
graphically and lets the user manipulate them, based on the class framework provided here in
the GeometricShapes application.
The classes and interfaces that define the geometric shapes in this example are shown in the
following diagram using Unified Modeling Language (UML) notation:
Defining common behavior with interfaces
This GeometricShapes application deals with three types of shapes: circles, squares, and
equilateral triangles. The GeometricShapes class structure begins with a very simple interface,
IGeometricShape, that lists methods common to all three types of shapes:
GeometricShapes Example Classes
<< interface >>
IGeometricShape
+getArea (): Number
+describe (): Strin
<< interface >>
IPolygon
+getPerimeter (): Number
+getSumOfAngles (): Number
Circle
+diameter:Number
+Circle () : Circle
+getArea () : Number
+describe () : String
+getCircumference () : Number
+numSides : int
+sideLength : Number
+RegularPolygon (): RegularPolygon
+getSumOfAngles (): Number
+getPerimeter (): Number
+getArea (): Number
+describe (): String
RegularPolygon
+EquilateralTriangle (): EquilateralTriangle
+getArea (): Number
+describe (): String
EquilateralTriangle
+Square (): Square
+getArea (): Number
+describe (): String
Square
Example: GeometricShapes 183
package com.example.programmingas3.geometricshapes
{
public interface IGeometricShape
{
function getArea():Number;
function describe():String;
}
}
The interface defines two methods: the getArea() method, which calculates and returns the
area of the shape, and the describe() method, which assembles a text description of the
shape’s properties.
We also want to know the distance around the perimeter of each shape. However, the
perimeter of a circle is called the circumference, and it’s calculated in a unique way, so the
behavior diverges from that of a triangle or a square. Still there is enough similarity between
triangles, squares, and other polygons that it makes sense to define a new interface class just
for them: IPolygon. The IPolygon interface is also rather simple, as shown here:
package com.example.programmingas3.geometricshapes
{
public interface IPolygon extends IGeometricShape
{
function getPerimeter():Number;
function getSumOfAngles():Number;
}
}
This interface defines two methods common to all polygons: the getPerimeter() method
that measures the combined distance of all the sides, and the getSumOfAngles() method that
adds up all the interior angles.
The IPolygon interface extends the IGeometricShape interface, which means that any class
that implements the IPolygon interface must declare all four methods—the two from the
IGeometricShape interface, and the two from the IPolygon interface.
Defining the shape classes
Once you have a good idea about the methods common to each type of shape, you can define
the shape classes themselves. In terms of how many methods you need to implement, the
simplest shape is the Circle class, shown here:
package com.example.programmingas3.geometricshapes
{
public class Circle implements IGeometricShape
{
public var diameter:Number;
184 Object-oriented programming in ActionScript
public function Circle(diam:Number = 100):void
{
this.diameter = diam;
}
public function getArea():Number
{
// The formula is Pi * radius * radius.
var radius:Number = diameter / 2;
return Math.PI * radius * radius;
}
public function getCircumference():Number
{
// The formula is Pi * diameter.
return Math.PI * diameter;
}
public function describe():String
{
var desc:String = "This shape is a Circle.n";
desc += "Its diameter is " + diameter + " pixels.n";
desc += "Its area is " + getArea() + ".n";
desc += "Its circumference is " + getCircumference() + ".n";
return desc;
}
}
}
The Circle class implements the IGeometricShape interface, so it must provide code for both
the getArea() method and the describe() method. In addition, it defines the
getCircumference() method, which is unique to the Circle class. The Circle class also
declares a property, diameter, which won’t be found in the other polygon classes.
The other two types of shapes, squares and equilateral triangles, have some other things in
common: they each have sides of equal length, and there are common formulas you can use to
calculate the perimeter and sum of interior angles for both. In fact, those common formulas
will apply to any other regular polygons that you need to define in the future as well.
The RegularPolygon class will be the superclass for both the Square class and the
EquilateralTriangle class. A superclass lets you define common methods in one place, so you
don’t have to define them separately in each subclass. Here is the code for the RegularPolygon
class:
package com.example.programmingas3.geometricshapes
{
public class RegularPolygon implements IPolygon
{
public var numSides:int;
Example: GeometricShapes 185
public var sideLength:Number;
public function RegularPolygon(len:Number = 100, sides:int = 3):void
{
this.sideLength = len;
this.numSides = sides;
}
public function getArea():Number
{
// This method should be overridden in subclasses.
return 0;
}
public function getPerimeter():Number
{
return sideLength * numSides;
}
public function getSumOfAngles():Number
{
if (numSides >= 3)
{
return ((numSides - 2) * 180);
}
else
{
return 0;
}
}
public function describe():String
{
var desc:String = "Each side is " + sideLength + " pixels long.n";
desc += "Its area is " + getArea() + " pixels square.n";
desc += "Its perimeter is " + getPerimeter() + " pixels long.n";
desc += "The sum of all interior angles in this shape is " +
getSumOfAngles() + " degrees.n";
return desc;
}
}
}
First, the RegularPolygon class declares two properties that are common to all regular
polygons: the length of each side (the sideLength property) and the number of sides (the
numSides property).
The RegularPolygon class implements the IPolygon interface and declares all four of the
IPolygon interface methods. It implements two of these—the getPerimeter() and
getSumOfAngles() methods—using common formulas.
186 Object-oriented programming in ActionScript
Because the formula for the getArea() method will differ from shape to shape, the base class
version of the method cannot include common logic that can be inherited by the subclass
methods. Instead, it simply returns a 0 default value to indicate that the area was not
calculated. To calculate the area of each shape correctly, the subclasses of the RegularPolygon
class will have to override the getArea() method themselves.
The following code for the EquilateralTriangle class show how the getArea() method is
overridden:
package com.example.programmingas3.geometricshapes
{
public class EquilateralTriangle extends RegularPolygon
{
public function EquilateralTriangle(len:Number = 100):void
{
super(len, 3);
}
public override function getArea():Number
{
// The formula is ((sideLength squared) * (square root of 3)) / 4.
return ( (this.sideLength * this.sideLength) * Math.sqrt(3) ) / 4;
}
public override function describe():String
{
/* starts with the name of the shape, then delegates the rest
of the description work to the RegularPolygon superclass */
var desc:String = "This shape is an equilateral Triangle.n";
desc += super.describe();
return desc;
}
}
}
The override keyword indicates that the EquilateralTriangle.getArea() method
intentionally overrides the getArea() method from the RegularPolygon superclass. When the
EquilateralTriangle.getArea() method is called, it calculates the area using the formula
in the preceding code, and the code in the RegularPolygon.getArea() method never
executes.
In contrast, the EquilateralTriangle class doesn’t define its own version of the
getPerimeter() method. When the EquilateralTriangle.getPerimeter() method is
called, the call goes up the inheritance chain and executes the code in the getPerimeter()
method of the RegularPolygon superclass.
Example: GeometricShapes 187
The EquilateralTriangle() constructor uses the super() statement to explicitly invoke
the RegularPolygon() constructor of its superclass. If both constructors had the same set of
parameters, you could have omitted the EquilateralTriangle() constructor completely,
and the RegularPolygon() constructor would be executed instead. However, the
RegularPolygon() constructor needs an extra parameter, numSides. So the
EquilateralTriangle() constructor calls super(len, 3), which passes along the len
input parameter and the value 3 to indicate that the triangle will have 3 sides.
The describe() method also uses the super() statement, but in a different way—to invoke
the RegularPolygon superclass’ version of the describe() method. The
EquilateralTriangle.describe() method first sets the desc string variable to a statement
about the type of shape. Then it gets the results of the RegularPolygon.describe() method
by calling super.describe(), and it appends that result to the desc string.
The Square class won’t be described in detail here, but it is similar to the EquilateralTriangle
class, providing a constructor and its own implementations of the getArea() and
describe() methods.
Polymorphism and the factory method
A set of classes that make good use of interfaces and inheritance can be used in many
interesting ways. For example, all of the shape classes described so far either implement the
IGeometricShape interface or extend a superclass that does. So if you define a variable to be an
instance of IGeometricShape, you don’t have to know whether it is actually an instance of the
Circle or the Square class in order to call its describe() method.
The following code shows how this works:
var myShape:IGeometricShape = new Circle(100);
trace(myShape.describe());
When myShape.describe() is called, it executes the method Circle.describe(), because
even though the variable is defined as an instance of the IGeometricShape interface, Circle is
its underlying class.
This example shows the principle of polymorphism in action: the exact same method call
results in different code being executed, depending on the class of the object whose method is
being invoked.
The GeometricShapes application applies this kind of interface-based polymorphism using a
simplified version of a design pattern known as the factory method. The term factory method
refers to a function that returns an object whose underlying data type or contents can differ
depending on the context.
188 Object-oriented programming in ActionScript
The GeometricShapeFactory class shown here defines a factory method named
createShape():
package com.example.programmingas3.geometricshapes
{
public class GeometricShapeFactory
{
public static var currentShape:IGeometricShape;
public static function createShape(shapeName:String,
len:Number):IGeometricShape
{
switch (shapeName)
{
case "Triangle":
return new EquilateralTriangle(len);
case "Square":
return new Square(len);
case "Circle":
return new Circle(len);
}
return null;
}
public static function describeShape(shapeType:String,
shapeSize:Number):String
{
GeometricShapeFactory.currentShape =
GeometricShapeFactory.createShape(shapeType, shapeSize);
return GeometricShapeFactory.currentShape.describe();
}
}
}
The createShape() factory method lets the shape subclass constructors define the details of
the instances that they create, while returning the new objects as IGeometricShape instances
so that they can be handled by the application in a more general way.
The describeShape() method in the preceding example shows how an application can use
the factory method to get a generic reference to a more specific object. The application can get
the description for a newly created Circle object like this:
GeometricShapeFactory.describeShape(“Circle”, 100);
Example: GeometricShapes 189
The describeShape() method then calls the createShape() factory method with the same
parameters, storing the new Circle object in a static variable named currentShape, which
was typed as an IGeometricShape object. Next, the describe() method is called on the
currentShape object, and that call is automatically resolved to execute the
Circle.describe() method, returning a detailed description of the circle.
Enhancing the sample application
The real power of interfaces and inheritance becomes apparent when you enhance or change
your application.
Say that you wanted to add a new shape, a pentagon, to this sample application. You would
create a new Pentagon class that extends the RegularPolygon class and defines its own versions
of the getArea() and describe() methods. Then you would add a new Pentagon option to
the combo box in the application’s user interface. But that’s it. The Pentagon class would
automatically get the functionality of the getPerimeter() method and the
getSumOfAngles() method from the RegularPolygon class by inheritance. Because it inherits
from a class that implements the IGeometricShape interface, a Pentagon instance can be
treated as an IGeometricShape instance too. That means you do not need to change any of the
methods in the GeometricShapeFactory class, making it much easier to add new types of
shapes when needed.
You may want to add a Pentagon class to the Geometric Shapes example as an exercise, to see
how interfaces and inheritance can ease the workload of adding new features to an
application.
190 Object-oriented programming in ActionScript
191
5
CHAPTER 5
Working with dates and times
Timing might not be everything, but it's usually a key factor in software applications.
ActionScript 3.0 provides powerful ways to manage calendar dates, times, and time intervals.
Two main classes provide most of this timing functionality: the Date class and the new Timer
class in the flash.utils package.
Contents
Basics of dates and times. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .191
Managing calendar dates and times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
Controlling time intervals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
Example: Simple analog clock. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Basics of dates and times
Introduction to working with dates and times
Dates and times are a common type of information used in ActionScript programs. For
instance, you might need to know the current day of the week or to measure how much time
a user spends on a particular screen, among many other possibilities. In ActionScript, you can
use the Date class to represent a single moment in time, including date and time information.
Within a Date instance are values for the individual date and time units, including year,
month, date, day of the week, hour, minutes, seconds, milliseconds, and time zone. For more
advanced uses, ActionScript also includes the Timer class, which you can use to perform
actions after a certain delay or at repeated intervals.
192 Working with dates and times
Common date and time tasks
This chapter describes the following common tasks for working with date and time
information:
■ Working with Date objects
■ Getting the current date and time
■ Accessing individual date and time units (days, years, hours, minutes, and so on)
■ Performing arithmetic with dates and times
■ Converting between time zones
■ Performing repeating actions
■ Performing actions after a set time interval
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ UTC time: Coordinated Universal Time—the “zero hour” reference time zone. All other
time zones are defined as a number of hours relative to (ahead of or behind) UTC time.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because the code listings in this chapter deal primarily with Date objects,
testing the examples will involve viewing the values of the variables used in the examples,
either by writing values into a text field instance on the Stage, or by using the trace()
function to print values to the Output panel. These techniques are described in detail in
“Testing in-chapter example code listings” on page 63.
Managing calendar dates and times 193
Managing calendar dates and times
All of the calendar date and time management functions in ActionScript 3.0 are concentrated
in the top-level Date class. The Date class contains methods and properties that let you
handle dates and times in either Coordinated Universal Time (UTC) or in local time specific
to a time zone. UTC is a standard time definition that is essentially the same as Greenwich
Mean Time (GMT).
Creating Date objects
The Date class boasts one of the most versatile constructor methods of all the core classes. You
can invoke it four different ways.
First, if given no parameters, the Date() constructor returns a Date object containing the
current date and time, in local time based on your time zone. Here’s an example:
var now:Date = new Date();
Second, if given a single numeric parameter, the Date() constructor treats that as the number
of milliseconds since January 1, 1970, and returns a corresponding Date object. Note that the
millisecond value you pass in is treated as milliseconds since January 1, 1970, in UTC.
However, the Date object shows values in your local time zone, unless you use the UTC-
specific methods to retrieve and display them. If you create a new Date object using a single
milliseconds parameter, make sure you account for the time zone difference between your
local time and UTC. The following statements create a Date object set to midnight on the
day of January 1, 1970, in UTC:
var millisecondsPerDay:int = 1000 * 60 * 60 * 24;
// gets a Date one day after the start date of 1/1/1970
var startTime:Date = new Date(millisecondsPerDay);
Third, you can pass multiple numeric parameters to the Date() constructor. It treats those
parameters as the year, month, day, hour, minute, second, and millisecond, respectively, and
returns a corresponding Date object. Those input parameters are assumed to be in local time
rather than UTC. The following statements get a Date object set to midnight at the start of
January 1, 2000, in local time:
var millenium:Date = new Date(2000, 0, 1, 0, 0, 0, 0);
194 Working with dates and times
Fourth, you can pass a single string parameter to the Date() constructor. It will try to parse
that string into date or time components and then return a corresponding Date object. If you
use this approach, it’s a good idea to enclose the Date() constructor in a try..catch block to
trap any parsing errors. The Date() constructor accepts a number of different string formats,
as listed in the ActionScript 3.0 Language and Components Reference. The following statement
initializes a new Date object using a string value:
var nextDay:Date = new Date(“Mon May 1 2006 11:30:00 AM”);
If the Date() constructor cannot successfully parse the string parameter, it will not raise an
exception. However, the resulting Date object will contain an invalid date value.
Getting time unit values
You can extract the values for various units of time within a Date object using properties or
methods of the Date class. Each of the following properties gives you the value of a time unit
in the Date object:
■ The fullYear property
■ The month property, which is in a numeric format with 0 for January up to 11 for
December
■ The date property, which is the calendar number of the day of the month, in the range
of 1 to 31
■ The day property, which is the day of the week in numeric format, with 0 standing for
Sunday
■ The hours property, in the range of 0 to 23
■ The minutes property
■ The seconds property
■ The milliseconds property
In fact, the Date class gives you a number of ways to get each of these values. For example, you
can get the month value of a Date object in four different ways:
■ The month property
■ The getMonth() method
■ The monthUTC property
■ The getMonthUTC() method
All four ways are essentially equivalent in terms of efficiency, so you can use whichever
approach suits your application best.
Managing calendar dates and times 195
The properties just listed all represent components of the total date value. For example, the
milliseconds property will never be greater than 999, since when it reaches 1000 the seconds
value increases by 1 and the milliseconds property resets to 0.
If you want to get the value of the Date object in terms of milliseconds since January 1, 1970
(UTC), you can use the getTime() method. Its counterpart, the setTime() method, lets you
change the value of an existing Date object using milliseconds since January 1, 1970 (UTC).
Performing date and time arithmetic
You can perform addition and subtraction on dates and times with the Date class. Date values
are kept internally in terms of milliseconds, so you should convert other values to milliseconds
before adding them to or subtracting them from Date objects.
If your application will perform a lot of date and time arithmetic, you might find it useful to
create constants that hold common time unit values in terms of milliseconds, like the
following:
public static const millisecondsPerMinute:int = 1000 * 60;
public static const millisecondsPerHour:int = 1000 * 60 * 60;
public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
Now it is easy to perform date arithmetic using standard time units. The following code sets a
date value to one hour from the current time using the getTime() and setTime() methods:
var oneHourFromNow:Date = new Date();
oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour);
Another way to set a date value is to create a new Date object using a single milliseconds
parameter. For example, the following code adds 30 days to one date to calculate another:
// sets the invoice date to today’s date
var invoiceDate:Date = new Date();
// adds 30 days to get the due date
var dueDate:Date = new Date(invoiceDate.getTime() + (30 *
millisecondsPerDay));
Next, the millisecondsPerDay constant is multiplied by 30 to represent 30 days’ time and
the result is added to the invoiceDate value and used to set the dueDate value.
196 Working with dates and times
Converting between time zones
Date and time arithmetic comes in handy when you want to convert dates from one time
zone to another. So does the getTimezoneOffset() method, which returns the value in
minutes by which the Date object’s time zone differs from UTC. It returns a value in minutes
because not all time zones are set to even-hour increments—some have half-hour offsets from
neighboring zones.
The following example uses the time zone offset to convert a date from local time to UTC. It
does the conversion by first calculating the time zone value in milliseconds and then adjusting
the Date value by that amount:
// creates a Date in local time
var nextDay:Date = new Date("Mon May 1 2006 11:30:00 AM");
// converts the Date to UTC by adding or subtracting the time zone offset
var offsetMilliseconds:Number = nextDay.getTimezoneOffset() * 60 * 1000;
nextDay.setTime(nextDay.getTime() + offsetMilliseconds);
Controlling time intervals
When you develop applications using Adobe Flash CS3 Professional, you have access to the
timeline, which provides a steady, frame-by-frame progression through your application. In
pure ActionScript projects, however, you must rely on other timing mechanisms.
Loops versus timers
In some programming languages, you must devise your own timing schemes using loop
statements like for or do..while.
Loop statements generally execute as fast as the local machine allows, which means that the
application runs faster on some machines and slower on others. If your application needs a
consistent timing interval, you need to tie it to an actual calendar or clock time. Many
applications, such as games, animations, and real-time controllers, need regular, time-driven
ticking mechanisms that are consistent from machine to machine.
The ActionScript 3.0 Timer class provides a powerful solution. Using the ActionScript 3.0
event model, the Timer class dispatches timer events whenever a specified time interval is
reached.
Controlling time intervals 197
The Timer class
The preferred way to handle timing functions in ActionScript 3.0 is to use the Timer class
(flash.utils.Timer), which can be used to dispatch events whenever an interval is reached.
To start a timer, you first create an instance of the Timer class, telling it how often to generate
a timer event and how many times to do so before stopping.
For example, the following code creates a Timer instance that dispatches an event every
second and continues for 60 seconds:
var oneMinuteTimer:Timer = new Timer(1000, 60);
The Timer object dispatches a TimerEvent object each time the given interval is reached. A
TimerEvent object’s event type is timer (defined by the constant TimerEvent.TIMER). A
TimerEvent object contains the same properties as a standard Event object.
If the Timer instance is set to a fixed number of intervals, it will also dispatch a
timerComplete event (defined by the constant TimerEvent.TIMER_COMPLETE) when it
reaches the final interval.
Here is a small sample application showing the Timer class in action:
package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class ShortTimer extends Sprite
{
public function ShortTimer()
{
// creates a new five-second Timer
var minuteTimer:Timer = new Timer(1000, 5);
// designates listeners for the interval and completion events
minuteTimer.addEventListener(TimerEvent.TIMER, onTick);
minuteTimer.addEventListener(TimerEvent.TIMER_COMPLETE,
onTimerComplete);
// starts the timer ticking
minuteTimer.start();
}
public function onTick(event:TimerEvent):void
{
// displays the tick count so far
// The target of this event is the Timer instance itself.
trace("tick " + event.target.currentCount);
}
198 Working with dates and times
public function onTimerComplete(event:TimerEvent):void
{
trace("Time's Up!");
}
}
}
When the ShortTimer class is created, it creates a Timer instance that will tick once per
second for five seconds. Then it adds two listeners to the timer: one that listens to each tick,
and one that listens for the timerComplete event.
Next, it starts the timer ticking, and from that point forward, the onTick() method executes
at one-second intervals.
The onTick() method simply displays the current tick count. After five seconds have passed,
the onTimerComplete() method executes, telling you that the time is up.
When you run this sample, you should see the following lines appear in your console or trace
window at the rate of one line per second:
tick 1
tick 2
tick 3
tick 4
tick 5
Time's Up!
Timing functions in the flash.utils package
ActionScript 3.0 contains a number of timing functions similar to those that were available in
ActionScript 2.0. These functions are provided as package-level functions in the flash.utils
package, and they operate just as they did in ActionScript 2.0.
Function Description
clearInterval(id:uint):void Cancels a specified setInterval() call.
clearTimeout(id:uint):void Cancels a specified setTimeout() call.
getTimer():int Returns the number of milliseconds that have
elapsed since Adobe Flash Player was
initialized.
setInterval(closure:Function,
delay:Number, ... arguments):uint
Runs a function at a specified interval (in
milliseconds).
setTimeout(closure:Function,
delay:Number, ... arguments):uint
Runs a specified function after a specified delay
(in milliseconds).
Example: Simple analog clock 199
These functions remain in ActionScript 3.0 for backward compatibility. Adobe does not
recommend that you use them in new ActionScript 3.0 applications. In general, it is easier
and more efficient to use the Timer class in your applications.
Example: Simple analog clock
A simple analog clock example illustrates two of the date and time concepts discussed in this
chapter:
■ Getting the current date and time and extracting values for the hours, minutes, and
seconds
■ Using a Timer to set the pace of an application
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The SimpleClock application files can be found in the
folder Samples/SimpleClock. The application consists of the following files:
Defining the SimpleClock class
The clock example is simple, but it’s a good idea to organize even simple applications well so
you could easily expand them in the future. To that end, the SimpleClock application uses the
SimpleClock class to handle the startup and time-keeping tasks, and then uses another class
named AnalogClockFace to actually display the time.
Here is the code that defines and initializes the SimpleClock class (note that in the Flash
version, SimpleClock extends the Sprite class instead):
public class SimpleClock extends UIComponent
{
/**
* The time display component.
*/
private var face:AnalogClockFace;
File Description
SimpleClockApp.mxml
or
SimpleClockApp.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/
simpleclock/SimpleClock.as
The main application file.
com/example/programmingas3/
simpleclock/AnalogClockFace.as
Draws a round clock face and hour, minute, and
seconds hands based on the time.
200 Working with dates and times
/**
* The Timer that acts like a heartbeat for the application.
*/
private var ticker:Timer;
The class has two important properties:
■ The face property, which is an instance of the AnalogClockFace class
■ The ticker property, which is an instance of the Timer class
The SimpleClock class uses a default constructor. The initClock() method takes care of the
real setup work, creating the clock face and starting the Timer instance ticking.
Creating the clock face
The next lines in the SimpleClock code create the clock face that is used to display the time:
/**
* Sets up a SimpleClock instance.
*/
public function initClock(faceSize:Number = 200)
{
// creates the clock face and adds it to the display list
face = new AnalogClockFace(Math.max(20, faceSize));
face.init();
addChild(face);
// draws the initial clock display
face.draw();
The size of the face can be passed in to the initClock() method. If no faceSize value is
passed, a default size of 200 pixels is used.
Next, the application initializes the face and then adds it to the display list using the
addChild() method inherited from the DisplayObject class. Then it calls the
AnalogClockFace.draw() method to display the clock face once, showing the current time.
Starting the timer
After creating the clock face, the initClock() method sets up a timer:
// creates a Timer that fires an event once per second
ticker = new Timer(1000);
// designates the onTick() method to handle Timer events
ticker.addEventListener(TimerEvent.TIMER, onTick);
// starts the clock ticking
ticker.start();
Example: Simple analog clock 201
First this method instantiates a Timer instance that will dispatch an event once per second
(every 1000 milliseconds). Since no second repeatCount parameter is passed to the Timer()
constructor, the Timer will keep repeating indefinitely.
The SimpleClock.onTick() method will execute once per second when the timer event is
received:
public function onTick(event:TimerEvent):void
{
// updates the clock display
face.draw();
}
The AnalogClockFace.draw() method simply draws the clock face and hands.
Displaying the current time
Most of the code in the AnalogClockFace class involves setting up the clock face’s display
elements. When the AnalogClockFace is initialized, it draws a circular outline, places a
numeric text label at each hour mark, and then creates three Shape objects, one each for the
hour hand, the minute hand, and the second hand on the clock.
Once the SimpleClock application is running, it calls the AnalogClockFace.draw() method
each second, as follows:
/**
* Called by the parent container when the display is being drawn.
*/
public override function draw():void
{
// stores the current date and time in an instance variable
currentTime = new Date();
showTime(currentTime);
}
This method saves the current time in a variable, so the time can’t change in the middle of
drawing the clock hands. Then it calls the showTime() method to display the hands, as the
following shows:
/**
* Displays the given Date/Time in that good old analog clock style.
*/
public function showTime(time:Date):void
{
// gets the time values
var seconds:uint = time.getSeconds();
var minutes:uint = time.getMinutes();
var hours:uint = time.getHours();
202 Working with dates and times
// multiplies by 6 to get degrees
this.secondHand.rotation = 180 + (seconds * 6);
this.minuteHand.rotation = 180 + (minutes * 6);
// Multiply by 30 to get basic degrees, then
// add up to 29.5 degrees (59 * 0.5)
// to account for the minutes.
this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5);
}
First, this method extracts the values for the hours, minutes, and seconds of the current time.
Then it uses these values to calculate the angle for each hand. Since the second hand makes a
full rotation in 60 seconds, it rotates 6 degrees each second (360/60). The minute hand
rotates the same amount each minute.
The hour hand updates every minute, too, so it can show some progress as the minutes tick
by. It rotates 30 degrees each hour (360/12), but it also rotates half a degree each minute (30
degrees divided by 60 minutes).
203
6
CHAPTER 6
Working with strings
The String class contains methods that let you work with text strings. Strings are important in
working with many objects. The methods described in this chapter are useful in working with
strings used in objects such as TextField, StaticText, XML, ContextMenu, and FileReference
objects.
Strings are sequences of characters. ActionScript 3.0 supports ASCII and Unicode characters.
Contents
Basics of strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Creating strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
The length property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207
Working with characters in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Comparing strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Obtaining string representations of other objects. . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Concatenating strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Finding substrings and patterns in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
Converting strings between uppercase and lowercase . . . . . . . . . . . . . . . . . . . . . . . 215
Example: ASCII art . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
204 Working with strings
Basics of strings
Introduction to working with strings
In programming parlance, a string is a text value—a sequence of letters, numbers, or other
characters strung together into a single value. For instance, this line of code creates a variable
with the data type String and assigns a literal string value to that variable:
var albumName:String = "Three for the money";
As this example shows, in ActionScript you can denote a string value by surrounding text with
double or single quotation marks. Here are several more examples of strings:
"Hello"
"555-7649"
"http://www.adobe.com/"
Any time you manipulate a piece of text in ActionScript, you are working with a string value.
The ActionScript String class is the data type you can use to work with text values. String
instances are frequently used for properties, method parameters, and so forth in many other
ActionScript classes.
Common tasks for working with strings
The following are common string-related tasks that are explored in this chapter:
■ Creating String objects
■ Working with special characters such as carriage-return, tab, and non-keyboard characters
■ Measuring string length
■ Isolating individual characters in a string
■ Joining strings
■ Comparing strings
■ Finding, extracting, and replacing portions of a string
■ Making strings uppercase or lowercase
Creating strings 205
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ ASCII: A system for representing text characters and symbols in computer programs. The
ASCII system supports the 26-letter English alphabet, plus a limited set of additional
characters.
■ Character: The smallest unit of text data (a single letter or symbol).
■ Concatenation: Joining multiple string values together by adding one to the end of the
other, creating a new string value.
■ Empty string: A string that contains no text, white space, or other characters, written as
"". An empty string value is different from a String variable with a null value—a null
String variable is a variable that does not have a String instance assigned to it, whereas an
empty string has an instance with a value that contains no characters.
■ String: A textual value (sequence of characters).
■ String literal (or “literal string”): A string value written explicitly in code, written as a text
value surrounded by double quotation marks or single quotation marks.
■ Substring: A string that is a portion of another string.
■ Unicode: A standard system for representing text characters and symbols in computer
programs. The Unicode system allows for the use of any character in any writing system.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because the code listings in this chapter deal primarily with manipulating
text, testing the examples will involve viewing the values of the variables used in the examples,
either by writing values into a text field instance on the Stage or by using the trace()
function to print values to the Output panel. These techniques are described in detail in
“Testing in-chapter example code listings” on page 63.
Creating strings
The String class is used to represent string (textual) data in ActionScript 3.0. ActionScript
strings support both ASCII and Unicode characters. The simplest way to create a string is to
use a string literal. To declare a string literal, use straight double quotation mark (") or single
quotation mark (') characters. For example, the following two strings are equivalent:
var str1:String = "hello";
var str2:String = 'hello';
206 Working with strings
You can also declare a string by using the new operator, as follows:
var str1:String = new String("hello");
var str2:String = new String(str1);
var str3:String = new String(); // str3 == ""
The following two strings are equivalent:
var str1:String = "hello";
var str2:String = new String("hello");
To use single quotation marks (') within a string literal defined with single quotation mark
(') delimiters, use the backslash escape character (). Similarly, to use double quotation marks
(") within a string literal defined with double quotation marks (") delimiters, use the
backslash escape character (). The following two strings are equivalent:
var str1:String = "That's "A-OK"";
var str2:String = 'That's "A-OK"';
You may choose to use single quotation marks or double quotation marks based on any single
or double quotation marks that exist in a string literal, as in the following:
var str1:String = "ActionScript <span class='heavy'>3.0</span>";
var str2:String = '<item id="155">banana</item>';
Keep in mind that ActionScript distinguishes between a straight single quotation mark (')
and a left or right single quotation mark (‘ or ’). The same is true for double quotation
marks. Use straight quotation marks to delineate string literals. When pasting text from
another source into ActionScript, be sure to use the correct characters.
The length property 207
As the following table shows, you can use the backslash escape character () to define other
characters in string literals:
The length property
Every string has a length property, which is equal to the number of characters in the string:
var str:String = "Adobe";
trace(str.length); // output: 5
An empty string and a null string both have a length of 0, as the following example shows:
var str1:String = new String();
trace(str1.length); // output: 0
str2:String = '';
trace(str2.length); // output: 0
Escape sequence Character
b Backspace
f Form feed
n Newline
r Carriage return
t Tab
unnnn The Unicode character with the character code specified by the
hexadecimal number nnnn; for example, u263a is the smiley
character.
xnn The ASCII character with the character code specified by the
hexadecimal number nn
' Single quotation mark
" Double quotation mark
 Single backslash character
208 Working with strings
Working with characters in strings
Every character in a string has an index position in the string (an integer). The index position
of the first character is 0. For example, in the following string, the character y is in position 0
and the character w is in position 5:
"yellow"
You can examine individual characters in various positions in a string using the charAt()
method and the charCodeAt() method, as in this example:
var str:String = "hello world!";
for (var:i = 0; i < str.length; i++)
{
trace(str.charAt(i), "-", str.charCodeAt(i));
}
When you run this code, the following output is produced:
h - 104
e - 101
l - 108
l - 108
o - 111
- 32
w - 119
o - 111
r - 114
l - 108
d - 100
! - 33
You can also use character codes to define a string using the fromCharCode() method, as the
following example shows:
var myStr:String =
String.fromCharCode(104,101,108,108,111,32,119,111,114,108,100,33);
// Sets myStr to "hello world!"
Comparing strings
You can use the following operators to compare strings: <, <=, !=, ==, =>, and >. These
operators can be used with conditional statements, such as if and while, as the following
example shows:
var str1:String = "Apple";
var str2:String = "apple";
if (str1 < str2)
{
trace("A < a, B < b, C < c, ...");
Concatenating strings 209
}
When using these operators with strings, ActionScript considers the character code value of
each character in the string, comparing characters from left to right, as in the following:
trace("A" < "B"); // true
trace("A" < "a"); // true
trace("Ab" < "az"); // true
trace("abc" < "abza"); // true
Use the == and != operators to compare strings with each other and to compare strings with
other types of objects, as the following example shows:
var str1:String = "1";
var str1b:String = "1";
var str2:String = "2";
trace(str1 == str1b); // true
trace(str1 == str2); // false
var total:uint = 1;
trace(str1 == total); // true
Obtaining string representations of other
objects
You can obtain a String representation for any kind of object. All objects have a toString()
method for this purpose:
var n:Number = 99.47;
var str:String = n.toString();
// str == "99.47"
When using the + concatenation operator with a combination of String objects and objects
that are not strings, you do not need to use the toString() method. For details on
concatenation, see the next section.
The String() global function returns the same value for a given object as the value returned
by the object calling the toString() method.
Concatenating strings
Concatenation of strings means taking two strings and joining them sequentially into one.
For example, you can use the + operator to concatenate two strings:
var str1:String = "green";
var str2:String = "ish";
var str3:String = str1 + str2; // str3 == "greenish"
210 Working with strings
You can also use the += operator to the produce the same result, as the following example
shows:
var str:String = "green";
str += "ish"; // str == "greenish"
Additionally, the String class includes a concat() method, which can be used as follows:
var str1:String = "Bonjour";
var str2:String = "from";
var str3:String = "Paris";
var str4:String = str1.concat(" ", str2, " ", str3);
// str4 == "Bonjour from Paris"
If you use the + operator (or the += operator) with a String object and an object that is not a
string, ActionScript automatically converts the nonstring object to a String object in order to
evaluate the expression, as shown in this example:
var str:String = "Area = ";
var area:Number = Math.PI * Math.pow(3, 2);
str = str + area; // str == "Area = 28.274333882308138"
However, you can use parentheses for grouping to provide context for the + operator, as the
following example shows:
trace("Total: $" + 4.55 + 1.45); // output: Total: $4.551.45
trace("Total: $" + (4.55 + 1.45)); // output: Total: $6
Finding substrings and patterns in strings
Substrings are sequential characters within a string. For example, the string "abc" has the
following substrings: "", "a", "ab", "abc", "b", "bc", "c". You can use ActionScript
methods to locate substrings of a string.
Patterns are defined in ActionScript by strings or by regular expressions. For example, the
following regular expression defines a specific pattern—the letters A, B, and C followed by a
digit character (the forward slashes are regular expression delimiters):
/ABCd/
ActionScript includes methods for finding patterns in strings and for replacing found matches
with replacement substrings. These methods are described in the following sections.
Regular expressions can define intricate patterns. For more information, see Chapter 9,
“Using regular expressions,” on page 285.
Finding substrings and patterns in strings 211
Finding a substring by character position
The substr() and substring() methods are similar. Both return a substring of a string.
Both take two parameters. In both methods, the first parameter is the position of the starting
character in the given string. However, in the substr() method, the second parameter is the
length of the substring to return, and in the substring() method, the second parameter is
the position of the character at the end of the substring (which is not included in the returned
string). This example shows the difference between these two methods:
var str:String = "Hello from Paris, Texas!!!";
trace(str.substr(11,15)); // output: Paris, Texas!!!
trace(str.substring(11,15)); // output: Pari
The slice() method functions similarly to the substring() method. When given two non-
negative integers as parameters, it works exactly the same. However, the slice() method can
take negative integers as parameters, in which case the character position is taken from the end
of the string, as shown in the following example:
var str:String = "Hello from Paris, Texas!!!";
trace(str.slice(11,15)); // output: Pari
trace(str.slice(-3,-1)); // output: !!
trace(str.slice(-3,26)); // output: !!!
trace(str.slice(-3,str.length)); // output: !!!
trace(str.slice(-8,-3)); // output: Texas
You can combine non-negative and negative integers as the parameters of the slice()
method.
Finding the character position of a matching
substring
You can use the indexOf() and lastIndexOf() methods to locate matching substrings
within a string, as the following example shows:
var str:String = "The moon, the stars, the sea, the land";
trace(str.indexOf("the")); // output: 10
Notice that the indexOf() method is case-sensitive.
You can specify a second parameter to indicate the index position in the string from which to
start the search, as follows:
var str:String = "The moon, the stars, the sea, the land"
trace(str.indexOf("the", 11)); // output: 21
The lastIndexOf() method finds the last occurrence of a substring in the string:
var str:String = "The moon, the stars, the sea, the land"
trace(str.lastIndexOf("the")); // output: 30
212 Working with strings
If you include a second parameter with the lastIndexOf() method, the search is conducted
from that index position in the string working backward (from right to left):
var str:String = "The moon, the stars, the sea, the land"
trace(str.lastIndexOf("the", 29)); // output: 21
Creating an array of substrings segmented by a
delimiter
You can use the split() method to create an array of substrings, which is divided based on a
delimiter. For example, you can segment a comma-delimited or tab-delimited string into
multiple strings.
The following example shows how to split an array into substrings with the ampersand (&)
character as the delimiter:
var queryStr:String = "first=joe&last=cheng&title=manager&StartDate=3/6/
65";
var params:Array = queryStr.split("&", 2); // params ==
["first=joe","last=cheng"]
The second parameter of the split() method, which is optional, defines the maximum size
of the array that is returned.
You can also use a regular expression as the delimiter character:
var str:String = "Give met5."
var a:Array = str.split(/s+/); // a == ["Give","me","5."]
For more information, see Chapter 9, “Using regular expressions,” on page 285 and the
ActionScript 3.0 Language and Components Reference.
Finding patterns in strings and replacing substrings
The String class includes the following methods for working with patterns in strings:
■ Use the match() and search() methods to locate substrings that match a pattern.
■ Use the replace() method to find substrings that match a pattern and replace them with
a specified substring.
These methods are described in the following sections.
You can use strings or regular expressions to define patterns used in these methods. For more
information on regular expressions, see Chapter 9, “Using regular expressions,” on page 285.
Finding substrings and patterns in strings 213
Finding matching substrings
The search() method returns the index position of the first substring that matches a given
pattern, as shown in this example:
var str:String = "The more the merrier.";
// (This search is case-sensitive.)
trace(str.search("the")); // output: 9
You can also use regular expressions to define the pattern to match, as this example shows:
var pattern:RegExp = /the/i;
var str:String = "The more the merrier.";
trace(str.search(pattern)); // 0
The output of the trace() method is 0, because the first character in the string is index
position 0. The i flag is set in the regular expression, so the search is not case-sensitive.
The search() method finds only one match and returns its starting index position, even if
the g (global) flag is set in the regular expression.
The following example shows a more intricate regular expression, one that matches a string in
double quotation marks:
var pattern:RegExp = /"[^"]*"/;
var str:String = "The "more" the merrier.";
trace(str.search(pattern)); // output: 4
str = "The "more the merrier.";
trace(str.search(pattern)); // output: -1
// (Indicates no match, since there is no closing double quotation mark.)
The match() method works similarly. It searches for a matching substring. However, when
you use the global flag in a regular expression pattern, as in the following example, match()
returns an array of matching substrings:
var str:String = "bob@example.com, omar@example.org";
var pattern:RegExp = /w*@w*.[org|com]+/g;
var results:Array = str.match(pattern);
The results array is set to the following:
["bob@example.com","omar@example.org"]
For more information on regular expressions, see Chapter 9, “Using regular expressions,” on
page 285.
214 Working with strings
Replacing matched substrings
You can use the replace() method to search for a specified pattern in a string and replace
matches with the specified replacement string, as the following example shows:
var str:String = "She sells seashells by the seashore.";
var pattern:RegExp = /sh/gi;
trace(str.replace(pattern, "sch"));
//sche sells seaschells by the seaschore.
Note that in this example, the matched strings are not case-sensitive because the i
(ignoreCase) flag is set in the regular expression, and multiple matches are replaced because
the g (global) flag is set. For more information, see Chapter 9, “Using regular expressions,”
on page 285.
You can include the following $ replacement codes in the replacement string. The
replacement text shown in the following table is inserted in place of the $ replacement code:
For example, the following shows the use of the $2 and $1 replacement codes, which represent
the first and second capturing group matched:
var str:String = "flip-flop";
var pattern:RegExp = /(w+)-(w+)/g;
trace(str.replace(pattern, "$2-$1")); // flop-flip
You can also use a function as the second parameter of the replace() method. The matching
text is replaced by the returned value of the function.
var str:String = "Now only $9.95!";
var price:RegExp = /$([d,]+.d+)+/i;
trace(str.replace(price, usdToEuro));
$ Code Replacement Text
$$ $
$& The matched substring.
$` The portion of the string that precedes the matched substring. This
code uses the straight left single quotation mark character (`), not the
straight single quotation mark (') or the left curly single quotation mark
(‘).
$' The portion of the string that follows the matched substring. This code
uses the straight single quotation mark (’).
$n The nth captured parenthetical group match, where n is a single digit, 1-
9, and $n is not followed by a decimal digit.
$nn The nnth captured parenthetical group match, where nn is a two-digit
decimal number, 01–99. If the nnth capture is undefined, the
replacement text is an empty string.
Converting strings between uppercase and lowercase 215
function usdToEuro(matchedSubstring:String,
capturedMatch1:String,
index:int,
str:String):String
{
var usd:String = capturedMatch1;
usd = usd.replace(",", "");
var exchangeRate:Number = 0.853690;
var euro:Number = usd * exchangeRate;
const euroSymbol:String = String.fromCharCode(8364);
return euro.toFixed(2) + " " + euroSymbol;
}
When you use a function as the second parameter of the replace() method, the following
arguments are passed to the function:
■ The matching portion of the string.
■ Any capturing parenthetical group matches. The number of arguments passed this way
will vary depending on the number of parenthetical matches. You can determine the
number of parenthetical matches by checking arguments.length - 3 within the
function code.
■ The index position in the string where the match begins.
■ The complete string.
Converting strings between uppercase
and lowercase
As the following example shows, the toLowerCase() method and the toUpperCase()
method convert alphabetical characters in the string to lowercase and uppercase, respectively:
var str:String = "Dr. Bob Roberts, #9."
trace(str.toLowerCase()); // dr. bob roberts, #9.
trace(str.toUpperCase()); // DR. BOB ROBERTS, #9.
After these methods are executed, the source string remains unchanged. To transform the
source string, use the following code:
str = str.toUpperCase();
These methods work with extended characters, not simply a–z and A–Z:
var str:String = "José Barça";
trace(str.toUpperCase(), str.toLowerCase()); // JOSÉ BARÇA josé barça
216 Working with strings
Example: ASCII art
This ASCII Art example shows a number of features of working with the String class in
ActionScript 3.0, including the following:
■ The split() method of the String class is used to extract values from a character-
delimited string (image information in a tab-delimited text file).
■ Several string-manipulation techniques, including split(), concatenation, and extracting
a portion of the string using substring() and substr(), are used to capitalize the first
letter of each word in the image titles.
■ The getCharAt() method is used to get a single character from a string (to determine the
ASCII character corresponding to a grayscale bitmap value).
■ String concatenation is used to build up the ASCII art representation of an image one
character at a time.
The term ASCII art refers to a text representations of an image, in which a grid of
monospaced font characters, such as Courier New characters, plots the image. The following
image shows an example of ASCII art produced by the application:
The ASCII art version of the graphic is shown on the right.
Example: ASCII art 217
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The ASCIIArt application files can be found in the
folder Samples/AsciiArt. The application consists of the following files:
Extracting tab-delimited values
This example uses the common practice of storing application data separate from the
application itself; that way, if the data changes (for example, if another image is added or an
image’s title changes), there is no need to recreate the SWF file. In this case, the image
metadata, including the image title, the URL of the actual image file, and some values that are
used to manipulate the image, are stored in a text file (the txt/ImageData.txt file in the
project). The contents of the text file are as follows:
FILENAMETITLEWHITE_THRESHHOLDBLACK_THRESHHOLD
FruitBasket.jpgPear, apple, orange, and bananad810
Banana.jpgA picture of a bananaC820
Orange.jpgorangeFF20
Apple.jpgpicture of an apple6E10
File Description
AsciiArtApp.mxml
or
AsciiArtApp.fla
The main application file in Flash (FLA) or Flex
(MXML)
com/example/programmingas3/asciiArt/
AsciiArtBuilder.as
The class that provides the main functionality of
the application, including extracting image
metadata from a text file, loading the images,
and managing the image-to-text conversion
process.
com/example/programmingas3/asciiArt/
BitmapToAsciiConverter.as
A class that provides the parseBitmapData()
method for converting image data into a String
version.
com/example/programmingas3/asciiArt/
Image.as
A class which represents a loaded bitmap
image.
com/example/programmingas3/asciiArt/
ImageInfo.as
A class representing metadata for an ASCII art
image (such as title, image file URL, and so on).
image/ A folder containing images used by the
application.
txt/ImageData.txt A tab-delimited text file, containing information
on the images to be loaded by the application.
218 Working with strings
The file uses a specific tab-delimited format. The first line (row) is a heading row. The
remaining lines contain the following data for each bitmap to be loaded:
■ The filename of the bitmap.
■ The display name of the bitmap.
■ The white-threshold and black-threshold values for the bitmaps. These are hex values
above which and below which a pixel is to be considered completely white or
completely black.
As soon as the application starts, the AsciiArtBuilder class loads and parses the contents of the
text file in order to create the “stack” of images that it will display, using the following code
from the AsciiArtBuilder class’s parseImageInfo() method:
var lines:Array = _imageInfoLoader.data.split("n");
var numLines:uint = lines.length;
for (var i:uint = 1; i < numLines; i++)
{
var imageInfoRaw:String = lines[i];
...
if (imageInfoRaw.length > 0)
{
// Create a new image info record and add it to the array of image info.
var imageInfo:ImageInfo = new ImageInfo();
// Split the current line into values (separated by tab (t)
// characters) and extract the individual properties:
var imageProperties:Array = imageInfoRaw.split("t");
imageInfo.fileName = imageProperties[0];
imageInfo.title = normalizeTitle(imageProperties[1]);
imageInfo.whiteThreshold = parseInt(imageProperties[2], 16);
imageInfo.blackThreshold = parseInt(imageProperties[3], 16);
result.push(imageInfo);
}
}
The entire contents of the text file are contained in a single String instance, the
_imageInfoLoader.data property. Using the split() method with the newline character
("n") as a parameter, the String instance is divided into an Array (lines) whose elements are
the individual lines of the text file. Next, the code uses a loop to work with each of the lines
(except the first, because it contains only headers rather than actual content). Inside the loop,
the split() method is used once again to divide the contents of the single line into a set of
values (the Array object named imageProperties). The parameter used with the split()
method in this case is the tab ("t") character, because the values in each line are delineated
by tab characters.
Example: ASCII art 219
Using String methods to normalize image titles
One of the design decisions for this application is that all the image titles are displayed using a
standard format, with the first letter of each word capitalized (except for a few words that are
commonly not capitalized in English titles). Rather than assume that the text file contains
properly formatted titles, the application formats the titles while they’re being extracted from
the text file.
In the previous code listing, as part of extracting individual image metadata values, the
following line of code is used:
imageInfo.title = normalizeTitle(imageProperties[1]);
In that code, the image’s title from the text file is passed through the normalizeTitle()
method before it is stored in the ImageInfo object:
private function normalizeTitle(title:String):String
{
var words:Array = title.split(" ");
var len:uint = words.length;
for (var i:uint; i < len; i++)
{
words[i] = capitalizeFirstLetter(words[i]);
}
return words.join(" ");
}
This method uses the split() method to divide the title into individual words (separated by
the space character), passes each word through the capitalizeFirstLetter() method, and
then uses the Array class’s join() method to combine the words back into a single string
again.
As its name suggests, the capitalizeFirstLetter() method actually does the work of
capitalizing the first letter of each word:
/**
* Capitalizes the first letter of a single word, unless it's one of
* a set of words that are normally not capitalized in English.
*/
private function capitalizeFirstLetter(word:String):String
{
switch (word)
{
220 Working with strings
case "and":
case "the":
case "in":
case "an":
case "or":
case "at":
case "of":
case "a":
// Don't do anything to these words.
break;
default:
// For any other word, capitalize the first character.
var firstLetter:String = word.substr(0, 1);
firstLetter = firstLetter.toUpperCase();
var otherLetters:String = word.substring(1);
word = firstLetter + otherLetters;
}
return word;
}
In English, the initial character of each word in a title is not capitalized if it is one of the
following words: “and,” “the,” “in,” “an,” “or,” “at,” “of,” or “a.” (This is a simplified version of
the rules.) To execute this logic, the code first uses a switch statement to check if the word is
one of the words that should not be capitalized. If so, the code simply jumps out of the
switch statement. On the other hand, if the word should be capitalized, that is done in
several steps, as follows:
1. The first letter of the word is extracted using substr(0, 1), which extracts a substring
starting with the character at index 0 (the first letter in the string, as indicated by the first
parameter 0). The substring will be one character in length (indicated by the second
parameter 1).
2. That character is capitalized using the toUpperCase() method.
3. The remaining characters of the original word are extracted using substring(1), which
extracts a substring starting at index 1 (the second letter) through the end of the string
(indicated by leaving off the second parameter of the substring() method).
4. The final word is created by combining the newly capitalized first letter with the remaining
letters using string concatenation: firstLetter + otherLetters.
Example: ASCII art 221
Generating the ASCII art text
The BitmapToAsciiConverter class provides the functionality of converting a bitmap image to
its ASCII text representation. This process is performed by the parseBitmapData() method,
which is partially shown here:
var result:String = "";
// Loop through the rows of pixels top to bottom:
for (var y:uint = 0; y < _data.height; y += verticalResolution)
{
// Within each row, loop through pixels left to right:
for (var x:uint = 0; x < _data.width; x += horizontalResolution)
{
...
// Convert the gray value in the 0-255 range to a value
// in the 0-64 range (since that's the number of "shades of
// gray" in the set of available characters):
index = Math.floor(grayVal / 4);
result += palette.charAt(index);
}
result += "n";
}
return result;
This code first defines a String instance named result that will be used to build up the
ASCII art version of the bitmap image. Next, it loops through individual pixels of the source
bitmap image. Using several color-manipulation techniques (omitted here for brevity), it
converts the red, green, and blue color values of an individual pixel to a single grayscale value
(a number from 0 to 255). The code then divides that value by 4 (as shown) to convert it to a
value in the 0-63 scale, which is stored in the variable index. (The 0-63 scale is used because
the “palette” of available ASCII characters used by this application contains 64 values.) The
palette of characters is defined as a String instance in the BitmapToAsciiConverter class:
// The characters are in order from darkest to lightest, so that their
// position (index) in the string corresponds to a relative color value
// (0 = black).
private static const palette:String =
"@#$%&8BMW*mwqpdbkhaoQ0OZXYUJCLtfjzxnuvcr[]{}1()|/?Il!i><+_~-;,. ";
Since the index variable defines which ASCII character in the palette corresponds to the
current pixel in the bitmap image, that character is retrieved from the palette String using
the charAt() method. It is then appended to the result String instance using the
concatenation assignment operator (+=). In addition, at the end of each row of pixels, a
newline character is concatenated to the end of the result String, forcing the line to wrap to
create a new row of character “pixels.”
222 Working with strings
223
7
CHAPTER 7
Working with arrays
Arrays allow you to store multiple values in a single data structure. You can use simple indexed
arrays that store values using fixed ordinal integer indexes or complex associative arrays that
store values using arbitrary keys. Arrays can also be multidimensional, containing elements
that are themselves arrays. This chapter discusses how to create and manipulate various types
of arrays.
Contents
Basics of arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .223
Indexed arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .225
Associative arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .234
Multidimensional arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .239
Cloning arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Example: PlayList. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .247
Basics of arrays
Introduction to working with arrays
Often in programming you’ll need to work with a set of items rather than a single object; for
example, in a music player application, you might want to have a list of songs waiting to be
played. You wouldn’t want to have to create a separate variable for each song on that list—it
would be preferable to have all the Song objects together in a bundle, and be able to work
with them as a group.
224 Working with arrays
An array is a programming element that acts as a container for a set of items, such as a list of
songs. Most commonly all the items in an array will be instances of the same class, but that is
not a requirement in ActionScript. The individual items in an array are known as the array’s
elements. You might think of an array as a file drawer for variables. Variables can be added as
elements in the array, which is like putting a folder into the file drawer. Once several files are
in the drawer, you can work with the array as a single variable (like carrying the whole drawer
to a different location); you can work with the variables as a group (like flipping through the
folders one by one searching for a piece of information); or you can access them individually
(like opening the drawer and selecting a single folder).
For example, imagine you’re creating a music player application where a user can select
multiple songs and add them to a playlist. In your ActionScript code, you might have a
method named addSongsToPlaylist(), which accepts a single array as a parameter. No
matter how many songs are to be added to the list (a few, a lot, or even only one), you have to
call the addSongsToPlaylist() method only one time, passing it the array containing the
Song objects. Inside the addSongsToPlaylist() method, you can use a loop to go through
the array’s elements (the songs) one by one and actually add them to the playlist.
The most common type of ActionScript array is an indexed array, which is an array where each
item is stored in a numbered slot (known as an index), and items are accessed using the
number, like an address. The Array class is used to represent an indexed array. Indexed arrays
work well for most programming needs. A special use of an indexed array is a
multidimensional array, which is an indexed array whose elements are indexed arrays (which
in turn contain other elements). Another type of array is an associative array, which uses a
string key instead of a numeric index to identify individual elements. Finally, for advanced
users, ActionScript 3.0 also includes the Dictionary class, which represents a dictionary—an
array that allows you to use any type of object as a key to distinguish between elements.
Common array tasks
The following common activities for working with arrays are described in this chapter:
■ Creating indexed arrays
■ Adding and removing array elements
■ Sorting array elements
■ Extracting portions of an array
■ Working with associative arrays and dictionaries
■ Working with multidimensional arrays
■ Copying array elements
■ Creating an array subclass
Indexed arrays 225
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Array: An object that serves as a container to group multiple objects together.
■ Associative array: An array that uses string keys to identify individual elements.
■ Dictionary: An array whose items consist of pairs of objects, known as the key and the
value. The key is used instead of a numeric index to identify a single element.
■ Element: A single item in an array.
■ Index: The numeric “address” used to identify a single element in an indexed array.
■ Indexed array: The standard type of array that stores each element in a numbered element,
and uses the number (index) to identify individual elements.
■ Key: The string or object used to identify a single element in an associative array or a
dictionary.
■ Multidimensional array: An array containing items that are arrays rather than single
values.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Essentially all the code listings in this chapter include the appropriate
trace() function call. To test the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the trace() function in the Output panel.
This and other techniques for testing example code listings are described in detail in “Testing
in-chapter example code listings” on page 63.
Indexed arrays
Indexed arrays store a series of one or more values organized such that each value can be
accessed using an unsigned integer value. The first index is always the number 0, and the
index increments by 1 for each subsequent element added to the array. As the following code
shows, you can create an indexed array by calling the Array class constructor or by initializing
the array with an array literal:
226 Working with arrays
// Use Array constructor.
var myArray:Array = new Array();
myArray.push("one");
myArray.push("two");
myArray.push("three");
trace(myArray); // output: one,two,three
// Use Array literal.
var myArray:Array = ["one", "two", "three"];
trace(myArray); // output: one,two,three
The Array class also contains properties and methods that allow you to modify indexed arrays.
These properties and methods apply almost exclusively to indexed arrays rather than
associative arrays.
Indexed arrays use an unsigned 32-bit integer for the index number. The maximum size of an
indexed array is 232 - 1 or 4,294,967,295. An attempt to create an array that is larger than
the maximum size results in a run-time error.
An array element can hold a value of any data type. ActionScript 3.0 does not support the
concept of typed arrays, which means that you cannot specify that all the elements of an array
belong to a specific data type.
This section explains how to create and modify indexed arrays using the Array class, starting
with how to create an array. The methods that modify arrays are grouped into three categories
that cover how to insert elements, remove elements, and sort arrays. Methods in a final group
treat an existing array as read-only; these methods merely query arrays. Rather than modifying
an existing array, the query methods all return a new array. The section concludes with a
discussion about extending the Array class.
Creating arrays
The Array constructor function can be used in three ways. First, if you call the constructor
with no arguments, you get an empty array. You can use the length property of the Array
class to verify that the array has no elements. For example, the following code calls the Array
constructor with no arguments:
var names:Array = new Array();
trace(names.length); // output: 0
Second, if you use a number as the only parameter to the Array constructor, an array of that
length is created, with each element’s value set to undefined. The argument must be an
unsigned integer between the values 0 and 4,294,967,295. For example, the following code
calls the Array constructor with a single numeric argument:
Indexed arrays 227
var names:Array = new Array(3);
trace(names.length); // output: 3
trace(names[0]); // output: undefined
trace(names[1]); // output: undefined
trace(names[2]); // output: undefined
Third, if you call the constructor and pass a list of elements as parameters, an array is created,
with elements corresponding to each of the parameters. The following code passes three
arguments to the Array constructor:
var names:Array = new Array("John", "Jane", "David");
trace(names.length); // output: 3
trace(names[0]); // output: John
trace(names[1]); // output: Jane
trace(names[2]); // output: David
You can also create arrays with array literals or object literals. An array literal can be assigned
directly to an array variable, as shown in the following example:
var names:Array = ["John", "Jane", "David"];
Inserting array elements
Three of the Array class methods—push(), unshift(), and splice()—allow you to insert
elements into an array. The push() method appends one or more elements to the end of an
array. In other words, the last element inserted into the array using the push() method will
have the highest index number. The unshift() method inserts one or more elements at the
beginning of an array, which is always at index number 0. The splice() method will insert
any number of items at a specified index in the array.
The following example demonstrates all three methods. An array named planets is created to
store the names of the planets in order of proximity to the Sun. First, the push() method is
called to add the initial item, Mars. Second, the unshift() method is called to insert the item
that belongs at the front of the array, Mercury. Finally, the splice() method is called to
insert the items Venus and Earth after Mercury, but before Mars. The first argument sent to
splice(), the integer 1, directs the insertion to begin at index 1. The second argument sent
to splice(), the integer 0, indicates that no items should be deleted. Finally, the third and
fourth arguments sent to splice(), Venus and Earth, are the items to be inserted.
var planets:Array = new Array();
planets.push("Mars"); // array contents: Mars
planets.unshift("Mercury"); // array contents: Mercury,Mars
planets.splice(1, 0, "Venus", "Earth");
trace(planets); // array contents: Mercury,Venus,Earth,Mars
228 Working with arrays
The push() and unshift() methods both return an unsigned integer that represents the
length of the modified array. The splice() method returns an empty array when used to
insert elements, which may seem strange, but makes more sense in light of the splice()
method’s versatility. You can use the splice() method not only to insert elements into an
array, but also to remove elements from an array. When used to remove elements, the
splice() method returns an array containing the elements removed.
Removing array elements
Three methods of the Array class—pop(), shift(), and splice()—allow you to remove
elements from an array. The pop() method removes an element from the end of the array. In
other words, it removes the element at the highest index number. The shift() method
removes an element from the beginning of the array, which means that it always removes the
element at index number 0. The splice() method, which can also be used to insert
elements, removes an arbitrary number of elements starting at the index number specified by
the first argument sent to the method.
The following example uses all three methods to remove elements from an array. An array
named oceans is created to store the names of large bodies of water. Some of the names in the
array are lakes rather than oceans, so they need to be removed.
First, the splice() method is used to remove the items Aral and Superior, and insert the
items Atlantic and Indian. The first argument sent to splice(), the integer 2, indicates
that the operation should start with the third item in the list, which is at index 2. The second
argument, 2, indicates that two items should be removed. The remaining arguments,
Atlantic and Indian, are values to be inserted at index 2.
Second, the pop() method is used to remove last element in the array, Huron. And third, the
shift() method is used to remove the first item in the array, Victoria.
var oceans:Array = ["Victoria", "Pacific", "Aral", "Superior", "Indian",
"Huron"];
oceans.splice(2, 2, "Arctic", "Atlantic"); // replaces Aral and Superior
oceans.pop(); // removes Huron
oceans.shift(); // removes Victoria
trace(oceans); // output: Pacific,Arctic,Atlantic,Indian
The pop() and shift() methods both return the item that was removed. The data type of
the return value is Object because arrays can hold values of any data type. The splice()
method returns an array containing the values removed. You can change the oceans array
example so that the call to splice() assigns the array to a new array variable, as shown in the
following example:
var lakes:Array = oceans.splice(2, 2, "Arctic", "Atlantic");
trace(lakes); // output: Aral,Superior
Indexed arrays 229
You may come across code that uses the delete operator on an array element. The delete
operator sets the value of an array element to undefined, but it does not remove the element
from the array. For example, the following code uses the delete operator on the third
element in the oceans array, but the length of the array remains 5:
var oceans:Array = ["Arctic", "Pacific", "Victoria", "Indian", "Atlantic"];
delete oceans[2];
trace(oceans); // output: Arctic,Pacific,,Indian,Atlantic
trace(oceans[2]); // output: undefined
trace(oceans.length); // output: 5
You can truncate an array using an array’s length property. If you set the length property of
an array to a length that is less than the current length of the array, the array is truncated,
removing any elements stored at index numbers higher than the new value of length minus
1. For example, if the oceans array were sorted such that all valid entries were at the
beginning of the array, you could use the length property to remove the entries at the end of
the array, as shown in the following code:
var oceans:Array = ["Arctic", "Pacific", "Victoria", "Aral", "Superior"];
oceans.length = 2;
trace(oceans); // output: Arctic,Pacific
Sorting an array
There are three methods—reverse(), sort(), and sortOn()—that allow you to change the
order of an array, either by sorting or reversing the order. All of these methods modify the
existing array. The reverse() method changes the order of the array such that the last
element becomes the first element, the penultimate element becomes the second element, and
so on. The sort() method allows you to sort an array in a variety of predefined ways, and
even allows you to create custom sorting algorithms. The sortOn() method allows you to sort
an indexed array of objects that have one or more common properties that can be used as sort
keys.
The reverse() method takes no parameters and does not return a value, but allows you to
toggle the order of your array from its current state to the reverse order. The following
example reverses the order of the oceans listed in the oceans array:
var oceans:Array = ["Arctic", "Atlantic", "Indian", "Pacific"];
oceans.reverse();
trace(oceans); // output: Pacific,Indian,Atlantic,Arctic
230 Working with arrays
The sort() method rearranges the elements in an array using the default sort order. The
default sort order has the following characteristics:
■ The sort is case-sensitive, which means that uppercase characters precede lowercase
characters. For example, the letter D precedes the letter b.
■ The sort is ascending, which means that lower character codes (such as A) precede higher
character codes (such as B).
■ The sort places identical values adjacent to each other but in no particular order.
■ The sort is string-based, which means that elements are converted to strings before they
are compared (for example, 10 precedes 3 because the string "1" has a lower character
code than the string "3" has).
You may find that you need to sort your array without regard to case, or in descending order,
or perhaps your array contains numbers that you want to sort numerically instead of
alphabetically. The sort() method has an options parameter that allows you to alter each
characteristic of the default sort order. The options are defined by a set of static constants in
the Array class, as shown in the following list:
■ Array.CASEINSENSITIVE: This option makes the sort disregard case. For example, the
lowercase letter b precedes the uppercase letter D.
■ Array.DESCENDING: This reverses the default ascending sort. For example, the letter B
precedes the letter A.
■ Array.UNIQUESORT: This causes the sort to abort if two identical values are found.
■ Array.NUMERIC: This causes numerical sorting, so that 3 precedes 10.
The following example highlights some of these options. An array named poets is created
that is sorted using several different options.
var poets:Array = ["Blake", "cummings", "Angelou", "Dante"];
poets.sort(); // default sort
trace(poets); // output: Angelou,Blake,Dante,cummings
poets.sort(Array.CASEINSENSITIVE);
trace(poets); // output: Angelou,Blake,cummings,Dante
poets.sort(Array.DESCENDING);
trace(poets); // output: cummings,Dante,Blake,Angelou
poets.sort(Array.DESCENDING | Array.CASEINSENSITIVE); // use two options
trace(poets); // output: Dante,cummings,Blake,Angelou
Indexed arrays 231
You can also write your own custom sort function, which you can pass as a parameter to the
sort() method. For example, if you have a list of names in which each list element contains a
person’s full name, but you want to sort the list by last name, you must use a custom sort
function to parse each element and use the last name in the sort function. The following code
shows how this can be done with a custom function that is used as a parameter to the
Array.sort() method:
var names:Array = new Array("John Q. Smith", "Jane Doe", "Mike Jones");
function orderLastName(a, b):int
{
var lastName:RegExp = /bS+$/;
var name1 = a.match(lastName);
var name2 = b.match(lastName);
if (name1 < name2)
{
return -1;
}
else if (name1 > name2)
{
return 1;
}
else
{
return 0;
}
}
trace(names); // output: John Q. Smith,Jane Doe,Mike Jones
names.sort(orderLastName);
trace(names); // output: Jane Doe,Mike Jones,John Q. Smith
The custom sort function orderLastName() uses a regular expression to extract the last name
from each element to use for the comparison operation. The function identifier
orderLastName is used as the sole parameter when calling the sort() method on the names
array. The sort function accepts two parameters, a and b, because it works on two array
elements at a time. The sort function’s return value indicates how the elements should be
sorted:
■ A return value of -1 indicates that the first parameter, a, precedes the second parameter, b.
■ A return value of 1 indicates that the second parameter, b, precedes the first, a.
■ A return value of 0 indicates that the elements have equal sorting precedence.
The sortOn() method is designed for indexed arrays with elements that contain objects.
These objects are expected to have at least one common property that can be used as the sort
key. The use of the sortOn() method for arrays of any other type yields unexpected results.
The following example revises the poets array so that each element is an object instead of a
string. Each object holds both the poet’s last name and year of birth.
232 Working with arrays
var poets:Array = new Array();
poets.push({name:"Angelou", born:"1928"});
poets.push({name:"Blake", born:"1757"});
poets.push({name:"cummings", born:"1894"});
poets.push({name:"Dante", born:"1265"});
poets.push({name:"Wang", born:"701"});
You can use the sortOn() method to sort the array by the born property. The sortOn()
method defines two parameters, fieldName and options. The fieldName argument must be
specified as a string. In the following example, sortOn() is called with two arguments,
"born" and Array.NUMERIC. The Array.NUMERIC argument is used to ensure that the sort is
done numerically instead of alphabetically. This is a good practice even when all the numbers
have the same number of digits because it ensures that the sort will continue to behave as
expected if a number with fewer or more digits is later added to the array.
poets.sortOn("born", Array.NUMERIC);
for (var i:int = 0; i < poets.length; ++i)
{
trace(poets[i].name, poets[i].born);
}
/* output:
Wang 701
Dante 1265
Blake 1757
cummings 1894
Angelou 1928
*/
Generally, the sort() and sortOn() methods modify an array. If you wish to sort an array
without modifying the existing array, pass the Array.RETURNINDEXEDARRAY constant as part
of the options parameter. This option directs the methods to return a new array that reflects
the sort and to leave the original array unmodified. The array returned by the methods is a
simple array of index numbers that reflects the new sort order and does not contain any
elements from the original array. For example, to sort the poets array by birth year without
modifying the array, include the Array.RETURNINDEXEDARRAY constant as part of the
argument passed for the options parameter.
The following example stores the returned index information in an array named indices and
uses the indices array in conjunction with the unmodified poets array to output the poets
in order of birth year:
var indices:Array;
indices = poets.sortOn("born", Array.NUMERIC | Array.RETURNINDEXEDARRAY);
for (var i:int = 0; i < indices.length; ++i)
{
var index:int = indices[i];
trace(poets[index].name, poets[index].born);
}
Indexed arrays 233
/* output:
Wang 701
Dante 1265
Blake 1757
cummings 1894
Angelou 1928
*/
Querying an array
The remaining four methods of the Array class—concat(), join(), slice(),
toString()—all query the array for information, but do not modify the array. The
concat() and slice() methods both return new arrays, while the join() and toString()
methods both return strings. The concat() method takes a new array or list of elements as
arguments and combines it with the existing array to create a new array. The slice() method
has two parameters, aptly named startIndex and an endIndex, and returns a new array
containing a copy of the elements “sliced” from the existing array. The slice begins with the
element at startIndex and ends with the element just before endIndex. That bears
repeating: the element at endIndex is not included in the return value.
The following example uses concat() and slice() to create new arrays using elements of
other arrays:
var array1:Array = ["alpha", "beta"];
var array2:Array = array1.concat("gamma", "delta");
trace(array2); // output: alpha,beta,gamma,delta
var array3:Array = array1.concat(array2);
trace(array3); // output: alpha,beta,alpha,beta,gamma,delta
var array4:Array = array3.slice(2,5);
trace(array4); // output: alpha,beta,gamma
You can use the join() and toString() methods to query the array and return its contents
as a string. If no parameters are used for the join() method, the two methods behave
identically—they return a string containing a comma-delimited list of all elements in the
array. The join() method, unlike the toString() method, accepts a parameter named
delimiter, which allows you to choose the symbol to use as a separator between each element
in the returned string.
The following example creates an array called rivers and calls both join() and toString()
to return the values in the array as a string. The toString() method is used to return
comma-separated values (riverCSV), while the join() method is used to return values
separated by the + character.
234 Working with arrays
var rivers:Array = ["Nile", "Amazon", "Yangtze", "Mississippi"];
var riverCSV:String = rivers.toString();
trace(riverCSV); // output: Nile,Amazon,Yangtze,Mississippi
var riverPSV:String = rivers.join("+");
trace(riverPSV); // output: Nile+Amazon+Yangtze+Mississippi
One issue to be aware of with the join() method is that any nested arrays are always returned
with comma-separated values, no matter what separator you specify for the main array
elements, as the following example shows:
var nested:Array = ["b","c","d"];
var letters:Array = ["a",nested,"e"];
var joined:String = letters.join("+");
trace(joined); // output: a+b,c,d+e
Associative arrays
An associative array, sometimes called a hash or map, uses keys instead of a numeric index to
organize stored values. Each key in an associative array is a unique string that is used to access
a stored value. An associative array is an instance of the Object class, which means that each
key corresponds to a property name. Associative arrays are unordered collections of key and
value pairs. Your code should not expect the keys of an associative array to be in a specific
order.
ActionScript 3.0 introduces an advanced type of associative array called a dictionary.
Dictionaries, which are instances of the Dictionary class in the flash.utils package, use keys
that can be of any data type but are usually instances of the Object class. In other words,
dictionary keys are not limited to values of type String.
This section describes how to create associative arrays that use strings for keys and how to use
the Dictionary class.
Associative arrays with string keys
There are two ways to create associative arrays in ActionScript 3.0. The first way is to use the
Object constructor, which has the advantage of allowing you to initialize your array with an
object literal. An instance of the Object class, also called a generic object, is functionally
identical to an associative array. Each property name of the generic object serves as the key
that provides access to a stored value.
Associative arrays 235
The following example creates an associative array named monitorInfo, using an object
literal to initialize the array with two key and value pairs:
var monitorInfo:Object = {type:"Flat Panel", resolution:"1600 x 1200"};
trace(monitorInfo["type"], monitorInfo["resolution"]);
// output: Flat Panel 1600 x 1200
If you do not need to initialize the array at declaration time, you can use the Object
constructor to create the array, as follows:
var monitorInfo:Object = new Object();
After the array is created using either an object literal or the Object class constructor, you can
add new values to the array using either the bracket operator ([]) or the dot operator (.). The
following example adds two new values to monitorArray:
monitorInfo["aspect ratio"] = "16:10"; // bad form, do not use spaces
monitorInfo.colors = "16.7 million";
trace(monitorInfo["aspect ratio"], monitorInfo.colors);
// output: 16:10 16.7 million
Note that the key named aspect ratio contains a space character. This is possible with the
bracket operator, but generates an error if attempted with the dot operator. Using spaces in
your key names is not recommended.
The second way to create an associative array is to use the Array constructor (or the
constructor of any dynamic class) and then use either the bracket operator ([]) or the dot
operator (.) to add key and value pairs to the array. If you declare your associative array to be
of type Array, you cannot use an object literal to initialize the array. The following example
creates an associative array named monitorInfo using the Array constructor and adds a key
called type and a key called resolution, along with their values:
var monitorInfo:Array = new Array();
monitorInfo["type"] = "Flat Panel";
monitorInfo["resolution"] = "1600 x 1200";
trace(monitorInfo["type"], monitorInfo["resolution"]);
// output: Flat Panel 1600 x 1200
There is no advantage in using the Array constructor to create an associative array. You cannot
use the Array.length property or any of the methods of the Array class with associative
arrays, even if you use the Array constructor or the Array data type. The use of the Array
constructor is best left for the creation of indexed arrays.
236 Working with arrays
Associative arrays with object keys
You can use the Dictionary class to create an associative array that uses objects for keys rather
than strings. Such arrays are sometimes called dictionaries, hashes, or maps. For example,
consider an application that determines the location of a Sprite object based on its association
with a specific container. You can use a Dictionary object to map each Sprite object to a
container.
The following code creates three instances of the Sprite class that serve as keys for the
Dictionary object. Each key is assigned a value of either GroupA or GroupB. The values can be
of any data type, but in this example both GroupA and GroupB are instances of the Object
class. Subsequently, you can access the value associated with each key with the property access
([]) operator, as shown in the following code:
import flash.display.Sprite;
import flash.utils.Dictionary;
var groupMap:Dictionary = new Dictionary();
// objects to use as keys
var spr1:Sprite = new Sprite();
var spr2:Sprite = new Sprite();
var spr3:Sprite = new Sprite();
// objects to use as values
var groupA:Object = new Object();
var groupB:Object = new Object();
// Create new key-value pairs in dictionary.
groupMap[spr1] = groupA;
groupMap[spr2] = groupB;
groupMap[spr3] = groupB;
if (groupMap[spr1] == groupA)
{
trace("spr1 is in groupA");
}
if (groupMap[spr2] == groupB)
{
trace("spr2 is in groupB");
}
if (groupMap[spr3] == groupB)
{
trace("spr3 is in groupB");
}
Associative arrays 237
Iterating with object keys
You can iterate through the contents of a Dictionary object with either a for..in loop or a
for each..in loop. A for..in loop allows you to iterate based on the keys, whereas a for
each..in loop allows you to iterate based on the values associated with each key.
Use the for..in loop for direct access to the object keys of a Dictionary object. You can also
access the values of the Dictionary object with the property access ([]) operator. The
following code uses the previous example of the groupMap dictionary to show how to iterate
through a Dictionary object with the for..in loop:
for (var key:Object in groupMap)
{
trace(key, groupMap[key]);
}
/* output:
[object Sprite] [object Object]
[object Sprite] [object Object]
[object Sprite] [object Object]
*/
Use the for each..in loop for direct access to the values of a Dictionary object. The
following code also uses the groupMap dictionary to show how to iterate through a Dictionary
object with the for each..in loop:
for each (var item:Object in groupMap)
{
trace(item);
}
/* output:
[object Object]
[object Object]
[object Object]
*/
Object keys and memory management
Adobe Flash Player uses a garbage collection system to recover memory that is no longer used.
When an object has no references pointing to it, the object becomes eligible for garbage
collection, and the memory is recovered the next time the garbage collection system executes.
For example, the following code creates a new object and assigns a reference to the object to
the variable myObject:
var myObject:Object = new Object();
238 Working with arrays
As long as any reference to the object exists, the garbage collection system will not recover the
memory that the object occupies. If the value of myObject is changed such that it points to a
different object or is set to the value null, the memory occupied by the original object
becomes eligible for garbage collection, but only if there are no other references to the original
object.
If you use myObject as a key in a Dictionary object, you are creating another reference to the
original object. For example, the following code creates two references to an object—the
myObject variable, and the key in the myMap object:
import flash.utils.Dictionary;
var myObject:Object = new Object();
var myMap:Dictionary = new Dictionary();
myMap[myObject] = "foo";
To make the object referenced by myObject eligible for garbage collection, you must remove
all references to it. In this case, you must change the value of myObject and delete the
myObject key from myMap, as shown in the following code:
myObject = null;
delete myMap[myObject];
Alternatively, you can use the useWeakReference parameter of the Dictionary constructor to
make all of the dictionary keys weak references. The garbage collection system ignores weak
references, which means that an object that has only weak references is eligible for garbage
collection. For example, in the following code, you do not need to delete the myObject key
from myMap in order to make the object eligible for garbage collection:
import flash.utils.Dictionary;
var myObject:Object = new Object();
var myMap:Dictionary = new Dictionary(true);
myMap[myObject] = "foo";
myObject = null; // Make object eligible for garbage collection.
Multidimensional arrays 239
Multidimensional arrays
Multidimensional arrays contain other arrays as elements. For example, consider a list of tasks
that is stored as an indexed array of strings:
var tasks:Array = ["wash dishes", "take out trash"];
If you want to store a separate list of tasks for each day of the week, you can create a
multidimensional array with one element for each day of the week. Each element contains an
indexed array, similar to the tasks array, that stores the list of tasks. You can use any
combination of indexed or associative arrays in multidimensional arrays. The examples in the
following sections use either two indexed arrays or an associative array of indexed arrays. You
might want to try the other combinations as exercises.
Two indexed arrays
When you use two indexed arrays, you can visualize the result as a table or spreadsheet. The
elements of the first array represent the rows of the table, while the elements of the second
array represent the columns.
For example, the following multidimensional array uses two indexed arrays to track task lists
for each day of the week. The first array, masterTaskList, is created using the Array class
constructor. Each element of the array represents a day of the week, with index 0 representing
Monday, and index 6 representing Sunday. These elements can be thought of as the rows in
the table. You can create each day’s task list by assigning an array literal to each of the seven
elements that you create in the masterTaskList array. The array literals represent the
columns in the table.
var masterTaskList:Array = new Array();
masterTaskList[0] = ["wash dishes", "take out trash"];
masterTaskList[1] = ["wash dishes", "pay bills"];
masterTaskList[2] = ["wash dishes", "dentist", "wash dog"];
masterTaskList[3] = ["wash dishes"];
masterTaskList[4] = ["wash dishes", "clean house"];
masterTaskList[5] = ["wash dishes", "wash car", "pay rent"];
masterTaskList[6] = ["mow lawn", "fix chair"];
You can access individual items on any of the task lists using bracket notation. The first set of
brackets represents the day of the week, and the second set of brackets represents the task list
for that day. For example, to retrieve the second task from Wednesday’s list, first use index 2
for Wednesday, and then use index 1 for the second task in the list.
trace(masterTaskList[2][1]); // output: dentist
240 Working with arrays
To retrieve the first task from Sunday’s list, use index 6 for Sunday and index 0 for the first
task on the list.
trace(masterTaskList[6][0]); // output: mow lawn
Associative array with an indexed array
To make the individual arrays easier to access, you can use an associative array for the days of
the week and an indexed array for the task lists. Using an associative array allows you to use
dot syntax when referring to a particular day of the week, but at the cost of extra run-time
processing to access each element of the associative array. The following example uses an
associative array as the basis of a task list, with a key and value pair for each day of the week:
var masterTaskList:Object = new Object();
masterTaskList["Monday"] = ["wash dishes", "take out trash"];
masterTaskList["Tuesday"] = ["wash dishes", "pay bills"];
masterTaskList["Wednesday"] = ["wash dishes", "dentist", "wash dog"];
masterTaskList["Thursday"] = ["wash dishes"];
masterTaskList["Friday"] = ["wash dishes", "clean house"];
masterTaskList["Saturday"] = ["wash dishes", "wash car", "pay rent"];
masterTaskList["Sunday"] = ["mow lawn", "fix chair"];
Dot syntax makes the code more readable by making it possible to avoid multiple sets of
brackets.
trace(masterTaskList.Wednesday[1]); // output: dentist
trace(masterTaskList.Sunday[0]); // output: mow lawn
You can iterate through the task list using a for..in loop, but you must use bracket notation
instead of dot syntax to access the value associated with each key. Because masterTaskList is
an associative array, the elements are not necessarily retrieved in the order that you may
expect, as the following example shows:
for (var day:String in masterTaskList)
{
trace(day + ": " + masterTaskList[day])
}
/* output:
Sunday: mow lawn,fix chair
Wednesday: wash dishes,dentist,wash dog
Friday: wash dishes,clean house
Thursday: wash dishes
Monday: wash dishes,take out trash
Saturday: wash dishes,wash car,pay rent
Tuesday: wash dishes,pay bills
*/
Advanced topics 241
Cloning arrays
The Array class has no built-in method for making copies of arrays. You can create a shallow
copy of an array by calling either the concat() or slice() methods with no arguments. In a
shallow copy, if the original array has elements that are objects, only the references to the
objects are copied rather than the objects themselves. The copy points to the same objects as
the original does. Any changes made to the objects are reflected in both arrays.
In a deep copy, any objects found in the original array are also copied so that the new array does
not point to the same objects as does the original array. Deep copying requires more than one
line of code, which usually calls for the creation of a function. Such a function could be
created as a global utility function or as a method of an Array subclass.
The following example defines a function named clone() that does deep copying. The
algorithm is borrowed from a common Java programming technique. The function creates a
deep copy by serializing the array into an instance of the ByteArray class, and then reading the
array back into a new array. This function accepts an object so that it can be used with both
indexed arrays and associative arrays, as shown in the following code:
import flash.utils.ByteArray;
function clone(source:Object):*
{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
Advanced topics
Extending the Array class
The Array class is one of the few core classes that is not final, which means that you can create
your own subclass of Array. This section provides an example of how to create a subclass of
Array and discusses some of the issues that can arise during the process.
242 Working with arrays
As mentioned previously, arrays in ActionScript are not typed, but you can create a subclass of
Array that accepts elements of only a specific data type. The example in the following sections
defines an Array subclass named TypedArray that limits its elements to values of the data type
specified in the first parameter. The TypedArray class is presented merely as an example of
how to extend the Array class and may not be suitable for production purposes for several
reasons. First, type checking occurs at run time rather than at compile time. Second, when a
TypedArray method encounters a mismatch, the mismatch is ignored and no exception is
thrown, although the methods can be easily modified to throw exceptions. Third, the class
cannot prevent the use of the array access operator to insert values of any type into the array.
Fourth, the coding style favors simplicity over performance optimization.
Declaring the subclass
Use the extends keyword to indicate that a class is a subclass of Array. A subclass of Array
should use the dynamic attribute, just as the Array class does. Otherwise, your subclass will
not function properly.
The following code shows the definition of the TypedArray class, which contains a constant to
hold the data type, a constructor method, and the four methods that are capable of adding
elements to the array. The code for each method is omitted in this example, but is delineated
and explained fully in the sections that follow:
public dynamic class TypedArray extends Array
{
private const dataType:Class;
public function TypedArray(...args) {}
AS3 override function concat(...args):Array {}
AS3 override function push(...args):uint {}
AS3 override function splice(...args) {}
AS3 override function unshift(...args):uint {}
}
Advanced topics 243
The four overridden methods all use the AS3 namespace instead of the public attribute
because this example assumes that the compiler option -as3 is set to true and the compiler
option -es is set to false. These are the default settings for Adobe Flex Builder 2 and for
Adobe Flash CS3 Professional. For more information, see “The AS3 namespace” on page 179.
TypedArray constructor
The subclass constructor poses an interesting challenge because the constructor must accept a
list of arguments of arbitrary length. The challenge is how to pass the arguments on to the
superconstructor to create the array. If you pass the list of arguments as an array, the
superconstructor considers it a single argument of type Array and the resulting array is always
1 element long. The traditional way to handle pass-through argument lists is to use the
Function.apply() method, which takes an array of arguments as its second parameter but
converts it to a list of arguments when executing the function. Unfortunately, the
Function.apply() method cannot be used with constructors.
The only option left is to recreate the logic of the Array constructor in the TypedArray
constructor. The following code shows the algorithm used in the Array class constructor,
which you can reuse in your Array subclass constructor:
public dynamic class Array
{
public function Array(...args)
{
var n:uint = args.length
if (n == 1 && (args[0] is Number))
{
var dlen:Number = args[0];
var ulen:uint = dlen;
if (ulen != dlen)
{
throw new RangeError("Array index is not a 32-bit unsigned integer
("+dlen+")");
}
length = ulen;
}
else
{
length = n;
for (var i:int=0; i < n; i++)
TIP
If you are an advanced developer who prefers to use prototype inheritance, you can
make two minor changes to the TypedArray class to make it compile with the compiler
option -es set to true. First, remove all occurrences of the override attribute and
replace the AS3 namespace with the public attribute. Second, substitute
Array.prototype for all four occurrences of super.
244 Working with arrays
{
this[i] = args[i]
}
}
}
}
The TypedArray constructor shares most of the code from the Array constructor, with only
four changes to the code. First, the parameter list includes a new required parameter of type
Class that allows specification of the array’s data type. Second, the data type passed to the
constructor is assigned to the dataType variable. Third, in the else statement, the value of
the length property is assigned after the for loop so that length includes only arguments
that are the proper type. Fourth, the body of the for loop uses the overridden version of the
push() method so that only arguments of the correct data type are added to the array. The
following example shows the TypedArray constructor function:
public dynamic class TypedArray extends Array
{
private var dataType:Class;
public function TypedArray(typeParam:Class, ...args)
{
dataType = typeParam;
var n:uint = args.length
if (n == 1 && (args[0] is Number))
{
var dlen:Number = args[0];
var ulen:uint = dlen
if (ulen != dlen)
{
throw new RangeError("Array index is not a 32-bit unsigned integer
("+dlen+")")
}
length = ulen;
}
else
{
for (var i:int=0; i < n; i++)
{
// type check done in push()
this.push(args[i])
}
length = this.length;
}
}
}
Advanced topics 245
TypedArray overridden methods
The TypedArray class overrides the four methods of the Array class that are capable of adding
elements to an array. In each case, the overridden method adds a type check that prevents the
addition of elements that are not the correct data type. Subsequently, each method calls the
superclass version of itself.
The push() method iterates through the list of arguments with a for..in loop and does a
type check on each argument. Any argument that is not the correct type is removed from the
args array with the splice() method. After the for..in loop ends, the args array contains
values only of type dataType. The superclass version of push() is then called with the
updated args array, as the following code shows:
AS3 override function push(...args):uint
{
for (var i:* in args)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
return (super.push.apply(this, args));
}
The concat() method creates a temporary TypedArray named passArgs to store the
arguments that pass the type check. This allows the reuse of the type check code that exists in
the push() method. A for..in loop iterates through the args array, and calls push() on
each argument. Because passArgs is typed as TypedArray, the TypedArray version of push()
is executed. The concat() method then calls its own superclass version, as the following code
shows:
AS3 override function concat(...args):Array
{
var passArgs:TypedArray = new TypedArray(dataType);
for (var i:* in args)
{
// type check done in push()
passArgs.push(args[i]);
}
return (super.concat.apply(this, passArgs));
}
246 Working with arrays
The splice() method takes an arbitrary list of arguments, but the first two arguments always
refer to an index number and the number of elements to delete. This is why the overridden
splice() method does type checking only for args array elements in index positions 2 or
higher. One point of interest in the code is that there appears to be a recursive call to
splice() inside the for loop, but this is not a recursive call because args is of type Array
rather than TypedArray, which means that the call to args.splice() is a call to the
superclass version of the method. After the for..in loop concludes, the args array contains
only values of the correct type in index positions 2 or higher, and splice() calls its own
superclass version, as shown in the following code:
AS3 override function splice(...args):*
{
if (args.length > 2)
{
for (var i:int=2; i< args.length; i++)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
}
return (super.splice.apply(this, args));
}
The unshift() method, which adds elements to the beginning of an array, also accepts an
arbitrary list of arguments. The overridden unshift() method uses an algorithm very similar
to that used by the push() method, as shown in the following example code:
AS3 override function unshift(...args):uint
{
for (var i:* in args)
{
if (!(args[i] is dataType))
{
args.splice(i,1);
}
}
return (super.unshift.apply(this, args));
}
}
Example: PlayList 247
Example: PlayList
The PlayList example demonstrates techniques for working with arrays, in the context of a
music playlist application that manages a list of songs. These techniques are:
■ Creating an indexed array
■ Adding items to an indexed array
■ Sorting an array of objects by different properties, using different sorting options
■ Converting an array to a character-delimited string
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The PlayList application files can be found in the
Samples/PlayList folder. The application consists of the following files:
PlayList class overview
The PlayList class manages a set of Song objects. It has public methods with functionality for
adding a song to the playlist (the addSong() method) and sorting the songs in the list (the
sortList() method). In addition, the class includes a read-only accessor property, songList,
which provides access to the actual set of songs in the playlist. Internally, the PlayList class
keeps track of its songs using a private Array variable:
public class PlayList
{
private var _songs:Array;
private var _currentSort:SortProperty = null;
private var _needToSort:Boolean = false;
...
}
File Description
PlayList.mxml
or
PlayList.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/playlist/
Song.as
A value object representing information about a
single song. The items that are managed by the
PlayList class are Song instances.
com/example/programmingas3/playlist/
SortProperty.as
A pseudo-enumeration whose available values
represent the properties of the Song class by
which a list of Song objects can be sorted.
248 Working with arrays
In addition to the _songs Array variable, which is used by the PlayList class to keep track of
its list of songs, two other private variables keep track of whether the list needs to be sorted
(_needToSort) and which property the song list is sorted by at a given time (_currentSort).
As with all objects, declaring an Array instance is only half the job of creating an Array. Before
accessing an Array instance’s properties or methods, it must be instantiated, which is done in
the PlayList class’s constructor.
public function PlayList()
{
this._songs = new Array();
// Set the initial sorting.
this.sortList(SortProperty.TITLE);
}
The first line of the constructor instantiates the _songs variable, so that it is ready to be used.
In addition, the sortList() method is called to set the initial sort-by property.
Adding a song to the list
When a user enters a new song into the application, the code in the data entry form calls the
PlayList class’s addSong() method.
/**
* Adds a song to the playlist.
*/
public function addSong(song:Song):void
{
this._songs.push(song);
this._needToSort = true;
}
Inside addSong(), the _songs array’s push() method is called, adding the Song object that
was passed to addSong() as a new element in that array. With the push() method, the new
element is added to the end of the array, regardless of any sorting that might have been
applied previously. This means that after the push() method has been called, the list of songs
is likely to no longer be sorted correctly, so the _needToSort variable is set to true. In theory,
the sortList() method could be called immediately, removing the need to keep track of
whether the list is sorted or not at a given time. In practice, however, there is no need for the
list of songs to be sorted until immediately before it is retrieved. By deferring the sorting
operation, the application doesn’t perform sorting that is unnecessary if, for example, several
songs are added to the list before it is retrieved.
Example: PlayList 249
Sorting the list of songs
Because the Song instances that are managed by the playlist are complex objects, users of the
application may wish to sort the playlist according to different properties, such as song title or
year of publication. In the PlayList application, the task of sorting the list of songs has three
parts: identifying the property by which the list should be sorted, indicating what sorting
options need to be used when sorting by that property, and performing the actual sort
operation.
Properties for sorting
A Song object keeps track of several properties, including song title, artist, publication year,
filename, and a user-selected set of genres in which the song belongs. Of these, only the first
three are practical for sorting. As a matter of convenience for developers, the example includes
the SortProperty class, which acts as an enumeration with values representing the properties
available for sorting.
public static const TITLE:SortProperty = new SortProperty("title");
public static const ARTIST:SortProperty = new SortProperty("artist");
public static const YEAR:SortProperty = new SortProperty("year");
The SortProperty class contain three constants, TITLE, ARTIST, and YEAR, each of which
stores a String containing the actual name of the associated Song class property that can be
used for sorting. Throughout the rest of the code, whenever a sort property is indicated, it is
done using the enumeration member. For instance, in the PlayList constructor, the list is
sorted initially by calling the sortList() method, as follows:
// Set the initial sorting.
this.sortList(SortProperty.TITLE);
Because the property for sorting is specified as SortProperty.TITLE, the songs are sorted
according to their title.
250 Working with arrays
Sorting by property and specifying sort options
The work of actually sorting the list of songs is performed by the PlayList class in the
sortList() method, as follows:
/**
* Sorts the list of songs according to the specified property.
*/
public function sortList(sortProperty:SortProperty):void
{
...
var sortOptions:uint;
switch (sortProperty)
{
case SortProperty.TITLE:
sortOptions = Array.CASEINSENSITIVE;
break;
case SortProperty.ARTIST:
sortOptions = Array.CASEINSENSITIVE;
break;
case SortProperty.YEAR:
sortOptions = Array.NUMERIC;
break;
}
// Perform the actual sorting of the data.
this._songs.sortOn(sortProperty.propertyName, sortOptions);
// Save the current sort property.
this._currentSort = sortProperty;
// Record that the list is sorted.
this._needToSort = false;
}
When sorting by title or artist, it makes sense to sort alphabetically, but when sorting by year,
it’s most logical to perform a numeric sort. The switch statement is used to define the
appropriate sorting option, stored in the variable sortOptions, according to the value
specified in the sortProperty parameter. Here again the named enumeration members are
used to distinguish between properties, rather than hard-coded values.
With the sort property and sort options determined, the _songs array is actually sorted by
calling its sortOn() method, passing those two values as parameters. The current sort
property is recorded, as is the fact that the song list is currently sorted.
Example: PlayList 251
Combining array elements into a character-delimited
string
In addition to using an array to maintain the song list in the PlayList class, in this example
arrays are also used in the Song class to help manage the list of genres to which a given song
belongs. Consider this snippet from the Song class’s definition:
private var _genres:String;
public function Song(title:String, artist:String, year:uint,
filename:String, genres:Array)
{
...
// Genres are passed in as an array
// but stored as a semicolon-separated string.
this._genres = genres.join(";");
}
When creating a new Song instance, the genres parameter that is used to specify the genre
(or genres) the song belongs to is defined as an Array instance. This makes it convenient to
group multiple genres together into a single variable that can be passed to the constructor.
However, internally the Song class maintains the genres in the private _genres variable as a
semicolon-separated String instance. The Array parameter is converted into a semicolon-
separated string by calling its join() method with the literal string value ";" as the specified
delimiter.
By the same token, the genres accessors allow genres to be set or retrieved as an Array:
public function get genres():Array
{
// Genres are stored as a semicolon-separated String,
// so they need to be transformed into an Array to pass them back out.
return this._genres.split(";");
}
public function set genres(value:Array):void
{
// Genres are passed in as an array,
// but stored as a semicolon-separated string.
this._genres = value.join(";");
}
The genres set accessor behaves exactly the same as the constructor; it accepts an Array and
calls the join() method to convert it to a semicolon-separated String. The get accessor
performs the opposite operation: the _genres variable’s split() method is called, splitting
the String into an array of values using the specified delimiter (the literal string value ";" as
before).
252 Working with arrays
253
8
CHAPTER 8
Handling errors
To “handle” an error means you build logic into your application that responds to, or fixes, an
error, generated either when an application is compiled or when a compiled application is
running. When your application handles errors, something occurs as a response when the error
is encountered, as opposed to no response and whatever process created the error failing
silently. Used correctly, error handling helps shield your application and its users from
otherwise unexpected behavior.
However, error handling is a broad category that includes responding to many kinds of errors
that are thrown during compilation or at run time. This chapter focuses on how to handle
run-time errors, the different types of errors that can be generated, and the advantages of the
new error-handling system in ActionScript 3.0. This chapter also explains how to implement
your own custom error-handling strategies for your applications.
Contents
Basics of error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Types of errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .257
Error handling in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Working with the debugger version of Flash Player . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Handling synchronous errors in an application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .262
Creating custom error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .267
Responding to error events and status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .268
Comparing the Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272
Example: CustomErrors application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .278
254 Handling errors
Basics of error handling
Introduction to error handling
A run-time error is something that goes wrong in your ActionScript code that stops the
ActionScript content from running in Adobe Flash Player. To ensure that your ActionScript
code runs smoothly for users, you must write code in your application that handles the
error—that fixes it, works around it, or at least lets the user know that it’s happened. This
process is called error handling.
Error handling is a broad category that includes responding to many kinds of errors that are
thrown during compilation or at run time. Errors that happen at compile time are often easier
to identify—you must fix them in order to complete the process of creating a SWF file. This
chapter doesn’t discuss compile-time errors; for more information on writing code that doesn’t
contain compile-time errors, see Chapter 3, “ActionScript language and syntax,” on page 67
and Chapter 4, “Object-oriented programming in ActionScript,” on page 137. This chapter
focuses on run-time errors.
Run-time errors can be more difficult to detect, because in order for them to occur the
erroneous code must actually be run. If a segment of your program has several branches of
code, like an if..then..else statement, you must test every possible condition, with all the
possible input values that real users might use, in order to confirm that your code is error-free.
Run-time errors can be divided into two categories: program errors are mistakes in your
ActionScript code, such as specifying the wrong data type for a method parameter; logical
errors are mistakes in the logic (the data checking and value manipulation) of your program,
such as using the wrong formula to calculate interest rates in a banking application. Again,
both of these types of errors can often be detected and corrected ahead of time by diligently
testing your application.
Ideally, you’ll want to identify and remove all errors from your application before it is released
to end users. However, not all errors can be foreseen or prevented. For example, suppose your
ActionScript application loads information from a particular website that is outside of your
control. If at some point that website isn’t available, the part of your application that depends
on that external data won’t behave correctly. The most important aspect of error handling
involves preparing for these unknown cases and handling them gracefully so that users can
continue to use your application, or at least get a friendly error message explaining why it isn’t
working.
Basics of error handling 255
Run-time errors are represented in two ways in ActionScript:
■ Error classes: Many errors have an error class associated with them. When an error occurs,
Flash Player creates an instance of the specific error class that is associated with that
particular error. Your code can use the information contained in that error object to make
an appropriate response to the error.
■ Error events: Sometimes an error occurs when Flash Player would normally trigger an
event. In those cases, Flash Player triggers an error event instead. Like other events, each
error event has a class associated with it, and Flash Player passes an instance of that class to
the methods that are subscribed to the error event.
To determine whether a particular method can trigger an error or error event, see the method’s
entry in the ActionScript 3.0 Language and Components Reference.
Common error-handling tasks
These are common error-related tasks you might need to perform with your code:
■ Writing code to handle errors
■ Testing for, catching, and re-throwing errors
■ Defining your own error class
■ Responding to error and status events
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Asynchronous: A program command such as a method call that doesn’t provide an
immediate result; instead it gives a result (or error) in the form of an event.
■ Catch: When an exception (a run-time error) occurs and your code becomes aware of the
exception, that code is said to catch the exception. Once an exception is caught, Flash
Player stops notifying other ActionScript code of the exception.
■ Debugger version: A special version of Flash Player that contains code for notifying users
of run-time errors. In the standard version of Flash Player (the one that most users have),
errors that aren’t handled by your ActionScript code are ignored by Flash Player. In the
debugger version (which is included with Adobe Flash CS3 Professional and Adobe Flex),
a warning message appears when an unhandled error happens.
■ Exception: An error that happens while a program is running and that the run-time
environment (that is, Flash Player) can’t resolve on its own.
256 Handling errors
■ Re-throw: When your code catches an exception, Flash Player no longer notifies other
objects of the exception. If it’s important for other objects to be notified of the exception,
your code must re-throw the exception to start the notification process again.
■ Synchronous: A program command, such as a method call, that provides an immediate
result (or immediately throws an error), meaning the response can be used within the
same code block.
■ Throw: The act of notifying Flash Player (and consequently, notifying other objects and
ActionScript code) that an error has occurred is known as throwing an error.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Essentially all the code listings in this chapter include the appropriate
trace() function call. To test the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the code listing’s trace() functions in the Output panel.
Some of the later code listings are more complex and are written as a class. To test these
examples:
1. Create an empty Flash document and save it to your computer.
2. Create a new ActionScript file and save it in the same directory as the Flash document. The
file’s name should match the name of the class in the code listing. For instance, if the code
listing defines a class named ErrorTest, use the name ErrorTest.as to save the ActionScript
file.
3. Copy the code listing into the ActionScript file and save the file.
4. In the Flash document, click a blank part of the Stage or work space to activate the
document Property inspector.
5. In the Property inspector, in the Document Class field, enter the name of the ActionScript
class you copied from the text.
6. Run the program using Control > Test Movie
You will see the results of the example in the Output panel (if the example uses the
trace() function) or in a text field created by the example code.
Types of errors 257
These techniques for testing example code listings are described in more detail in “Testing in-
chapter example code listings” on page 63.
Types of errors
When you develop and run applications, you encounter different types of errors and error
terminology. The following list introduces the major error types and terms:
■ Compile-time errors are raised by the ActionScript compiler during code compilation.
Compile-time errors occur when syntactical problems in your code prevent your
application from being built.
■ Run-time errors occur when you run your application after you compile it. Run-time errors
represent errors that are caused while a SWF file plays in Adobe Flash Player 9. In most
cases, you will be able to handle run-time errors as they occur, reporting them to the user
and taking steps to keep your application running. If the error is a fatal error, such as not
being able to connect to a remote website or load required data, you can use error
handling to allow your application to finish gracefully.
■ Synchronous errors are run-time errors that occur at the time a function is invoked—for
example, when you try to use a specific method and the argument you pass to the method
is invalid, so Flash Player throws an exception. Most errors occur synchronously—at the
time the statement executes—and the flow of control passes immediately to the most
applicable catch statement.
For example, the following code excerpt throws a run-time error because the browse()
method is not called before the program attempts to upload a file:
var fileRef:FileReference = new FileReference();
try
{
fileRef.upload("http://www.yourdomain.com/fileupload.cfm");
}
catch (error:IllegalOperationError)
{
trace(error);
// Error #2037: Functions called in incorrect sequence, or earlier
// call was unsuccessful.
}
258 Handling errors
In this case, a run-time error is thrown synchronously because Flash Player determined
that the browse() method was not called before the file upload was attempted.
For detailed information on synchronous error handling, see “Handling synchronous
errors in an application” on page 262.
■ Asynchronous errors are run-time errors that occur at various points during run time; they
generate events and are caught by event listeners. An asynchronous operation is one in
which a function initiates an operation, but doesn’t wait for it to complete. You can create
an error event listener to wait for the application or user to try some operation, and if the
operation fails, you catch the error with an event listener and respond to the error event.
Then, the event listener calls an event handler function to respond to the error event in a
useful manner. For example, the event handler could launch a dialog box that prompts the
user to resolve the error.
Consider the file-upload synchronous error example presented earlier. If you successfully
call the browse() method before beginning a file upload, Flash Player would dispatch
several events. For example, when an upload starts, the open event is dispatched. When
the file upload operation completes successfully, the complete event is dispatched.
Because event handling is asynchronous (that is, it does not occur at specific, known,
predesignated times), you need to use the addEventListener() method to listen for
these specific events, as the following code shows:
var fileRef:FileReference = new FileReference();
fileRef.addEventListener(Event.SELECT, selectHandler);
fileRef.addEventListener(Event.OPEN, openHandler);
fileRef.addEventListener(Event.COMPLETE, completeHandler);
fileRef.browse();
function selectHandler(event:Event):void
{
trace("...select...");
var request:URLRequest = new URLRequest("http://www.yourdomain.com/
fileupload.cfm");
request.method = URLRequestMethod.POST;
event.target.upload(request.url);
}
function openHandler(event:Event):void
{
trace("...open...");
}
function completeHandler(event:Event):void
{
trace("...complete...");
}
Error handling in ActionScript 3.0 259
For detailed information on asynchronous error handling, see “Responding to error events
and status” on page 268.
■ Uncaught exceptions are errors thrown with no corresponding logic (like a catch
statement) to respond to them. If your application throws an error, and no appropriate
catch statement or event handler can be found at the current or higher level to handle the
error, the error is considered an uncaught exception.
At run time, Flash Player ignores, by design, uncaught errors and tries to continue playing
if the error doesn’t stop the current SWF file, because users can’t necessarily resolve an
error themselves. The process of ignoring an uncaught error is called “failing silently” and
can complicate debugging applications. The debugger version of Flash Player responds to
an uncaught error by terminating the current script and displaying the uncaught error in
trace statement output or writing the error message to a log file. If the exception object is
an instance of the Error class or one of its subclasses, the getStackTrace() method is
invoked, and the stack trace information will also be displayed in trace statement output
or in a log file. For more information about using the debugger version of Flash Player, see
“Working with the debugger version of Flash Player” on page 261.
Error handling in ActionScript 3.0
Since many applications can run without building the logic to handle errors, developers are
tempted to postpone building error handling into their applications. However, without error
handling, an application may easily stall or frustrate the user if something doesn’t work as
expected. ActionScript 2.0 has an Error class that allows you to build logic into custom
functions to throw an exception with a specific message. Because error handling is critical for
making a user-friendly application, ActionScript 3.0 includes an expanded architecture for
catching errors.
NOTE
While the ActionScript 3.0 Language and Components Reference documents the
exceptions thrown by many methods, it may not include all possible exceptions for each
method. A method may throw an exception for syntax errors or other problems that are
not noted explicitly in the method description, even when the description does list some
of the exceptions a method throws.
260 Handling errors
ActionScript 3.0 error-handling elements
ActionScript 3.0 includes many tools for error handling, including:
■ Error classes. In compliance with the ECMAScript (ECMA-262) edition 4 draft language
specification, ActionScript 3.0 includes a broad range of Error classes to expand the scope
of situations that may produce error objects. Each Error class helps applications handle
and respond to specific error conditions, whether they are related to system errors (like a
MemoryError condition), coding errors (like an ArgumentError condition), networking
and communication errors (like a URIError condition), or other situations. For more
information on each class, see “Comparing the Error classes” on page 272.
■ Fewer silent failures. In earlier versions of Flash Player, errors were generated and reported
only if you explicitly used the throw statement. For Flash Player 9, native ActionScript
methods and properties throw run-time errors that allow you to handle these exceptions
more effectively when they occur, and then individually react to each exception.
■ Clear error messages displayed during debugging. When you are using the debugger
version of Flash Player, problematic code or situations will generate robust error messages,
which help you easily identify reasons why a particular block of code fails. This makes
fixing errors more efficient. For more information, see “Working with the debugger
version of Flash Player” on page 261.
■ Precise errors allow for clear error messages displayed to users at run time. In previous
versions of Flash Player, the FileReference.upload() method returned a Boolean value
of false if the upload() call was unsuccessful, indicating one of five possible errors. If an
error occurs when you call the upload() method in ActionScript 3.0, you can throw one
of four specific errors, which helps you display more accurate error messages to end users.
■ Refined error handling. Distinct errors are thrown for many common situations. For
example, in ActionScript 2.0, before a FileReference object has been populated, the name
property has the value null (so, before you can use or display the name property, you need
to ensure that the value is set and not null). In ActionScript 3.0, if you attempt to access
the name property before it has been populated, Flash Player throws an
IllegalOperationError, which informs you that the value has not been set, and you can use
try..catch..finally blocks to handle the error. For more information see “Using
try..catch..finally statements” on page 262.
■ No significant performance drawbacks. Using try..catch..finally blocks to handle
errors takes little or no additional resources compared to previous versions of
ActionScript.
■ An ErrorEvent class that allows you to build listeners for specific asynchronous error
events. For more information see “Responding to error events and status” on page 268.
Working with the debugger version of Flash Player 261
Error-handling strategies
As long as your application doesn’t encounter a problematic condition, it may still run
successfully if you don’t build error-handling logic into your code. However, if you don’t
actively handle errors and your application does encounter a problem, your users will never
know why your application fails when it does.
There are different ways you can approach error handling in your application. The following
list summarizes the three major options for handling errors:
■ Use try..catch..finally statements. These will catch synchronous errors as they
occur. You can nest your statements into a hierarchy to catch exceptions at various levels of
code execution. For more information, see “Using try..catch..finally statements”
on page 262.
■ Create your own custom error objects. You can use the Error class to create your own
custom error objects to track specific operations in your application that are not covered
by built-in error types. Then you can use try..catch..finally statements on your
custom error objects. For more information see “Creating custom error classes”
on page 267.
■ Write event listeners and handlers to respond to error events. By using this strategy, you
can create global error handlers that let you handle similar events without duplicating a lot
of code in try..catch..finally blocks. You are also more likely to catch asynchronous
errors using this approach. For more information, see “Responding to error events and
status” on page 268.
Working with the debugger version of
Flash Player
Adobe provides developers with a special edition of the Flash Player to assist debugging
efforts. You obtain a copy of the debugger version of Flash Player when you install Adobe
Flash CS3 Professional or Adobe Flex Builder 2.
There is a notable difference in how the debugger version and the release version of Flash
Player indicate errors. The debugger version shows the error type (such as a generic Error,
IOError, or EOFError), error number, and a human-readable error message. The release
version shows only the error type and error number. For example, consider the following
code:
try
{
tf.text = myByteArray.readBoolean();
}
262 Handling errors
catch (error:EOFError)
{
tf.text = error.toString();
}
If the readBoolean() method threw an EOFError in the debugger version of Flash Player,
the following message would be displayed in the tf text field: “EOFError: Error #2030: End
of file was encountered.”
The same code in a release version of Flash Player would display the following text:
“EOFError: Error #2030.”
In order to keep Flash Player’s resources and size to a minimum in the release version, error
message strings are not present. You can look up the error number in the documentation (the
appendixes of the ActionScript 3.0 Language and Components Reference) to correlate to an error
message. Alternatively, you can reproduce the error using the debugger version of Flash Player
to see the full message.
Handling synchronous errors in an
application
The most common error handling is synchronous error-handling logic, where you insert
statements into your code to catch synchronous errors at run time. This type of error handling
lets your application notice and recover from run-time errors when functions fail. The logic
for catching a synchronous error includes try..catch..finally statements, which literally
try an operation, catch any error response from Flash Player, and finally execute some other
operation to handle the failed operation.
Using try..catch..finally statements
When you work with synchronous run-time errors, use the try..catch..finally
statements to catch errors. When a run-time error occurs, Flash Player throws an exception,
which means that Flash Player suspends normal execution and creates a special object of type
Error. The Error object is then thrown to the first available catch block.
The try statement encloses statements that have the potential to create errors. You always use
the catch statement with a try statement. If an error is detected in one of the statements in
the try statement block, the catch statements that are attached to that try statement will
execute.
Handling synchronous errors in an application 263
The finally statement encloses statements that will execute whether or not an error occurs
in the try block. If there is no error, the statements within the finally block execute after
the try block statements complete. If there is an error, the appropriate catch statement
executes first, followed by the statements in the finally block.
The following code demonstrates the syntax for using the try..catch..finally statements:
try
{
// some code that could throw an error
}
catch (err:Error)
{
// code to react to the error
}
finally
{
// Code that runs whether or not an error was thrown. This code can clean
// up after the error, or take steps to keep the application running.
}
Each catch statement identifies a specific type of exception that it handles. The catch
statement can specify only error classes that are subclasses of the Error class. Each catch
statement is checked in order. Only the first catch statement that matches the type of error
thrown will execute. In other words, if you first check the higher-level Error class and then a
subclass of the Error class, only the higher-level Error class will match. The following code
illustrates this point:
try
{
throw new ArgumentError("I am an ArgumentError");
}
catch (error:Error)
{
trace("<Error> " + error.message);
}
catch (error:ArgumentError)
{
trace("<ArgumentError> " + error.message);
}
The previous code displays the following output:
<Error> I am an ArgumentError
In order to correctly catch the ArgumentError, you need to make sure that the most specific
error types are listed first and the more generic error types are listed later, as the following code
shows:
try
{
264 Handling errors
throw new ArgumentError("I am an ArgumentError");
}
catch (error:ArgumentError)
{
trace("<ArgumentError> " + error.message);
}
catch (error:Error)
{
trace("<Error> " + error.message);
}
Several methods and properties in the Flash Player API throw run-time errors if they
encounter errors while they execute. For example, the close() method in the Sound class
throws an IOError if the method is unable to close the audio stream, as demonstrated in the
following code:
var mySound:Sound = new Sound();
try
{
mySound.close();
}
catch (error:IOError)
{
// Error #2029: This URLStream object does not have an open stream.
}
As you become more familiar with the ActionScript 3.0 Language and Components Reference,
you’ll notice which methods throw exceptions, as detailed in each method’s description.
The throw statement
Flash Player throws exceptions when it encounters errors in your application at run time. In
addition, you can explicitly throw exceptions yourself using the throw statement. When
explicitly throwing errors, Adobe recommends that you throw instances of the Error class or
its subclasses. The following code demonstrates a throw statement that throws an instance of
the Error class, MyErr, and eventually calls a function, myFunction(), to respond after the
error is thrown:
var MyError:Error = new Error("Encountered an error with the numUsers
value", 99);
var numUsers:uint = 0;
try
{
if (numUsers == 0)
{
trace("numUsers equals 0");
}
}
Handling synchronous errors in an application 265
catch (error:uint)
{
throw MyError; // Catch unsigned integer errors.
}
catch (error:int)
{
throw MyError; // Catch integer errors.
}
catch (error:Number)
{
throw MyError; // Catch number errors.
}
catch (error:*)
{
throw MyError; // Catch any other error.
}
finally
{
myFunction(); // Perform any necessary cleanup here.
}
Notice that the catch statements are ordered so that the most specific data types are listed
first. If the catch statement for the Number data type was listed first, neither the catch
statement for the uint data type nor the catch statement for the int data type would ever get
executed.
Displaying a simple error message
One of the biggest benefits of the new exception and error event model is that it allows you to
tell users when and why an action has failed. Your part is to write the code to display the
message and offer options in response.
The following code shows a simple try..catch statement to display the error in a text field:
package
{
import flash.display.Sprite;
import flash.text.TextField;
public class SimpleError extends Sprite
{
public var employee:XML =
<EmpCode>
<costCenter>1234</costCenter>
NOTE
In the Java programming language, each function that can throw an exception must
declare this fact, listing the exception classes it can throw in a throws clause attached to
the function declaration. ActionScript does not require you to declare the exceptions
that can be thrown by a function.
266 Handling errors
<costCenter>1-234</costCenter>
</EmpCode>;
public function SimpleError()
{
try
{
if (employee.costCenter.length() != 1)
{
throw new Error("Error, employee must have exactly one cost
center assigned.");
}
}
catch (error:Error)
{
var errorMessage:TextField = new TextField();
errorMessage.autoSize = TextFieldAutoSize.LEFT;
errorMessage.textColor = 0xFF0000;
errorMessage.text = error.message;
addChild(errorMessage);
}
}
}
}
Using a wider range of error classes and built-in compiler errors, ActionScript 3.0 offers more
information than previous versions of ActionScript about why something has failed. This
enables you to build more stable applications with better error handling.
Rethrowing errors
When you build applications, there are several occasions in which you may need to rethrow
an error if you are unable to handle the error properly. For example, the following code shows
a nested try..catch block, which rethrows a custom ApplicationError if the nested catch
block is unable to handle the error:
try
{
try
{
trace("<< try >>");
throw new ArgumentError("some error which will be rethrown");
}
catch (error:ApplicationError)
{
trace("<< catch >> " + error);
trace("<< throw >>");
throw error;
Creating custom error classes 267
}
catch (error:Error)
{
trace("<< Error >> " + error);
}
}
catch (error:ApplicationError)
{
trace("<< catch >> " + error);
}
The output from the previous snippet would be the following:
<< try >>
<< catch >> ApplicationError: some error which will be rethrown
<< throw >>
<< catch >> ApplicationError: some error which will be rethrown
The nested try block throws a custom ApplicationError error that is caught by the
subsequent catch block. This nested catch block can try to handle the error, and if
unsuccessful, throw the ApplicationError object to the enclosing try..catch block.
Creating custom error classes
You can extend one of the standard Error classes to create your own specialized error classes in
ActionScript. There are a number of reasons to create your own error classes:
■ To identify specific errors or groups of errors that are unique to your application.
For example, you may want to take different actions for errors thrown by your own code,
in addition to those trapped by Flash Player. You can create a subclass of the Error class to
track the new error data type in try..catch blocks.
■ To provide unique error display capabilities for errors generated by your application.
For example, you can create a new toString() method that formats your error messages
in a certain way. You can also define a lookupErrorString() method that takes an error
code and retrieves the proper message based on the user’s language preference.
A specialized error class must extend the core ActionScript Error class. Here is an example of a
specialized AppError class that extends the Error class:
public class AppError extends Error
{
public function AppError(message:String, errorID:int)
{
super(message, errorID);
}
}
268 Handling errors
The following shows an example of using AppError in your project:
try
{
throw new AppError("Encountered Custom AppError", 29);
}
catch (error:AppError)
{
trace(error.errorID + ": " + error.message)
}
Responding to error events and status
One of the most noticeable improvements to error handling in ActionScript 3.0 is the support
for error event handling for responding to asynchronous run-time errors. (For a definition of
asynchronous errors, see “Types of errors” on page 257.)
You can create event listeners and event handlers to respond to the error events. Many classes
dispatch error events the same way they dispatch other events. For example, an instance of the
XMLSocket class normally dispatches three types of events: Event.CLOSE, Event.CONNECT,
and DataEvent.DATA. However, when a problem occurs, the XMLSocket class can dispatch
the IOErrorEvent.IOError or the SecurityErrorEvent.SECURITY_ERROR. For more
information about event listeners and event handlers, see Chapter 10, “Handling events,” on
page 313.
Error events fit into one of two categories:
■ Error events that extend the ErrorEvent class
The flash.events.ErrorEvent class contains the properties and methods for managing
Flash Player run-time errors related to networking and communication operations.
The AsyncErrorEvent, IOErrorEvent, and SecurityErrorEvent classes extend the
ErrorEvent class. If you’re using the debugger version of Flash Player, a dialog box will
inform you at run-time of any error events without listener functions that the player
encounters.
NOTE
If you want to override the Error.toString() method in your subclass, you need to give it
one ...(rest) parameter. The ECMAScript (ECMA-262) edition 3 language
specification defines the Error.toString() method that way, and ActionScript 3.0
defines it the same way for backward compatibility with that specification. Therefore,
when you override the Error.toString() method, you must match the parameters
exactly. You will not want to pass any parameters to your toString() method at run time,
because those parameters are ignored.
Responding to error events and status 269
■ Status-based error events
The status-based error events are related to the netStatus and status properties of
the networking and communication classes. If Flash Player encounters a problem
when reading or writing data, the value of the netStatus.info.level or
status.level properties (depending on the class object you’re using) is set to the
value "error". You respond to this error by checking if the level property contains
the value "error" in your event handler function.
Working with error events
The ErrorEvent class and its subclasses contain error types for handling errors dispatched by
Flash Player as it tries to read or write data.
The following example uses both a try..catch statement and error event handlers to display
any errors detected while trying to read a local file. You can add more sophisticated handling
code to provide a user with options or otherwise handle the error automatically in the places
indicated by the comment “your error-handling code here”:
package
{
import flash.display.Sprite;
import flash.errors.IOError;
import flash.events.IOErrorEvent;
import flash.events.TextEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.URLRequest;
import flash.text.TextField;
public class LinkEventExample extends Sprite
{
private var myMP3:Sound;
public function LinkEventExample()
{
myMP3 = new Sound();
var list:TextField = new TextField();
list.autoSize = TextFieldAutoSize.LEFT;
list.multiline = true;
list.htmlText = "<a href="event:track1.mp3">Track 1</a><br>";
list.htmlText += "<a href="event:track2.mp3">Track 2</a><br>";
addEventListener(TextEvent.LINK, linkHandler);
addChild(list);
}
private function playMP3(mp3:String):void
{
try
270 Handling errors
{
myMP3.load(new URLRequest(mp3));
myMP3.play();
}
catch (err:Error)
{
trace(err.message);
// your error-handling code here
}
myMP3.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
}
private function linkHandler(linkEvent:TextEvent):void
{
playMP3(linkEvent.text);
// your error-handling code here
}
private function errorHandler(errorEvent:IOErrorEvent):void
{
trace(errorEvent.text);
// your error-handling code here
}
}
}
Working with status change events
Flash Player dynamically changes the value of the netStatus.info.level or status.level
properties for the classes that support the level property. The classes that have the
netStatus.info.level property are NetConnection, NetStream, and SharedObject. The
classes that have the status.level property are HTTPStatusEvent, Camera, Microphone,
and LocalConnection. You can write a handler function to respond to the change in level
value and track communication errors.
The following example uses a netStatusHandler() function to test the value of the level
property. If the level property indicates that an error has been encountered, the code traces
the message “Video stream failed”.
package
{
import flash.display.Sprite;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
Responding to error events and status 271
public class VideoExample extends Sprite
{
private var videoUrl:String = "Video.flv";
private var connection:NetConnection;
private var stream:NetStream;
public function VideoExample()
{
connection = new NetConnection();
connection.addEventListener(NetStatusEvent.NET_STATUS,
netStatusHandler);
connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR,
securityErrorHandler);
connection.connect(null);
}
private function netStatusHandler(event:NetStatusEvent):void
{
if (event.info.level = "error")
{
trace("Video stream failed")
}
else
{
connectStream();
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void
{
trace("securityErrorHandler: " + event);
}
private function connectStream():void
{
var stream:NetStream = new NetStream(connection);
var video:Video = new Video();
video.attachNetStream(stream);
stream.play(videoUrl);
addChild(video);
}
}
}
272 Handling errors
Comparing the Error classes
ActionScript provides a number of predefined Error classes. Many of these classes are used by
Flash Player, but you can also use the same Error classes in your own code. There are two
main types of Error classes in ActionScript 3.0: ActionScript core Error classes and flash.error
package Error classes. The core Error classes are prescribed by the ECMAScript (ECMA-262)
edition 4 draft language specification. The flash.error package contents are additional classes
introduced to aid ActionScript 3.0 application development and debugging.
ECMAScript core Error classes
The ECMAScript core error classes include the Error, EvalError, RangeError, ReferenceError,
SyntaxError, TypeError, and URIError classes. Each of these classes are located in the top-level
namespace.
Class name Description Notes
Error The Error class can be used for
throwing exceptions, and is the
base class for the other
exception classes defined in
ECMAScript: EvalError,
RangeError, ReferenceError,
SyntaxError, TypeError, and
URIError.
The Error class serves as the base class
for all run-time errors thrown by Flash
Player, and is the recommended base
class for any custom error classes.
EvalError An EvalError exception is thrown
if any parameters are passed to
the Function class’s constructor
or if user code calls the eval()
function.
In ActionScript 3.0, support for the
eval() function has been removed and
attempts to use the function cause an
error to be thrown.
Earlier versions of Flash Player used the
eval() function to access variables,
properties, objects, or movie clips by
name.
RangeError A RangeError exception is
thrown if a numeric value falls
outside of an acceptable range.
For example, a RangeError would be
thrown by the Timer class if a delay was
either negative or was not finite. A
RangeError could also be thrown if you
attempted to add a display object at an
invalid depth.
Comparing the Error classes 273
ReferenceError A ReferenceError exception is
thrown when a reference to an
undefined property is attempted
on a sealed (nondynamic) object.
Versions of the ActionScript
compiler before ActionScript 3.0
did not throw an error when
access was attempted to a
property that was undefined.
However, because the new
ECMAScript specification
specifies that an error should be
thrown in this condition,
ActionScript 3.0 throws the
ReferenceError exception.
Exceptions for undefined variables point
to potential bugs, helping you improve
software quality. However, if you are not
used to having to initialize your variables,
this new ActionScript behavior may
require some changes in your coding
habits.
SyntaxError A SyntaxError exception is
thrown when a parsing error
occurs in your ActionScript code.
For more information, see
Section 15.11.6.4 of the
ECMAScript (ECMA-262)
edition 3 (until edition 4 is
available) language specification
at www.ecma-international.org/
publications/standards/Ecma-
262.htm, as well as Section
10.3.1 of the ECMAScript for
XML (E4X) specification
(ECMA-357 edition 2) at
www.ecma-international.org/
publications/standards/Ecma-
357.htm.
A SyntaxError can be thrown under the
following circumstances:
• ActionScript throws SyntaxError
exceptions when an invalid regular
expression is parsed by the RegExp
class.
• ActionScript throws SyntaxError
exceptions when invalid XML is parsed
by the XMLDocument class.
Class name Description Notes
274 Handling errors
TypeError The TypeError exception is
thrown when the actual type of
an operand is different from the
expected type.
For more information, see
Section 15.11.6.5 of the
ECMAScript specification at
www.ecma-international.org/
publications/standards/Ecma-
262.htm, as well as Section 10.3
of the E4X specification at
www.ecma-international.org/
publications/standards/Ecma-
357.htm.
A TypeError can be thrown under the
following circumstances:
• An actual parameter of a function or
method could not be coerced to the
formal parameter type.
• A value is assigned to a variable and
cannot be coerced to the variable’s
type.
• The right side of the is or instanceof
operator is not a valid type.
• The super keyword is used illegally.
• A property lookup results in more than
one binding, and is therefore
ambiguous.
• A method is invoked on an
incompatible object. For example, a
TypeError exception is thrown if a
method in the RegExp class is
“grafted” onto a generic object and
then invoked.
URIError The URIError exception is thrown
when one of the global URI
handling functions is used in a
way that is incompatible with its
definition.
For more information, see
Section 15.11.6.6 of the
ECMAScript specification at
www.ecma-international.org/
publications/standards/Ecma-
262.htm.
A URIError can be thrown under the
following circumstances:
An invalid URI is specified for a Flash
Player API function that expects a valid
URI, such as Socket.connect().
Class name Description Notes
Comparing the Error classes 275
ActionScript core Error classes
In addition to the core ECMAScript Error classes, ActionScript adds several classes of its own
for ActionScript-specific error conditions and error-handling functionality.
Because these classes are ActionScript language extensions to ECMAScript edition 4 draft
language specification that could potentially be interesting additions to the draft specification,
they are kept at the top level instead of being placed in a package like flash.error.
Class name Description Notes
ArgumentError The ArgumentError class
represents an error that occurs
when the parameter values
supplied during a function call do
not match the parameters
defined for that function.
Some examples of argument errors
include the following:
• Too few or too many arguments
are supplied to a method.
• An argument was expected to be
a member of an enumeration and
was not.
SecurityError The SecurityError exception is
thrown when a security violation
takes place and access is denied.
Some examples of security errors
include the following:
• An unauthorized property access
or method call is made across a
security sandbox boundary.
• An attempt was made to access a
URL not permitted by the security
sandbox.
• A socket connection was
attempted to an unauthorized port
number—for example, a port below
1024—without a policy file
present.
• An attempt was made to access
the user's camera or microphone,
and the request to access the
device was denied by the user.
VerifyError A VerifyError exception is thrown
when a malformed or corrupted
SWF file is encountered.
When a SWF file loads another
SWF file, the parent SWF can catch
a VerifyError generated by the
loaded SWF.
276 Handling errors
flash.error package Error classes
The flash.error package contains Error classes that are considered part of the Flash Player API.
In contrast to the Error classes just described, the flash.error package communicates errors
events that are specific to Flash Player.
Class name Description Notes
EOFError An EOFError exception is thrown
when you attempt to read past
the end of the available data.
For example, an EOFError is thrown
when one of the read methods in the
IDataInput interface is called and
there is insufficient data to satisfy
the read request.
IllegalOperationError An IllegalOperationError
exception is thrown when a
method is not implemented or
the implementation doesn't cover
the current usage.
Examples of illegal operation error
exceptions include the following:
• A base class, such as
DisplayObjectContainer, provides
more functionality than the Stage
can support. For example, if you
attempt to get or set a mask on
the Stage (using stage.mask),
Flash Player will throw an
IllegalOperationError with the
message “The Stage class does
not implement this property or
method.”
• A subclass inherits a method it
does not require and does not
want to support.
• Certain accessibility methods are
called when Flash Player is
compiled without accessibility
support.
• Authoring-only features are
invoked from a run-time version of
Flash Player.
• You attempt to set the name of an
object placed on the timeline.
IOError An IOError exception is thrown
when some type of I/O exception
occurs.
You get this error, for example,
when a read-write operation is
attempted on a socket that is not
connected or that has become
disconnected.
Comparing the Error classes 277
MemoryError A MemoryError exception is
thrown when a memory
allocation request fails.
By default, ActionScript Virtual
Machine 2 does not impose a limit
on how much memory an
ActionScript program may allocate.
On a desktop PC, memory
allocation failures are infrequent.
You see an error thrown when the
system is unable to allocate the
memory required for an operation.
So, on a desktop PC, this exception
is rare unless an allocation request
is extremely large; for example, a
request for 3 billion bytes is
impossible because a 32-bit
Microsoft® Windows® program can
access only 2 GB of address space.
ScriptTimeoutError A ScriptTimeoutError exception
is thrown when a script timeout
interval of 15 seconds is reached.
By catching a
ScriptTimeoutError exception,
you can handle the script timeout
more gracefully. If there is no
exception handler, the uncaught
exception handler will display a
dialog box with an error
message.
To prevent a malicious developer
from catching the exception and
staying in an infinite loop, only the
first ScriptTimeoutError exception
thrown in the course of a particular
script can be caught. A subsequent
ScriptTimeoutError exception
cannot be caught by your code and
will immediately go to the uncaught
exception handler.
StackOverflowError The StackOverflowError
exception is thrown when the
stack available to the script has
been exhausted.
A StackOverflowError exception
might indicate that infinite recursion
has occurred.
Class name Description Notes
278 Handling errors
Example: CustomErrors application
The CustomErrors application demonstrates techniques for working with custom errors when
building an application. These techniques are:
■ Validating an XML packet
■ Writing a custom error
■ Throwing custom errors
■ Notifying users when an error is thrown
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The CustomErrors application files can be found in
the Samples/CustomError folder. The application consists of the following files:
CustomErrors application overview
The CustomErrors.mxml file contains the user interface and some logic for the custom error
application. Once the application’s creationComplete event is dispatched, the initApp()
method is invoked. This method defines a sample XML packet that will be verified by the
Validator class. The following code shows the initApp() method:
private function initApp():void
{
employeeXML =
<employee id="12345">
<firstName>John</firstName>
File Description
CustomErrors.mxml
or
CustomErrors.fla
The main application file in Flash (FLA) or Flex
(MXML)
com/example/programmingas3/errors/
ApplicationError.as
A class that serves as the base error class for
both the FatalError and WarningError classes.
com/example/programmingas3/errors/
FatalError.as
A class that defines a FatalError error that can
be thrown by the application. This class extends
the custom ApplicationError class.
com/example/programmingas3/errors/
Validator.as
A class that defines a single method that
validates a user-supplied employee XML
packet.
com/example/programmingas3/errors/
WarningError.as
A class that defines a WarningError error that
can be thrown by the application. This class
extends the custom ApplicationError class.
Example: CustomErrors application 279
<lastName>Doe</lastName>
<costCenter>12345</costCenter>
<costCenter>67890</costCenter>
</employee>;
}
The XML packet is later displayed in a TextArea component instance on the Stage. This
allows you to modify the XML packet before attempting to revalidate it.
When the user clicks the Validate button, the validateData() method is called. This
method validates the employee XML packet using the validateEmployeeXML() method in
the Validator class. The following code shows the validateData() method:
public function validateData():void
{
try
{
var tempXML:XML = XML(xmlText.text);
Validator.validateEmployeeXML(tempXML);
status.text = "The XML was successfully validated.";
}
catch (error:FatalError)
{
showFatalError(error);
}
catch (error:WarningError)
{
showWarningError(error);
}
catch (error:Error)
{
showGenericError(error);
}
}
First, a temporary XML object is created using the contents of the TextArea component
instance xmlText. Next, the validateEmployeeXML() method in the custom Validator class
(com.example.programmingas3/errors/Validator.as) is invoked and passes the temporary
XML object as a parameter. If the XML packet is valid, the status Label component instance
displays a success message and the application exits. If the validateEmployeeXML() method
threw a custom error (that is, a FatalError, WarningError, or a generic Error occurred), the
appropriate catch statement executes and calls either the showFatalError(),
showWarningError(), or showGenericError() methods. Each of these methods displays an
appropriate message in an Alert component to notify the user of the specific error that
occurred. Each method also updates the status Label component instance with a specific
message.
280 Handling errors
If a fatal error occurs during an attempt to validate the employee XML packet, the error
message is displayed in an Alert component, and the xmlText TextArea component instance
and validateBtn Button component instance are disabled, as the following code shows:
public function showFatalError(error:FatalError):void
{
var message:String = error.message + "nn" + "Click OK to end.";
var title:String = error.getTitle();
Alert.show(message, title);
status.text = "This application has ended.";
this.xmlText.enabled = false;
this.validateBtn.enabled = false;
}
If a warning error instead of a fatal error occurs, the error message is displayed in an Alert
component instance, but the TextField and Button component instances aren’t disabled. The
showWarningError() method displays the custom error message in the Alert component
instance. The message also asks the user to decide if they want to proceed with validating the
XML or abort the script. The following excerpt shows the showWarningError() method:
public function showWarningError(error:WarningError):void
{
var message:String = error.message + "nn" + "Do you want to exit this
application?";
var title:String = error.getTitle();
Alert.show(message, title, Alert.YES | Alert.NO, null, closeHandler);
status.text = message;
}
When the user closes the Alert component instance by using either the Yes or No button, the
closeHandler() method is invoked. The following excerpt shows the closeHandler()
method:
private function closeHandler(event:CloseEvent):void
{
switch (event.detail)
{
case Alert.YES:
showFatalError(new FatalError(9999));
break;
case Alert.NO:
break;
}
}
If the user chooses to abort the script by clicking Yes in the warning error Alert dialog, a
FatalError is thrown, causing the application to terminate.
Example: CustomErrors application 281
Building a custom validator
The custom Validator class contains a single method, validateEmployeeXML(). The
validateEmployeeXML() method takes a single argument, employee, which is the XML
packet that you wish to validate. The validateEmployeeXML() method is as follows:
public static function validateEmployeeXML(employee:XML):void
{
// checks for the integrity of items in the XML
if (employee.costCenter.length() < 1)
{
throw new FatalError(9000);
}
if (employee.costCenter.length() > 1)
{
throw new WarningError(9001);
}
if (employee.ssn.length() != 1)
{
throw new FatalError(9002);
}
}
To be validated, an employee must belong to one (and only one) cost center. If the employee
doesn’t belong to any cost centers, the method throws a FatalError, which bubbles up to the
validateData() method in the main application file. If the employee belongs to more than
one cost center, a WarningError is thrown. The final check in the XML validator is that the
user has exactly one social security number defined (the ssn node in the XML packet). If
there is not exactly one ssn node, a FatalError error is thrown.
You can add additional checks to the validateEmployeeXML() method—for instance, to
ensure that the ssn node contains a valid number, or that the employee has at least one phone
number and e-mail address defined, and that both values are valid. You can also modify the
XML so that each employee has a unique ID and specifies the ID of their manager.
Defining the ApplicationError class
The ApplicationError class serves as the base class for both the FatalError and WarningError
classes. The ApplicationError class extends the Error class, and defines its own custom
methods and properties, including defining an error ID, severity, and an XML object that
contains the custom error codes and messages. This class also defines two static constants that
are used to define the severity of each error type.
282 Handling errors
The ApplicationError class’s constructor method is as follows:
public function ApplicationError()
{
messages =
<errors>
<error code="9000">
<![CDATA[Employee must be assigned to a cost center.]]>
</error>
<error code="9001">
<![CDATA[Employee must be assigned to only one cost center.]]>
</error>
<error code="9002">
<![CDATA[Employee must have one and only one SSN.]]>
</error>
<error code="9999">
<![CDATA[The application has been stopped.]]>
</error>
</errors>;
}
Each error node in the XML object contains a unique numeric code and an error message.
Error messages can be easily looked up by their error code using E4X, as seen in the following
getMessageText() method:
public function getMessageText(id:int):String
{
var message:XMLList = messages.error.(@code == id);
return message[0].text();
}
The getMessageText() method takes a single integer argument, id, and returns a string.
The id argument is the error code for the error to look up. For example, passing an id of
9001 retrieves the error saying that employees must be assigned to only one cost center. If
more than one error has the same error code, ActionScript returns the error message only for
the first result found (message[0] in the returned XMLList object).
The next method in this class, getTitle(), doesn’t take any parameters and returns a string
value that contains the error ID for this specific error. This value is used in the Alert
component’s title to help you easily identify the exact error that occurred during validation of
the XML packet. The following excerpt shows the getTitle() method:
public function getTitle():String
{
return "Error #" + id;
}
Example: CustomErrors application 283
The final method in the ApplicationError class is toString(). This method overrides the
function defined in the Error class so that you can customize the presentation of the error
message. The method returns a string that identifies the specific error number and message
that occurred.
public override function toString():String
{
return "[APPLICATION ERROR #" + id + "] " + message;
}
Defining the FatalError class
The FatalError class extends the custom ApplicationError class and defines three methods: the
FatalError constructor, getTitle(), and toString(). The first method, the FatalError
constructor, takes a single integer argument, errorID, and sets the error’s severity using the
static constant values defined in the ApplicationError class, and gets the specific error’s error
message by calling the getMessageText() method in the ApplicationError class. The
FatalError constructor is as follows:
public function FatalError(errorID:int)
{
id = errorID;
severity = ApplicationError.FATAL;
message = getMessageText(errorID);
}
The next method in the FatalError class, getTitle(), overrides the getTitle() method
defined earlier in the ApplicationError class, and appends the text “-- FATAL” in the title to
inform the user that a fatal error has occurred. The getTitle() method is as follows:
public override function getTitle():String
{
return "Error #" + id + " -- FATAL";
}
The final method in this class, toString(), overrides the toString() method defined in the
ApplicationError class. The toString() method is
public override function toString():String
{
return "[FATAL ERROR #" + id + "] " + message;
}
284 Handling errors
Defining the WarningError class
The WarningError class extends the ApplicationError class and is nearly identical to the
FatalError class, except for a couple minor string changes and sets the error severity to
ApplicationError.WARNING instead of ApplicationError.FATAL, as seen in the following
code:
public function WarningError(errorID:int)
{
id = errorID;
severity = ApplicationError.WARNING;
message = super.getMessageText(errorID);
}
285
9
CHAPTER 9
Using regular expressions
A regular expression describes a pattern that is used to find and manipulate matching text in
strings. Regular expressions resemble strings, but they can include special codes to describe
patterns and repetition. For example, the following regular expression matches a string that
starts with the character A followed by one or more sequential digits:
/Ad+/
This chapter describes the basic syntax for constructing regular expressions. However, regular
expressions can have many complexities and nuances. You can find detailed resources on
regular expressions on the web and in bookstores. Keep in mind that different programming
environments implement regular expressions in different ways. ActionScript 3.0 implements
regular expressions as defined in the ECMAScript edition 3 language specification (ECMA-
262).
Contents
Basics of regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .286
Regular expression syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .288
Methods for using regular expressions with strings . . . . . . . . . . . . . . . . . . . . . . . . . 305
Example: A Wiki parser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
286 Using regular expressions
Basics of regular expressions
Introduction to using regular expressions
A regular expression describes a pattern of characters. Regular expressions are typically used to
verify that a text value conforms to a particular pattern (such as verifying that a user-entered
phone number has the proper number of digits) or to replace portions of a text value that
matches a particular pattern.
Regular expressions can be simple. For example, suppose you wanted to confirm that a
particular string matches “ABC,” or wanted to replace every occurrence of “ABC” in a string
with some other text. In that case, you could use the following regular expression, which
defines the pattern consisting of the letters A, B, and C in sequence:
/ABC/
Note that the regular expression literal is delineated with the forward slash (/) character.
Regular expression patterns can also be complex, and sometimes cryptic in appearance, such
as the following expression to match a valid e-mail address:
/([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}/
Most commonly you will use regular expressions to search for patterns in strings and to
replace characters. In those cases, you will create a regular expression object and use it as a
parameter for one of several String class methods. The following methods of the String class
take regular expressions as parameters: match(), replace(), search(), and split(). For
more information on these methods, see “Finding patterns in strings and replacing substrings”
on page 212.
The RegExp class includes the following methods: test() and exec(). For more
information, see “Methods for using regular expressions with strings” on page 305.
Common regular expression tasks
There are several common uses for regular expressions, which are described in detail in this
chapter:
■ Creating a regular expression pattern
■ Using special characters in patterns
■ Identifying sequences of multiple characters (such as “a two-digit number” or “between
seven and ten letters”)
■ Identifying any character in a range of letters or numbers (such as “any letter from a to m”)
■ Identifying a character in a set of possible characters
Basics of regular expressions 287
■ Identifying subsequences (segments within a pattern)
■ Matching and replacing text based on patterns
Important concepts and terms
The following reference list contains important terms used in this chapter:
■ Escape character: A character indicating that the character that follows should be treated
as a metacharacter rather than a literal character. In regular expression syntax, the
backslash character () is the escape character, so a backslash followed by another character
is a special code rather than just the character itself.
■ Flag: A character that specifies some option about how the regular expression pattern
should be used, such as whether to distinguish between uppercase and lowercase
characters.
■ Metacharacter: A character that has special meaning in a regular expression pattern, as
opposed to literally representing that character in the pattern.
■ Quantifier: A character (or several characters) indicating how many times a part of the
pattern should repeat. For example, a quantifier would be used to designate that a United
States postal code should contain five or nine numbers.
■ Regular expression: A program statement defining a pattern of characters that can be used
to confirm whether other strings match that pattern or to replace portions of a string.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because the code listings in this chapter consist primarily of regular
expression patterns, testing the examples involves a few steps:
1. Create a new Flash document.
2. Select a keyframe and open the Actions panel.
3. Create a RegExp (regular expression) variable such as this one:
var pattern:RegExp = /ABC/;
4. Copy the pattern from the example and assign it as the value of your RegExp variable. For
instance, in the previous line of code, the pattern is the part of the code to the right of the
equals sign, not including the semicolon (/ABC/).
5. Create one or more String variables containing strings appropriate for testing your regular
expression. For instance, if you are creating a regular expression to test for valid e-mail
addresses, create a few String variables containing valid and bad e-mail addresses:
288 Using regular expressions
var goodEmail:String = "bob@example.com";
var badEmail:String = "5@$2.99";
6. Add lines of code to test the String variables to determine whether they match the regular
expression pattern. These will be the values that you’ll want to output to the screen using
the trace() function or by writing them to a text field on the Stage.
trace(goodEmail, " is valid:", pattern.test(goodEmail));
trace(badEmail, " is valid:", pattern.test(badEmail));
For instance, assuming pattern defines the regular expression pattern for a valid e-mail
address, the preceding lines of code writes this text to the Output panel:
bob@example.com is valid: true
5@$2.99 is valid: false
For more information about testing values by writing the values into a text field instance
on the Stage or by using the trace() function to print the values to the Output panel, see
“Testing in-chapter example code listings” on page 63.
Regular expression syntax
This section describes all of the elements of ActionScript regular expression syntax. As you’ll
see, regular expressions can have many complexities and nuances. You can find detailed
resources on regular expressions on the web and in bookstores. Keep in mind that different
programming environments implement regular expressions in different ways. ActionScript
3.0 implements regular expressions as defined in the ECMAScript edition 3 language
specification (ECMA-262).
Generally, you use regular expressions that match more complicated patterns than a simple
string of characters. For example, the following regular expression defines the pattern
consisting of the letters A, B, and C in sequence followed by any digit:
/ABCd/
The d code represents “any digit.” The backslash () character is called the escape character,
and combined with the character that follows it (in this case the letter d), it has special
meaning in the regular expression. This chapter describes these escape character sequences
and other regular expression syntax features.
The following regular expression defines the pattern of the letters ABC followed by any
number of digits (note the asterisk):
/ABCd*/
Regular expression syntax 289
The asterisk character (*) is a metacharacter. A metacharacter is a character that has special
meaning in regular expressions. The asterisk is a specific type of metacharacter called a
quantifier, which is used to quantify the amount of repetition of a character or group of
characters. For more information, see “Quantifiers” on page 295.
In addition to its pattern, a regular expression can contain flags, which specify how the regular
expression is to be matched. For example, the following regular expression uses the i flag,
which specifies that the regular expression ignores case sensitivity in matching strings:
/ABCd*/i
For more information, see “Flags and properties” on page 301.
You can use regular expressions with the following methods of the String class: match(),
replace(), and search(). For more information on these methods, see “Finding patterns in
strings and replacing substrings” on page 212.
Creating an instance of a regular expression
There are two ways to create a regular expression instance. One way uses forward slash
characters (/) to delineate the regular expression; the other uses the new constructor. For
example, the following regular expressions are equivalent:
var pattern1:RegExp = /bob/i;
var pattern2:RegExp = new RegExp("bob", "i");
Forward slashes delineate a regular expression literal in the same way as quotation marks
delineate a string literal. The part of the regular expression within the forward slashes defines
the pattern. The regular expression can also include flags after the final delineating slash. These
flags are considered to be part of the regular expression, but they are separate from its pattern.
When using the new constructor, you use two strings to define the regular expression. The
first string defines the pattern, and the second string defines the flags, as in the following
example:
var pattern2:RegExp = new RegExp("bob", "i");
When including a forward slash within a regular expression that is defined by using the
forward slash delineators, you must precede the forward slash with the backslash () escape
character. For example, the following regular expression matches the pattern 1/2:
var pattern:RegExp = /1/2/;
To include quotation marks within a regular expression that is defined with the new
constructor, you must add backslash () escape character before the quotation marks (just as
you would when defining any String literal). For example, the following regular expressions
match the pattern eat at "joe's":
290 Using regular expressions
var pattern1:RegExp = new RegExp("eat at "joe's"", "");
var pattern2:RegExp = new RegExp('eat at "joe's"', "");
Do not use the backslash escape character with quotation marks in regular expressions that are
defined by using the forward slash delineators. Similarly, do not use the escape character with
forward slashes in regular expressions that are defined with the new constructor. The following
regular expressions are equivalent, and they define the pattern 1/2 "joe's":
var pattern1:RegExp = /1/2 "joe's"/;
var pattern2:RegExp = new RegExp("1/2 "joe's"", "");
var pattern3:RegExp = new RegExp('1/2 "joe's"', '');
Also, in a regular expression that is defined with the new constructor, to use a metasequence
that begins with the backslash () character, such as d (which matches any digit), type the
backslash character twice:
var pattern:RegExp = new RegExp("d+", ""); // matches one or more digits
You must type the backlash character twice in this case, because the first parameter of the
RegExp() constructor method is a string, and in a string literal you must type a backslash
character twice to have it recognized as a single backslash character.
The sections that follow describe syntax for defining regular expression patterns.
For more information on flags, see “Flags and properties” on page 301.
Characters, metacharacters, and metasequences
The simplest regular expression is one that matches a sequence of characters, as in the
following example:
var pattern:RegExp = /hello/;
However, the following characters, known as metacharacters, have special meanings in regular
expressions:
^ $  . * + ? ( ) [ ] { } |
For example, the following regular expression matches the letter A followed by zero or more
instances of the letter B (the asterisk metacharacter indicates this repetition), followed by the
letter C:
/AB*C/
To include a metacharacter without its special meaning in a regular expression pattern, you
must use the backslash () escape character. For example, the following regular expression
matches the letter A followed by the letter B, followed by an asterisk, followed by the letter C:
var pattern:RegExp = /AB*C/;
Regular expression syntax 291
A metasequence, like a metacharacter, has special meaning in a regular expression. A
metasequence is made up of more than one character. The following sections provide details
on using metacharacters and metasequences.
About metacharacters
The following table summarizes the metacharacters that you can use in regular expressions:
Metacharacter Description
^ (caret) Matches at the start of the string. With the m (multiline) flag set, the
caret matches the start of a line as well (see “The m (multiline) flag”
on page 302). Note that when used at the start of a character class, the
caret indicates negation, not the start of a string. For more information,
see“Character classes” on page 293.
$ (dollar sign) Matches at the end of the string. With the m (multiline) flag set, $
matches the position before a newline (n) character as well. For more
information, see “The m (multiline) flag” on page 302.
 (backslash) Escapes the special metacharacter meaning of special characters.
Also, use the backslash character if you want to use a forward slash
character in a regular expression literal, as in /1/2/ (to match the
character 1, followed by the forward slash character, followed by the
character 2).
. (dot) Matches any single character.
A dot matches a newline character (n) only if the s (dotall) flag is set.
For more information, see “The s (dotall) flag” on page 303.
* (star) Matches the previous item repeated zero or more times.
For more information, see “Quantifiers” on page 295.
+ (plus) Matches the previous item repeated one or more times.
For more information, see “Quantifiers” on page 295.
? (question mark) Matches the previous item repeated zero times or one time.
For more information, see “Quantifiers” on page 295.
( and ) Defines groups within the regular expression. Use groups for the
following:
• To confine the scope of the | alternator: /(a|b|c)d/
• To define the scope of a quantifier: /(walla.){1,2}/
• In backreferences. For example, the 1 in the following regular
expression matches whatever matched the first parenthetical group of
the pattern:
/(w*) is repeated: 1/
For more information, see “Groups” on page 297.
292 Using regular expressions
About metasequences
Metasequences are sequences of characters that have special meaning in a regular expression
pattern. The following table describes these metasequences:
[ and ] Defines a character class, which defines possible matches for a single
character:
/[aeiou]/ matches any one of the specified characters.
Within character classes, use the hyphen (-) to designate a range of
characters:
/[A-Z0-9]/ matches uppercase A through Z or 0 through 9.
Within character classes, insert a backslash to escape the ] and
- characters:
/[+-]d+/ matches either + or - before one or more digits.
Within character classes, other characters, which are normally
metacharacters, are treated as normal characters (not metacharacters),
without the need for a backslash:
/[$£]/ matches either $ or £.
For more information, see “Character classes” on page 293.
| (pipe) Used for alternation, to match either the part on the left side or the part
on the right side:
/abc|xyz/ matches either abc or xyz.
Metasequence Description
{n}
{n,}
and
{n,n}
Specifies a numeric quantifier or quantifier range for the previous item:
/A{27}/ matches the character A repeated 27 times.
/A{3,}/ matches the character A repeated 3 or more times.
/A{3,5}/ matches the character A repeated 3 to 5 times.
For more information, see “Quantifiers” on page 295.
b Matches at the position between a word character and a nonword
character. If the first or last character in the string is a word character,
also matches the start or end of the string.
B Matches at the position between two word characters. Also matches the
position between two nonword characters.
d Matches a decimal digit.
D Matches any character other than a digit.
f Matches a form feed character.
n Matches the newline character.
r Matches the return character.
Metacharacter Description
Regular expression syntax 293
Character classes
You use character classes to specify a list of characters to match one position in the regular
expression. You define character classes with square brackets ( [ and ] ). For example, the
following regular expression defines a character class that matches bag, beg, big, bog, or bug:
/b[aeiou]g/
Escape sequences in character classes
Most metacharacters and metasequences that normally have special meanings in a regular
expression do not have those same meanings inside a character class. For example, in a regular
expression, the asterisk is used for repetition, but this is not the case when the asterisk appears
in a character class. The following character class matches the asterisk literally, along with any
of the other characters listed:
/[abc*123]/
s Matches any white-space character (a space, tab, newline, or return
character).
S Matches any character other than a white-space character.
t Matches the tab character.
unnnn Matches the Unicode character with the character code specified by the
hexadecimal number nnnn. For example, u263a is the smiley character.
v Matches a vertical feed character.
w Matches a word character (A–Z, a–z, 0-9, or _). Note that w does not
match non-English characters, such as é, ñ, or ç.
W Matches any character other than a word character.
xnn Matches the character with the specified ASCII value, as defined by the
hexadecimal number nn.
Metasequence Description
294 Using regular expressions
However, the three characters listed in the following table do function as metacharacters, with
special meaning, in character classes:
For any of these characters to be recognized as literal characters (without the special
metacharacter meaning), you must precede the character with the backslash escape character.
For example, the following regular expression includes a character class that matches any one
of four symbols ($, , ], or -):
/[$]-]/
In addition to the metacharacters that retain their special meanings, the following
metasequences function as metasequences within character classes:
Other regular expression metasequences and metacharacters are treated as normal characters
within a character class.
Ranges of characters in character classes
Use the hyphen to specify a range of characters, such as A-Z, a-z, or 0-9. These characters
must constitute a valid range in the character set. For example, the following character class
matches any one of the characters in the range a-z or any digit:
/[a-z0-9]/
Metacharacter Meaning in character classes
] Defines the end of the character class.
- Defines a range of characters (see “Ranges of characters in
character classes” on page 294).
 Defines metasequences and undoes the special meaning of
metacharacters.
Metasequence Meaning in character classes
n Matches a newline character.
r Matches a return character.
t Matches a tab character.
unnnn Matches the character with the specified Unicode code point value
(as defined by the hexadecimal number nnnn).
xnn Matches the character with the specified ASCII value (as defined by
the hexadecimal number nn).
Regular expression syntax 295
You can also use the xnn ASCII character code to specify a range by ASCII value. For
example, the following character class matches any character from a set of extended ASCII
characters (such as é and ê):
/[x80-x9A]/
Negated character classes
When you use a caret (^) character at the beginning of a character class, it negates that class—
any character not listed is considered a match. The following character class matches any
character except for a lowercase letter (a–z) or a digit:
/[^a-z0-9]/
You must type the caret (^) character at the beginning of a character class to indicate negation.
Otherwise, you are simply adding the caret character to the characters in the character class.
For example, the following character class matches any one of a number of symbol characters,
including the caret:
/[!.,#+*%$&^]/
Quantifiers
You use quantifiers to specify repetitions of characters or sequences in patterns, as follows:
You can apply a quantifier to a single character, to a character class, or to a group:
■ /a+/ matches the character a repeated one or more times.
■ /d+/ matches one or more digits.
■ /[abc]+/ matches a repetition of one or more character, each of which is either a, b, or c.
■ /(very, )*/ matches the word very followed by a comma and a space repeated zero or
more times.
Quantifier
metacharacter
Description
* (star) Matches the previous item repeated zero or more times.
+ (plus) Matches the previous item repeated one or more times.
? (question mark) Matches the previous item repeated zero times or one time.
{n}
{n,}
and
{n,n}
Specifies a numeric quantifier or quantifier range for the previous item:
/A{27}/ matches the character A repeated 27 times.
/A{3,}/ matches the character A repeated 3 or more times.
/A{3,5}/ matches the character A repeated 3 to 5 times.
296 Using regular expressions
You can use quantifiers within parenthetical groupings that have quantifiers applied to them.
For example, the following quantifier matches strings such as word and word-word-word:
/w+(-w+)*/
By default, regular expressions perform what is known as greedy matching. Any subpattern in
the regular expression (such as .*) tries to match as many characters in the string as possible
before moving forward to the next part of the regular expression. For example, consider the
following regular expression and string:
var pattern:RegExp = /<p>.*</p>/;
str:String = "<p>Paragraph 1</p> <p>Paragraph 2</p>";
The regular expression matches the entire string:
<p>Paragraph 1</p> <p>Paragraph 2</p>
Suppose, however, that you want to match only one <p>...</p> grouping. You can do this
with the following:
<p>Paragraph 1</p>
Add a question mark (?) after any quantifier to change it to what is known as a lazy quantifier.
For example, the following regular expression, which uses the lazy *? quantifier, matches <p>
followed by the minimum number of characters possible (lazy), followed by </p>:
/<p>.*?</p>/
Keep in mind the following points about quantifiers:
■ The quantifiers {0} and {0,0} do not exclude an item from a match.
■ Do not combine multiple quantifiers, as in /abc+*/.
■ The dot (.) does not span lines unless the s (dotall) flag is set, even if it is followed by a *
quantifier. For example, consider the following code:
var str:String = "<p>Testn";
str += "Multiline</p>";
var re:RegExp = /<p>.*</p>/;
trace(str.match(re)); // null;
re = /<p>.*</p>/s;
trace(str.match(re));
// output: <p>Test
// Multiline</p>
For more information, see “The s (dotall) flag” on page 303.
Regular expression syntax 297
Alternation
Use the | (pipe) character in a regular expression to have the regular expression engine
consider alternatives for a match. For example, the following regular expression matches any
one of the words cat, dog, pig, rat:
var pattern:RegExp = /cat|dog|pig|rat/;
You can use parentheses to define groups to restrict the scope of the | alternator. The following
regular expression matches cat followed by nap or nip:
var pattern:RegExp = /cat(nap|nip)/;
For more information, see “Groups” on page 297.
The following two regular expressions, one using the | alternator, the other using a character
class (defined with [ and ] ), are equivalent:
/1|3|5|7|9/
/[13579]/
For more information, see “Character classes” on page 293.
Groups
You can specify a group in a regular expression by using parentheses, as follows:
/class-(d*)/
A group is a subsection of a pattern. You can use groups to do the following things:
■ Apply a quantifier to more than one character.
■ Delineate subpatterns to be applied with alternation (by using the | character).
■ Capture substring matches (for example, by using 1 in a regular expression to match a
previously matched group, or by using $1 similarly in the replace() method of the
String class).
The following sections provide details on these uses of groups.
Using groups with quantifiers
If you do not use a group, a quantifier applies to the character or character class that precedes
it, as the following shows:
var pattern:RegExp = /ab*/ ;
// matches the character a followed by
// zero or more occurrences of the character b
pattern = /ad+/;
// matches the character a followed by
// one or more digits
298 Using regular expressions
pattern = /a[123]{1,3}/;
// matches the character a followed by
// one to three occurrences of either 1, 2, or 3
However, you can use a group to apply a quantifier to more than one character or character
class:
var pattern:RegExp = /(ab)*/;
// matches zero or more occurrences of the character a
// followed by the character b, such as ababab
pattern = /(ad)+/;
// matches one or more occurrences of the character a followed by
// a digit, such as a1a5a8a3
pattern = /(spam ){1,3}/;
// matches 1 to 3 occurrences of the word spam followed by a space
For more information on quantifiers, see “Quantifiers” on page 295.
Using groups with the alternator (|) character
You can use groups to define the group of characters to which you want to apply an alternator
(|) character, as follows:
var pattern:RegExp = /cat|dog/;
// matches cat or dog
pattern = /ca(t|d)og/;
// matches catog or cadog
Using groups to capture substring matches
When you define a standard parenthetical group in a pattern, you can later refer to it in the
regular expression. This is known as a backreference, and these sorts of groups are known as
capturing groups. For example, in the following regular expression, the sequence 1 matches
whatever substring matched the capturing parenthetical group:
var pattern:RegExp = /(d+)-by-1/;
// matches the following: 48-by-48
You can specify up to 99 of these backreferences in a regular expression by typing
1, 2, ... , 99.
Regular expression syntax 299
Similarly, in the replace() method of the String class, you can use $1–$99 to insert captured
group substring matches in the replacement string:
var pattern:RegExp = /Hi, (w+)./;
var str:String = "Hi, Bob.";
trace(str.replace(pattern, "$1, hello."));
// output: Bob, hello.
Also, if you use capturing groups, the exec() method of the RegExp class and the match()
method of the String class return substrings that match the capturing groups:
var pattern:RegExp = /(w+)@(w+).(w+)/;
var str:String = "bob@example.com";
trace(pattern.exec(str));
// bob@example.com,bob,example,com
Using noncapturing groups and lookahead groups
A noncapturing group is one that is used for grouping only; it is not “collected,” and it does
not match numbered backreferences. Use (?: and ) to define noncapturing groups, as
follows:
var pattern = /(?:com|org|net);
For example, note the difference between putting (com|org) in a capturing versus a
noncapturing group (the exec() method lists capturing groups after the complete match):
var pattern:RegExp = /(w+)@(w+).(com|org)/;
var str:String = "bob@example.com";
trace(pattern.exec(str));
// bob@example.com,bob,example,com
//noncapturing:
var pattern:RegExp = /(w+)@(w+).(?:com|org)/;
var str:String = "bob@example.com";
trace(pattern.exec(str));
// bob@example.com,bob,example
A special type of noncapturing group is the lookahead group, of which there are two types: the
positive lookahead group and the negative lookahead group.
Use (?= and ) to define a positive lookahead group, which specifies that the subpattern in the
group must match at the position. However, the portion of the string that matches the
positive lookahead group can match remaining patterns in the regular expression. For
example, because (?=e) is a positive lookahead group in the following code, the character e
that it matches can be matched by a subsequent part of the regular expression—in this case,
the capturing group, w*):
300 Using regular expressions
var pattern:RegExp = /sh(?=e)(w*)/i;
var str:String = "Shelly sells seashells by the seashore";
trace(pattern.exec(str));
// Shelly,elly
Use (?! and ) to define a negative lookahead group that specifies that the subpattern in the
group must not match at the position. For example:
var pattern:RegExp = /sh(?!e)(w*)/i;
var str:String = "She sells seashells by the seashore";
trace(pattern.exec(str));
// shore,ore
Using named groups
A named group is a type of group in a regular expression that is given a named identifier. Use
(?P<name> and ) to define the named group. For example, the following regular expression
includes a named group with the identifier named digits:
var pattern = /[a-z]+(?P<digits>d+)[a-z]+/;
When you use the exec() method, a matching named group is added as a property of the
result array:
var myPattern:RegExp = /([a-z]+)(?P<digits>d+)[a-z]+/;
var str:String = "a123bcd";
var result:Array = myPattern.exec(str);
trace(result.digits); // 123
Here is another example, which uses two named groups, with the identifiers name and dom:
var emailPattern:RegExp =
/(?P<name>(w|[_.-])+)@(?P<dom>((w|-)+))+.w{2,4}+/;
var address:String = "bob@example.com";
var result:Array = emailPattern.exec(address);
trace(result.name); // bob
trace(result.dom); // example
NOTE
Named groups are not part of the ECMAScript language specification. They are an
added feature in ActionScript 3.0.
Regular expression syntax 301
Flags and properties
The following table lists the five flags that you can set for regular expressions. Each flag can be
accessed as a property of the regular expression object.
Note that these properties are read-only. You can set the flags (g, i, m, s, x) when you set a
regular expression variable, as follows:
var re:RegExp = /abc/gimsx;
However, you cannot directly set the named properties. For instance, the following code
results in an error:
var re:RegExp = /abc/;
re.global = true; // This generates an error.
By default, unless you specify them in the regular expression declaration, the flags are not set,
and the corresponding properties are also set to false.
Additionally, there are two other properties of a regular expression:
■ The lastIndex property specifies the index position in the string to use for the next call
to the exec() or test() method of a regular expression.
■ The source property specifies the string that defines the pattern portion of the regular
expression.
The g (global) flag
When the g (global) flag is not included, a regular expression matches no more than one
match. For example, with the g flag not included in the regular expression, the
String.match() method returns only one matching substring:
var str:String = "she sells seashells by the seashore.";
var pattern:RegExp = /shw*/;
trace(str.match(pattern)) // output: she
Flag Property Description
g global Matches more than one match.
i ignoreCase Case-insensitive matching. Applies to the A—Z and a—z characters,
but not to extended characters such as É and é.
m multiline With this flag set, $ and ^ can match the beginning of a line and end
of a line, respectively.
s dotall With this flag set, . (dot) can match the newline character (n).
x extended Allows extended regular expressions. You can type spaces in the
regular expression, which are ignored as part of the pattern. This lets
you type regular expression code more legibly.
302 Using regular expressions
When the g flag is set, the Sting.match() method returns multiple matches, as follows:
var str:String = "she sells seashells by the seashore.";
var pattern:RegExp = /shw*/g;
// The same pattern, but this time the g flag IS set.
trace(str.match(pattern)); // output: she,shells,shore
The i (ignoreCase) flag
By default, regular expression matches are case-sensitive. When you set the i (ignoreCase)
flag, case sensitivity is ignored. For example, the lowercase s in the regular expression does not
match the uppercase letter S, the first character of the string:
var str:String = "She sells seashells by the seashore.";
trace(str.search(/sh/)); // output: 13 -- Not the first character
With the i flag set, however, the regular expression does match the capital letter S:
var str:String = "She sells seashells by the seashore.";
trace(str.search(/sh/i)); // output: 0
The i flag ignores case sensitivity only for the A–Z and a–z characters, but not for extended
characters such as É and é.
The m (multiline) flag
If the m (multiline) flag is not set, the ^ matches the beginning of the string and the $
matches the end of the string. If the m flag is set, these characters match the beginning of a line
and end of a line, respectively. Consider the following string, which includes a newline
character:
var str:String = "Testn";
str += "Multiline";
trace(str.match(/^w*/g)); // Match a word at the beginning of the string.
Even though the g (global) flag is set in the regular expression, the match() method matches
only one substring, since there is only one match for the ^—the beginning of the string. The
output is:
Test
Here is the same code with the m flag set:
var str:String = "Testn";
str += "Multiline";
trace(str.match(/^w*/gm)); // Match a word at the beginning of lines.
This time, the output includes the words at the beginning of both lines:
Test,Multiline
Regular expression syntax 303
Note that only the n character signals the end of a line. The following characters do not:
■ Return (r) character
■ Unicode line-separator (u2028) character
■ Unicode paragraph-separator (u2029) character
The s (dotall) flag
If the s (dotall or “dot all”) flag is not set, a dot (.) in a regular expression pattern does not
match a newline character (n). So for the following example, there is no match:
var str:String = "<p>Testn";
str += "Multiline</p>";
var re:RegExp = /<p>.*?</p>/;
trace(str.match(re));
However, if the s flag is set, the dot matches the newline character:
var str:String = "<p>Testn";
str += "Multiline</p>";
var re:RegExp = /<p>.*?</p>/s;
trace(str.match(re));
In this case, the match is the entire substring within the <p> tags, including the newline
character:
<p>Test
Multiline</p>
The x (extended) flag
Regular expressions can be difficult to read, especially when they include a lot of metasymbols
and metasequences. For example:
/<p(>|(s*[^>]*>)).*?</p>/gi
When you use the x (extended) flag in a regular expression, any blank spaces that you type in
the pattern are ignored. For example, the following regular expression is identical to the
previous example:
/ <p (> | (s* [^>]* >)) .*? </p> /gix
If you have the x flag set and do want to match a blank space character, precede the blank
space with a backslash. For example, the following two regular expressions are equivalent:
/foo bar/
/foo  bar/x
304 Using regular expressions
The lastIndex property
The lastIndex property specifies the index position in the string at which to start the next
search. This property affects the exec() and test() methods called on a regular expression
that has the g flag set to true. For example, consider the following code:
var pattern:RegExp = /pw*/gi;
var str:String = "Pedro Piper picked a peck of pickled peppers.";
trace(pattern.lastIndex);
var result:Object = pattern.exec(str);
while (result != null)
{
trace(pattern.lastIndex);
result = pattern.exec(str);
}
The lastIndex property is set to 0 by default (to start searches at the beginning of the string).
After each match, it is set to the index position following the match. Therefore, the output for
the preceding code is the following:
0
5
11
18
25
36
44
If the global flag is set to false, the exec() and test() methods do not use or set the
lastIndex property.
The match(), replace(), and search() methods of the String class start all searches from
the beginning of the string, regardless of the setting of the lastIndex property of the regular
expression used in the call to the method. (However, the match() method does set
lastIndex to 0.)
You can set the lastIndex property to adjust the starting position in the string for regular
expression matching.
The source property
The source property specifies the string that defines the pattern portion of a regular
expression. For example:
var pattern:RegExp = /foo/gi;
trace(pattern.source); // foo
Methods for using regular expressions with strings 305
Methods for using regular expressions
with strings
The RegExp class includes two methods: exec() and test().
In addition to the exec() and test() methods of the RegExp class, the String class includes
the following methods that let you match regular expressions in strings: match(), replace(),
search(), and splice().
The test() method
The test() method of the RegExp class simply checks the supplied string to see if it contains
a match for the regular expression, as the following example shows:
var pattern:RegExp = /Class-w/;
var str = "Class-A";
trace(pattern.test(str)); // output: true
The exec() method
The exec() method of the RegExp class checks the supplied string for a match of the regular
expression and returns an array with the following:
■ The matching substring
■ Substring matches for any parenthetical groups in the regular expression
The array also includes an index property, indicating the index position of the start of the
substring match.
For example, consider the following code:
var pattern:RegExp = /d{3}-d{3}-d{4}/; //U.S phone number
var str:String = "phone: 415-555-1212";
var result:Array = pattern.exec(str);
trace(result.index, " - ", result);
// 7 - 415-555-1212
Use the exec() method multiple times to match multiple substrings when the g (global)
flag is set for the regular expression:
var pattern:RegExp = /w*shw*/gi;
var str:String = "She sells seashells by the seashore";
var result:Array = pattern.exec(str);
while (result != null)
{
306 Using regular expressions
trace(result.index, "t", pattern.lastIndex, "t", result);
result = pattern.exec(str);
}
//output:
// 0 3 She
// 10 19 seashells
// 27 35 seashore
String methods that use RegExp parameters
The following methods of the String class take regular expressions as parameters: match(),
replace(), search(), and split(). For more information on these methods, see “Finding
patterns in strings and replacing substrings” on page 212.
Example: A Wiki parser
This simple Wiki text conversion example illustrates a number of uses for regular expressions:
■ Converting lines of text that match a source Wiki pattern to the appropriate HTML
output strings.
■ Using a regular expression to convert URL patterns to HTML <a> hyperlink tags.
■ Using a regular expression to convert U.S. dollar strings (such as "$9.95") to euro strings
(such as "8.24 €").
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The WikiEditor application files can be found in the
folder Samples/WikiEditor. The application consists of the following files:
File Description
WikiEditor.mxml
or
WikiEditor.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/
regExpExamples/WikiParser.as
A class that includes methods that use regular
expressions to convert Wiki input text patterns
to the equivalent HTML output.
com/example/programmingas3/
regExpExamples/URLParser.as
A class that includes methods that use regular
expressions to convert URL strings to HTML
<a> hyperlink tags.
com/example/programmingas3/
regExpExamples/CurrencyConverter.as
A class that includes methods that use regular
expressions to convert U.S. dollar strings to
euro strings.
Example: A Wiki parser 307
Defining the WikiParser class
The WikiParser class includes methods that convert Wiki input text into the equivalent
HTML output. This is not a very robust Wiki conversion application, but it does illustrate
some good uses of regular expressions for pattern matching and string conversion.
The constructor function, along with the setWikiData() method, simply initializes a sample
string of Wiki input text, as follows:
public function WikiParser()
{
wikiData = setWikiData();
}
When the user clicks the Test button in the sample application, the application invokes the
parseWikiString() method of the WikiParser object. This method calls a number of other
methods, which in turn assemble the resulting HTML string.
public function parseWikiString(wikiString:String):String
{
var result:String = parseBold(wikiString);
result = parseItalic(result);
result = linesToParagraphs(result);
result = parseBullets(result);
return result;
}
Each of the methods called—parseBold(), parseItalic(), linesToParagraphs(), and
parseBullets()—uses the replace() method of the string to replace matching patterns,
defined by a regular expression, in order to transform the input Wiki text into HTML-
formatted text.
Converting boldface and italic patterns
The parseBold() method looks for a Wiki boldface text pattern (such as '''foo''') and
transforms it into its HTML equivalent (such as <b>foo</b>), as follows:
private function parseBold(input:String):String
{
var pattern:RegExp = /'''(.*?)'''/g;
return input.replace(pattern, "<b>$1</b>");
}
Note that the (.?*) portion of the regular expression matches any number of characters (*)
between the two defining ''' patterns. The ? quantifier makes the match nongreedy, so that
for a string such as '''aaa''' bbb '''ccc''', the first matched string will be '''aaa'''
and not the entire string (which starts and ends with the ''' pattern).
308 Using regular expressions
The parentheses in the regular expression define a capturing group, and the replace()
method refers to this group by using the $1 code in the replacement string. The g (global)
flag in the regular expression ensures that the replace() method replaces all matches in the
string (not simply the first one).
The parseItalic() method works similarly to the parseBold() method, except that it
checks for two apostrophes ('') as the delimiter for italic text (not three):
private function parseItalic(input:String):String
{
var pattern:RegExp = /''(.*?)''/g;
return input.replace(pattern, "<i>$1</i>");
}
Converting bullet patterns
As the following example shows, the parseBullet() method looks for the Wiki bullet line
pattern (such as * foo) and transforms it into its HTML equivalent (such as <li>foo</li>):
private function parseBullets(input:String):String
{
var pattern:RegExp = /^*(.*)/gm;
return input.replace(pattern, "<li>$1</li>");
}
The ^ symbol at the beginning of the regular expression matches the beginning of a line. The
m (multiline) flag in the regular expression causes the regular expression to match the ^
symbol against the start of a line, not simply the start of the string.
The * pattern matches an asterisk character (the backslash is used to signal a literal asterisk
instead of a * quantifier).
The parentheses in the regular expression define a capturing group, and the replace()
method refers to this group by using the $1 code in the replacement string. The g (global)
flag in the regular expression ensures that the replace() method replaces all matches in the
string (not simply the first one).
Converting paragraph Wiki patterns
The linesToParagraphs() method converts each line in the input Wiki string to an HTML
<p> paragraph tag. These lines in the method strip out empty lines from the input Wiki
string:
var pattern:RegExp = /^$/gm;
var result:String = input.replace(pattern, "");
Example: A Wiki parser 309
The ^ and $ symbols the regular expression match the beginning and end of a line. The m
(multiline) flag in the regular expression causes the regular expression to match the ^
symbol against the start of a line, not simply the start of the string.
The replace() method replaces all matching substrings (empty lines) with an empty string
(""). The g (global) flag in the regular expression ensures that the replace() method
replaces all matches in the string (not simply the first one).
Converting URLs to HTML <a> tags
When the user clicks the Test button in the sample application, if the user selected the
urlToATag check box, the application calls the URLParser.urlToATag() static method to
convert URL strings from the input Wiki string into HTML <a> tags.
var protocol:String = "((?:http|ftp)://)";
var urlPart:String = "([a-z0-9_-]+.[a-z0-9_-]+)";
var optionalUrlPart:String = "(.[a-z0-9_-]*)";
var urlPattern:RegExp = new RegExp(protocol + urlPart + optionalUrlPart,
"ig");
var result:String = input.replace(urlPattern,
"<a href='$1$2$3'><u>$1$2$3</u></a>");
The RegExp() constructor function is used to assemble a regular expression (urlPattern)
from a number of constituent parts. These constituent parts are each strings that define part
of the regular expression pattern.
The first part of the regular expression pattern, defined by the protocol string, defines an
URL protocol: either http:// or ftp://. The parentheses define a noncapturing group,
indicated by the ? symbol. This means that the parentheses are simply used to define a group
for the | alternation pattern; the group will not match backreference codes ($1, $2, $3) in the
replacement string of the replace() method.
The other constituent parts of the regular expression each use capturing groups (indicated by
parentheses in the pattern), which are then used in the backreference codes ($1, $2, $3) in the
replacement string of the replace() method.
The part of the pattern defined by the urlPart string matches at least one of the following
characters: a-z, 0-9, _, or -. The + quantifier indicates that at least one character is matched.
The . indicates a required dot (.) character. And the remainder matches another string of at
least one of these characters: a-z, 0-9, _, or -.
The part of the pattern defined by the optionalUrlPart string matches zero or more of the
following: a dot (.) character followed by any number of alphanumeric characters (including
_ and -). The * quantifier indicates that zero or more characters are matched.
310 Using regular expressions
The call to the replace() method employs the regular expression and assembles the
replacement HTML string, using backreferences.
The urlToATag() method then calls the emailToATag() method, which uses similar
techniques to replace e-mail patterns with HTML <a> hyperlink strings. The regular
expressions used to match HTTP, FTP, and e-mail URLs in this sample file are fairly simple,
for the purposes of exemplification; there are much more complicated regular expressions for
matching such URLs more correctly.
Converting U.S. dollar strings to euro strings
When the user clicks the Test button in the sample application, if the user selected the
dollarToEuro check box, the application calls the CurrencyConverter.usdToEuro() static
method to convert U.S. dollar strings (such as "$9.95") to euro strings (such as "8.24 €"), as
follows:
var usdPrice:RegExp = /$([d,]+.d+)+/g;
return input.replace(usdPrice, usdStrToEuroStr);
The first line defines a simple pattern for matching U.S. dollar strings. Notice that the $
character is preceded with the backslash () escape character.
The replace() method uses the regular expression as the pattern matcher, and it calls the
usdStrToEuroStr() function to determine the replacement string (a value in euros).
When a function name is used as the second parameter of the replace() method, the
following are passed as parameters to the called function:
■ The matching portion of the string.
■ Any captured parenthetical group matches. The number of arguments passed this way
varies depending on the number of captured parenthetical group matches. You can
determine the number of captured parenthetical group matches by checking
arguments.length - 3 within the function code.
■ The index position in the string where the match begins.
■ The complete string.
The usdStrToEuroStr() method converts U.S. dollar string patterns to euro strings, as
follows:
private function usdToEuro(...args):String
{
var usd:String = args[1];
usd = usd.replace(",", "");
var exchangeRate:Number = 0.828017;
var euro:Number = Number(usd) * exchangeRate;
trace(usd, Number(usd), euro);
Example: A Wiki parser 311
const euroSymbol:String = String.fromCharCode(8364); // €
return euro.toFixed(2) + " " + euroSymbol;
}
Note that args[1] represents the captured parenthetical group matched by the usdPrice
regular expression. This is the numerical portion of the U.S. dollar string: that is, the dollar
amount without the $ sign. The method applies an exchange rate conversion and returns the
resulting string (with a trailing € symbol instead of a leading $ symbol).
312 Using regular expressions
313
10
CHAPTER 10
Handling events
An event-handling system allows programmers to respond to user input and system events in
a convenient way. The ActionScript 3.0 event model is not only convenient, but also
standards-compliant, and well integrated with the Adobe Flash Player 9 display list. Based on
the Document Object Model (DOM) Level 3 Events Specification, an industry-standard
event-handling architecture, the new event model provides a powerful yet intuitive event-
handling tool for ActionScript programmers.
This chapter is organized in five sections. The first two sections provide background
information about event handling in ActionScript. The last three sections describe the main
concepts behind the event model: the event flow, the event object, and event listeners. The
ActionScript 3.0 event-handling system interacts closely with the display list, and this chapter
assumes that you have a basic understanding of the display list. For more information, see
“Display programming” on page 371.
Contents
Basics of handling events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
How ActionScript 3.0 event handling differs from earlier versions. . . . . . . . . . . . . 317
The event flow. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Event objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322
Event listeners. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327
Example: Alarm Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .335
314 Handling events
Basics of handling events
Introduction to handling events
You can think of events as occurrences of any kind in your SWF file that are of interest to you
as a programmer. For example, most SWF files support user interaction of some sort—
whether it's something as simple as responding to a mouse click or something more complex,
such as accepting and processing data entered into a form. Any such user interaction with
your SWF file is considered an event. Events can also occur without any direct user
interaction, such as when data has finished loading from a server or when an attached camera
has become active.
In ActionScript 3.0, each event is represented by an event object, which is an instance of the
Event class or one of its subclasses. An event object not only stores information about a
specific event, but also contains methods that facilitate manipulation of the event object. For
example, when Flash Player detects a mouse click, it creates an event object (an instance of the
MouseEvent class) to represent that particular mouse click event.
After creating an event object, Flash Player dispatches it, which means that the event object is
passed to the object that is the target of the event. An object that serves as the destination for
a dispatched event object is called an event target. For example, when an attached camera
becomes active, Flash Player dispatches an event object directly to the event target, which in
this case is the object that represents the camera. If the event target is on the display list,
however, the event object is passed down through the display list hierarchy until it reaches the
event target. In some cases, the event object then “bubbles” back up the display list hierarchy
along the same route. This traversal of the display list hierarchy is called the event flow.
You can “listen” for event objects in your code using event listeners. Event listeners are the
functions or methods that you write to respond to specific events. To ensure that your
program responds to events, you must add event listeners either to the event target or to any
display list object that is part of an event object’s event flow.
Any time you write event listener code, it follows this basic structure (elements in bold are
placeholders you’d fill in for your specific case):
function eventResponse(eventObject:EventType):void
{
// Actions performed in response to the event go here.
}
eventTarget.addEventListener(EventType.EVENT_NAME, eventResponse);
Basics of handling events 315
This code does two things. First, it defines a function, which is the way to specify the actions
that will be performed in response to the event. Next, it calls the addEventListener()
method of the source object, in essence “subscribing” the function to the specified event so
that when the event happens, the function’s actions are carried out. When the event actually
happens, the event target checks its list of all the functions and methods that are registered as
event listeners. It then calls each one in turn, passing the event object as a parameter.
You need to alter four things in this code to create your own event listener. First, you must
change the name of the function to the name you want to use (this must be changed in two
places, where the code says eventResponse). Second, you must specify the appropriate class
name of the event object that is dispatched by the event you want to listen for (EventType in
the code), and you must specify the appropriate constant for the specific event (EVENT_NAME
in the listing). Third, you must call the addEventListener() method on the object that will
dispatch the event (eventTarget in this code). Optionally, you can change the name of the
variable used as the function’s parameter (eventObject in this code).
Common event-handling tasks
The following are common event-handling tasks, each of which is described in this chapter:
■ Writing code to respond to events
■ Stopping code from responding to events
■ Working with event objects
■ Working with event flow:
■ Identifying event flow information
■ Stopping event flow
■ Preventing default behavior
■ Dispatching events from your classes
■ Creating a custom event type
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Default behavior: Some events include a behavior that normally happens along with the
event, known as the default behavior. For example, when a user types text in a text field, a
text input event is raised. The default behavior for that event is to actually display the
character that was typed into the text field—but you can override that default behavior (if
for some reason you don’t want the typed character to be displayed).
■ Dispatch: To notify event listeners that an event has occurred.
316 Handling events
■ Event: Something that happens to an object that the object can tell other objects about.
■ Event flow: When events happen to an object on the display list (an object displayed on
the screen), all the objects that contain the object are notified of the event and notify their
event listeners in turn. This process starts with the Stage and proceeds through the display
list to the actual object where the event occurred, and then proceeds back to the Stage
again. This process is known as the event flow.
■ Event object: An object that contains information about a particular event’s occurrence,
which is sent to all listeners when an event is dispatched.
■ Event target: The object that actually dispatches an event. For example, if the user clicks a
button that is inside a Sprite that is in turn inside the Stage, all those objects dispatch
events, but the event target is the one where the event actually happened—in this case, the
clicked button.
■ Listener: An object or function that has registered itself with an object, to indicate that it
should be notified when a specific event takes place.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Essentially, all the code listings in this chapter include a trace() function
call for testing the results of the code. To test the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the code listing’s trace() functions in the Output panel.
Some of the code listings are more complex and are written as a class. To test these examples:
1. Create an empty Flash document and save it to your computer.
2. Create a new ActionScript file and save it in the same directory as the Flash document. The
file’s name should match the name of the class in the code listing. For instance, if the code
listing defines a class named EventTest, use the name EventTest.as to save the ActionScript
file.
3. Copy the code listing into the ActionScript file and save the file.
4. In the Flash document, click a blank part of the Stage or work space to activate the
document Property inspector.
How ActionScript 3.0 event handling differs from earlier versions 317
5. In the Property inspector, in the Document Class field, enter the name of the ActionScript
class you copied from the text.
6. Run the program using Control > Test Movie
You will see the results of the example in the Output panel.
These techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
How ActionScript 3.0 event handling
differs from earlier versions
The most noticeable difference between event handling in ActionScript 3.0 and event
handling in previous versions of ActionScript is that in ActionScript 3.0 there is only one
system for event handling, whereas in previous versions of ActionScript there are several
different event-handling systems. This section begins with an overview of how event handling
worked in previous versions of ActionScript, and then discusses how event handling has
changed for ActionScript 3.0.
Event handling in previous versions of ActionScript
Versions of ActionScript before ActionScript 3.0 provided a number of different ways to
handle events:
■ on() event handlers that can be placed directly on Button and MovieClip instances
■ onClipEvent() handlers that can be placed directly on MovieClip instances
■ Callback function properties, such as XML.onload and Camera.onActivity
■ Event listeners that you register using the addListener() method
■ The UIEventDispatcher class that partially implemented the DOM event model.
Each of these mechanisms presents its own set of advantages and limitations. The on() and
onClipEvent() handlers are easy to use, but make subsequent maintenance of projects more
difficult because code placed directly on buttons and movie clips can be difficult to find.
Callback functions are also simple to implement, but limit you to only one callback function
for any given event. Event listeners are more difficult to implement—they require not only
the creation of a listener object and function, but also the registration of the listener with the
object that generates the event. This increased overhead, however, enables you to create several
listener objects and register them all for the same event.
318 Handling events
The development of components for ActionScript 2.0 engendered yet another event model.
This new model, embodied in the UIEventDispatcher class, was based on a subset of the
DOM Events Specification. Developers who are familiar with component event handling will
find the transition to the new ActionScript 3.0 event model relatively painless.
Unfortunately, the syntax used by the various event models overlap in various ways, and differ
in others. For example, in ActionScript 2.0, some properties, such as TextField.onChanged,
can be used as either a callback function or an event listener. However, the syntax for
registering listener objects differs depending on whether you are using one of the six classes
that support listeners or the UIEventDispatcher class. For the Key, Mouse, MovieClipLoader,
Selection, Stage, and TextField classes, you use the addListener() method, but for
components event handling, you use a method called addEventListener().
Another complexity introduced by the different event-handling models was that the scope of
the event handler function varied widely depending on the mechanism used. In other words,
the meaning of the this keyword was not consistent among the event-handling systems.
Event handling in ActionScript 3.0
ActionScript 3.0 introduces a single event-handling model that replaces the many different
event-handling mechanisms that existed in previous versions of the language. The new event
model is based on the Document Object Model (DOM) Level 3 Events Specification.
Although the SWF file format does not adhere specifically to the Document Object Model
standard, there are sufficient similarities between the display list and the structure of the
DOM to make implementation of the DOM event model possible. An object on the display
list is analogous to a node in the DOM hierarchical structure, and the terms display list object
and node are used interchangeably throughout this discussion.
The Flash Player implementation of the DOM event model includes a concept named default
behaviors. A default behavior is an action that Flash Player executes as the normal consequence
of certain events.
Default behaviors
Developers are usually responsible for writing code that responds to events. In some cases,
however, a behavior is so commonly associated with an event that Flash Player automatically
executes the behavior unless the developer adds code to cancel it. Because Flash Player
automatically exhibits the behavior, such behaviors are called default behaviors.
How ActionScript 3.0 event handling differs from earlier versions 319
For example, when a user enters text into a TextField object, the expectation that the text will
be displayed in that TextField object is so common that the behavior is built into Flash Player.
If you do not want this default behavior to occur, you can cancel it using the new event-
handling system. When a user inputs text into a TextField object, Flash Player creates an
instance of the TextEvent class to represent that user input. To prevent Flash Player from
displaying the text in the TextField object, you must access that specific TextEvent instance
and call that instance’s preventDefault() method.
Not all default behaviors can be prevented. For example, Flash Player generates a MouseEvent
object when a user double-clicks a word in a TextField object. The default behavior, which
cannot be prevented, is that the word under the cursor is highlighted.
Many types of event objects do not have associated default behaviors. For example, Flash
Player dispatches a connect event object when a network connection is established, but there
is no default behavior associated with it. The API documentation for the Event class and its
subclasses lists each type of event and describes any associated default behavior, and whether
that behavior can be prevented.
It is important to understand that default behaviors are associated only with event objects
dispatched by Flash Player, and do not exist for event objects dispatched programmatically
through ActionScript. For example, you can use the methods of the EventDispatcher class to
dispatch an event object of type textInput, but that event object will not have a default
behavior associated with it. In other words, Flash Player will not display a character in a
TextField object as a result of a textInput event that you dispatched programmatically.
What’s new for event listeners in ActionScript 3.0
For developers with experience using the ActionScript 2.0 addListener() method, it may be
helpful to point out the differences between the ActionScript 2.0 event listener model and the
ActionScript 3.0 event model. The following list describes a few major differences between
the two event models:
■ To add event listeners in ActionScript 2.0, you use addListener() in some cases and
addEventListener() in others, whereas in ActionScript 3.0, you use
addEventListener() in all situations.
■ There is no event flow in ActionScript 2.0, which means that the addListener() method
can be called only on the object that broadcasts the event, whereas in ActionScript 3.0, the
addEventListener() method can be called on any object that is part of the event flow.
■ In ActionScript 2.0, event listeners can be either functions, methods, or objects, whereas
in ActionScript 3.0, only functions or methods can be event listeners.
320 Handling events
The event flow
Flash Player dispatches event objects whenever an event occurs. If the event target is not on
the display list, Flash Player dispatches the event object directly to the event target. For
example, Flash Player dispatches the progress event object directly to a URLStream object. If
the event target is on the display list, however, Flash Player dispatches the event object into
the display list, and the event object travels through the display list to the event target.
The event flow describes how an event object moves through the display list. The display list is
organized in a hierarchy that can be described as a tree. At the top of the display list hierarchy
is the Stage, which is a special display object container that serves as the root of the display list.
The Stage is represented by the flash.display.Stage class and can only be accessed through a
display object. Every display object has a property named stage that refers to the Stage for
that application.
When Flash Player dispatches an event object, that event object makes a roundtrip journey
from the Stage to the target node. The DOM Events Specification defines the target node as
the node representing the event target. In other words, the target node is the display list object
where the event occurred. For example, if a user clicks on a display list object named child1,
Flash Player will dispatch an event object using child1 as the target node.
The event flow is conceptually divided into three parts. The first part is called the capture
phase; this phase comprises all of the nodes from the Stage to the parent of the target node.
The second part is called the target phase, which consists solely of the target node. The third
part is called the bubbling phase. The bubbling phase comprises the nodes encountered on
the return trip from the parent of the target node back to the Stage.
The names of the phases make more sense if you conceive of the display list as a vertical
hierarchy with the Stage at the top, as shown in the following diagram:
Stage
Parent Node
Child1 Node Child2 Node
The event flow 321
If a user clicks on Child1 Node, Flash Player dispatches an event object into the event flow.
As the following image shows, the object’s journey starts at Stage, moves down to Parent
Node, then moves to Child1 Node, and then “bubbles” back up to Stage, moving through
Parent Node again on its journey back to Stage.
In this example, the capture phase comprises Stage and Parent Node during the initial
downward journey. The target phase comprises the time spent at Child1 Node. The bubbling
phase comprises Parent Node and Stage as they are encountered during the upward journey
back to the root node.
The event flow contributes to a more powerful event-handling system than that previously
available to ActionScript programmers. In previous versions of ActionScript, the event flow
does not exist, which means that event listeners can be added only to the object that generates
the event. In ActionScript 3.0, you can add event listeners not only to a target node, but also
to any node along the event flow.
The ability to add event listeners along the event flow is useful when a user interface
component comprises more than one object. For example, a button object often contains a
text object that serves as the button’s label. Without the ability to add a listener to the event
flow, you would have to add a listener to both the button object and the text object to ensure
that you receive notification about click events that occur anywhere on the button. The
existence of the event flow, however, allows you to place a single event listener on the button
object that handles click events that occur either on the text object or on the areas of the
button object that are not obscured by the text object.
Not every event object, however, participates in all three phases of the event flow. Some types
of events, such as the enterFrame and init event types, are dispatched directly to the target
node and participate in neither the capture phase nor the bubbling phase. Other events may
target objects that are not on the display list, such as events dispatched to an instance of the
Socket class. These event objects will also flow directly to the target object, without
participating in the capture and bubbling phases.
Stage
Parent Node
Child1 Node Child2 Node
Capture
Phase
Bubbling
Phase
Target Phase
322 Handling events
To find out how a particular event type behaves, you can either check the API documentation
or examine the event object's properties. Examining the event object’s properties is described
in the following section.
Event objects
Event objects serve two main purposes in the new event-handling system. First, event objects
represent actual events by storing information about specific events in a set of properties.
Second, event objects contain a set of methods that allow you to manipulate event objects and
affect the behavior of the event-handling system.
To facilitate access to these properties and methods, the Flash Player API defines an Event
class that serves as the base class for all event objects. The Event class defines a fundamental set
of properties and methods that are common to all event objects.
This section begins with a discussion of the Event class properties, continues with a
description of the Event class methods, and concludes with an explanation of why subclasses
of the Event class exist.
Understanding Event class properties
The Event class defines a number of read-only properties and constants that provide
important information about an event object.The following are especially important:
■ Event object types are represented by constants and stored in the Event.type property.
■ Whether an event’s default behavior can be prevented is represented by a Boolean value
and stored in the Event.cancelable property.
■ Event flow information is contained in the remaining properties.
Event object types
Every event object has an associated event type. Event types are stored in the Event.type
property as string values. It is useful to know the type of an event object so that your code can
distinguish objects of different types from one another. For example, the following code
specifies that the clickHandler() listener function should respond to any mouse click event
objects that are passed to myDisplayObject:
myDisplayObject.addEventListener(MouseEvent.CLICK, clickHandler);
Some two dozen event types are associated with the Event class itself and are represented by
Event class constants, some of which are shown in the following excerpt from the Event class
definition:
Event objects 323
package flash.events
{
public class Event
{
// class constants
public static const ACTIVATE:String = "activate";
public static const ADDED:String = "added";
// remaining constants omitted for brevity
}
}
These constants provide an easy way to refer to specific event types. You should use these
constants instead of the strings they represent. If you misspell a constant name in your code,
the compiler will catch the mistake, but if you instead use strings, a typographical error may
not manifest at compile time and could lead to unexpected behavior that could be difficult to
debug. For example, when adding an event listener, use the following code:
myDisplayObject.addEventListener(MouseEvent.CLICK, clickHandler);
rather than:
myDisplayObject.addEventListener("click", clickHandler);
Default behavior information
Your code can check whether the default behavior for any given event object can be prevented
by accessing the cancelable property. The cancelable property holds a Boolean value that
indicates whether or not a default behavior can be prevented. You can prevent, or cancel, the
default behavior associated with a small number of events using the preventDefault()
method. For more information, see “Cancelling default event behavior” on page 326.
Event flow information
The remaining Event class properties contain important information about an event object
and its relationship to the event flow, as described in the following list:
■ The bubbles property contains information about the parts of the event flow in which
the event object participates.
■ The eventPhase property indicates the current phase in the event flow.
■ The target property stores a reference to the event target.
■ The currentTarget property stores a reference to the display list object that is currently
processing the event object.
324 Handling events
The bubbles property
An event is said to bubble if its event object participates in the bubbling phase of the event
flow, which means that the event object is passed from the target node back through its
ancestors until it reaches the Stage. The Event.bubbles property stores a Boolean value that
indicates whether the event object participates in the bubbling phase. Because all events that
bubble also participate in the capture and target phases, any event that bubbles participates in
all three of the event flow phases. If the value is true, the event object participates in all three
phases. If the value is false, the event object does not participate in the bubbling phase.
The eventPhase property
You can determine the event phase for any event object by investigating its eventPhase
property. The eventPhase property contains an unsigned integer value that represents one of
the three phases of the event flow. The Flash Player API defines a separate EventPhase class
that contains three constants that correspond to the three unsigned integer values, as shown in
the following code excerpt:
package flash.events
{
public final class EventPhase
{
public static const CAPTURING_PHASE:uint = 1;
public static const AT_TARGET:uint = 2;
public static const BUBBLING_PHASE:uint = 3;
}
}
These constants correspond to the three valid values of the eventPhase property. You can use
these constants to make your code more readable. For example, if you want to ensure that a
function named myFunc() is called only if the event target is in the target stage, you can use
the following code to test for this condition:
if (event.eventPhase == EventPhase.AT_TARGET)
{
myFunc();
}
The target property
The target property holds a reference to the object that is the target of the event. In some
cases, this is straightforward, such as when a microphone becomes active, the target of the
event object is the Microphone object. If the target is on the display list, however, the display
list hierarchy must be taken into account. For example, if a user inputs a mouse click on a
point that includes overlapping display list objects, Flash Player always chooses the object that
is farthest away from the Stage as the event target.
Event objects 325
For complex SWF files, especially those in which buttons are routinely decorated with smaller
child objects, the target property may not be used frequently because it will often point to a
button’s child object instead of the button. In these situations, the common practice is to add
event listeners to the button and use the currentTarget property because it points to the
button, whereas the target property may point to a child of the button.
The currentTarget property
The currentTarget property contains a reference to the object that is currently processing
the event object. Although it may seem odd not to know which node is currently processing
the event object that you are examining, keep in mind that you can add a listener function to
any display object in that event object's event flow, and the listener function can be placed in
any location. Moreover, the same listener function can be added to different display objects.
As a project increases in size and complexity, the currentTarget property becomes more and
more useful.
Understanding Event class methods
There are three categories of Event class methods:
■ Utility methods, which can create copies of an event object or convert it to a string
■ Event flow methods, which remove event objects from the event flow
■ Default behavior methods, which prevent default behavior or check whether it has been
prevented
Event class utility methods
There are two utility methods in the Event class. The clone() method allows you to create
copies of an event object. The toString() method allows you to generate a string
representation of the properties of an event object along with their values. Both of these
methods are used internally by the event model system, but are exposed to developers for
general use.
For advanced developers creating subclasses of the Event class, you must override and
implement versions of both utility methods to ensure that the event subclass will work
properly.
326 Handling events
Stopping event flow
You can call either the Event.stopPropogation() method or the
Event.stopImmediatePropogation() method to prevent an event object from continuing
on its way through the event flow. The two methods are nearly identical and differ only in
whether the current node’s other event listeners are allowed to execute:
■ The Event.stopPropogation() method prevents the event object from moving on to
the next node, but only after any other event listeners on the current node are allowed to
execute.
■ The Event.stopImmediatePropogation() method also prevents the event object from
moving on to the next node, but does not allow any other event listeners on the current
node to execute.
Calling either of these methods has no effect on whether the default behavior associated with
an event occurs. Use the default behavior methods of the Event class to prevent default
behavior.
Cancelling default event behavior
The two methods that pertain to cancelling default behavior are the preventDefault()
method and the isDefaultPrevented() method. Call the preventDefault() method to
cancel the default behavior associated with an event. To check whether preventDefault()
has already been called on an event object, call the isDefaultPrevented() method, which
returns a value of true if the method has already been called and false otherwise.
The preventDefault() method will work only if the event’s default behavior can be
cancelled. You can check whether this is the case by referring to the API documentation for
that event type, or by using ActionScript to examine the cancelable property of the event
object.
Cancelling the default behavior has no effect on the progress of an event object through the
event flow. Use the event flow methods of the Event class to remove an event object from the
event flow.
Event listeners 327
Subclasses of the Event class
For many events, the common set of properties defined in the Event class is sufficient. Other
events, however, have unique characteristics that cannot be captured by the properties
available in the Event class. For these events, the Flash Player API defines several subclasses of
the Event class.
Each subclass provides additional properties and event types that are unique to that category
of events. For example, events related to mouse input have several unique characteristics that
cannot be captured by the properties defined in the Event class. The MouseEvent class
extends the Event class by adding ten properties that contain information such as the location
of the mouse event and whether specific keys were pressed during the mouse event.
An Event subclass also contains constants that represent the event types that are associated
with the subclass. For example, the MouseEvent class defines constants for several mouse
event types, include the click, doubleClick, mouseDown, and mouseUp event types.
As described in the section “Event class utility methods” on page 325, when creating an Event
subclass you must override the clone() and toString() methods to provide functionality
specific to the subclass.
Event listeners
Event listeners, which are also called event handlers, are functions that Flash Player executes in
response to specific events. Adding an event listener is a two-step process. First, you create a
function or class method for Flash Player to execute in response to the event. This is
sometimes called the listener function or the event handler function. Second, you use the
addEventListener() method to register your listener function with the target of the event or
any display list object that lies along the appropriate event flow.
Creating a listener function
The creation of listener functions is one area where the ActionScript 3.0 event model deviates
from the DOM event model. In the DOM event model, there is a clear distinction between
an event listener and a listener function: an event listener is an instance of a class that
implements the EventListener interface, whereas a listener function is a method of that class
named handleEvent(). In the DOM event model, you register the class instance that
contains the listener function rather than the actual listener function.
328 Handling events
In the ActionScript 3.0 event model, there is no distinction between an event listener and a
listener function. ActionScript 3.0 does not have an EventListener interface, and listener
functions can be defined outside a class or as part of a class. Moreover, listener functions do
not have to be named handleEvent()—they can be named with any valid identifier. In
ActionScript 3.0, you register the name of the actual listener function.
Listener function defined outside of a class
The following code creates a simple SWF file that displays a red square shape. A listener
function named clickHandler(), which is not part of a class, listens for mouse click events
on the red square.
package
{
import flash.display.Sprite;
public class ClickExample extends Sprite
{
public function ClickExample()
{
var child:ChildSprite = new ChildSprite();
addChild(child);
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
class ChildSprite extends Sprite
{
public function ChildSprite()
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
addEventListener(MouseEvent.CLICK, clickHandler);
}
}
function clickHandler(event:MouseEvent):void
{
trace("clickHandler detected an event of type: " + event.type);
trace("the this keyword refers to: " + this);
}
When a user interacts with the resulting SWF file by clicking on the square, Flash Player
generates the following trace output:
Event listeners 329
clickHandler detected an event of type: click
the this keyword refers to: [object global]
Notice that the event object is passed as an argument to clickHandler(). This allows your
listener function to examine the event object. In this example, you use the event object's type
property to ascertain that the event is a click event.
The example also checks the value of the this keyword. In this case, this represents the
global object, which makes sense because the function is defined outside of any custom class
or object.
Listener function defined as a class method
The following example is identical to the previous example that defines the ClickExample
class except that the clickHandler() function is defined as a method of the ChildSprite
class:
package
{
import flash.display.Sprite;
public class ClickExample extends Sprite
{
public function ClickExample()
{
var child:ChildSprite = new ChildSprite();
addChild(child);
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
class ChildSprite extends Sprite
{
public function ChildSprite()
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
addEventListener(MouseEvent.CLICK, clickHandler);
}
private function clickHandler(event:MouseEvent):void
{
trace("clickHandler detected an event of type: " + event.type);
trace("the this keyword refers to: " + this);
}
}
330 Handling events
When a user interacts with the resulting SWF file by clicking on the red square, Flash Player
generates the following trace output:
clickHandler detected an event of type: click
the this keyword refers to: [object ChildSprite]
Note that the this keyword refers to the ChildSprite instance named child. This is a change
in behavior from ActionScript 2.0. If you used components in ActionScript 2.0, you may
remember that when a class method was passed in to
UIEventDispatcher.addEventListener(), the scope of the method was bound to the
component that broadcast the event instead of the class in which the listener method was
defined. In other words, if you used this technique in ActionScript 2.0, the this keyword
would refer to the component broadcasting the event instead of the ChildSprite instance.
This was a significant issue for some programmers because it meant that they could not access
other methods and properties of the class containing the listener method. As a workaround,
ActionScript 2.0 programmers could use the mx.util.Delegate class to change the scope of
the listener method. This is no longer necessary, however, because ActionScript 3.0 creates a
bound method when addEventListener() is called. As a result, the this keyword refers to
the ChildSprite instance named child, and the programmer has access to the other methods
and properties of the ChildSprite class.
Event listener that should not be used
There is a third technique in which you create a generic object with a property that points to
a dynamically assigned listener function, but it is not recommended. It is discussed here
because it was commonly used in ActionScript 2.0, but should not be used in ActionScript
3.0. This technique is not recommended because the this keyword will refer to the global
object instead of your listener object.
The following example is identical to the previous ClickExample class example, except that
the listener function is defined as part of a generic object named myListenerObj:
package
{
import flash.display.Sprite;
public class ClickExample extends Sprite
{
public function ClickExample()
{
var child:ChildSprite = new ChildSprite();
addChild(child);
}
}
}
Event listeners 331
import flash.display.Sprite;
import flash.events.MouseEvent;
class ChildSprite extends Sprite
{
public function ChildSprite()
{
graphics.beginFill(0xFF0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
addEventListener(MouseEvent.CLICK, myListenerObj.clickHandler);
}
}
var myListenerObj:Object = new Object();
myListenerObj.clickHandler = function (event:MouseEvent):void
{
trace("clickHandler detected an event of type: " + event.type);
trace("the this keyword refers to: " + this);
}
The results of the trace will look like this:
clickHandler detected an event of type: click
the this keyword refers to: [object global]
You would expect that this would refer to myListenerObj and that the trace output would
be [object Object], but instead it refers to the global object. When you pass in a dynamic
property name as an argument to addEventListener(), Flash Player is unable to create a
bound method. This is because what you are passing as the listener parameter is nothing
more than the memory address of your listener function, and Flash Player has no way to link
that memory address with the myListenerObj instance.
Managing event listeners
You can manage your listener functions using the methods of the IEventDispatcher interface.
The IEventDispatcher interface is the ActionScript 3.0 version of the EventTarget interface of
the DOM event model. Although the name IEventDispatcher may seem to imply that its
main purpose is to send (or dispatch) event objects, the methods of this class are actually used
much more frequently to register event listeners, check for event listeners, and remove event
listeners. The IEventDispatcher interface defines five methods, as shown in the following
code:
package flash.events
{
public interface IEventDispatcher
{
332 Handling events
function addEventListener(eventName:String,
listener:Object,
useCapture:Boolean=false,
priority:Integer=0,
useWeakReference:Boolean=false):Boolean;
function removeEventListener(eventName:String,
listener:Object,
useCapture:Boolean=false):Boolean;
function dispatchEvent(eventObject:Event):Boolean;
function hasEventListener(eventName:String):Boolean;
function willTrigger(eventName:String):Boolean;
}
}
The Flash Player API implements the IEventDispatcher interface with the EventDispatcher
class, which serves as a base class for all classes that can be event targets or part of an event
flow. For example, the DisplayObject class inherits from the EventDispatcher class. This
means that any object on the display list has access to the methods of the IEventDispatcher
interface.
Adding event listeners
The addEventListener() method is the workhorse of the IEventDispatcher interface. You
use it to register your listener functions. The two required parameters are type and listener.
You use the type parameter to specify the type of event. You use the listener parameter to
specify the listener function that will execute when the event occurs. The listener parameter
can be a reference to either a function or a class method.
The useCapture parameter of the addEventListener() method allows you to control the
event flow phase on which your listener will be active. If useCapture is set to true, your
listener will be active during the capture phase of the event flow. If useCapture is set to
false, your listener will be active during the target and bubbling phases of the event flow. To
listen for an event during all phases of the event flow, you must call addEventListener()
twice, once with useCapture set to true, and then again with useCapture set to false.
NOTE
Do not use parentheses when you specify the listener parameter. For example, the
clickHandler() function is specified without parentheses in the following call to the
addEventListener() method:
addEventListener(MouseEvent.CLICK, clickHandler).
Event listeners 333
The priority parameter of the addEventListener() method is not an official part of the
DOM Level 3 event model. It is included in ActionScript 3.0 to provide you with more
flexibility in organizing your event listeners. When you call addEventListener(), you can
set the priority for that event listener by passing an integer value as the priority parameter.
The default value is 0, but you can set it to negative or positive integer values. The higher the
number, the sooner that event listener will be executed. Event listeners with the same priority
are executed in the order that they were added, so the earlier a listener is added, the sooner it
will be executed.
The useWeakReference parameter allows you to specify whether the reference to the listener
function is weak or normal. Setting this parameter to true allows you to avoid situations in
which listener functions persist in memory even though they are no longer needed. Flash
Player uses a technique called garbage collection to clear objects from memory that are no
longer in use. An object is considered no longer in use if no references to it exist. The garbage
collector disregards weak references, which means that a listener function that has only a weak
reference pointing to it is eligible for garbage collection.
One important consequence of this parameter involves working with display objects’ events.
Normally, you might expect a display object to be removed from memory when it is removed
from the display list. However, if other objects have subscribed as listeners to that display
object, with the useWeakReference parameter set to false (the default), the display object
will continue to exist in Flash Player’s memory even though it no longer appears on the screen.
To work around this issue, either have all the listeners subscribe to the display object with the
useWeakReference parameter set to true, or else remove all the event listeners from the
display object using the removeEventListener() method.
Removing event listeners
You can use the removeEventListener() method to remove an event listener that you no
longer need. It is a good idea to remove any listeners that will no longer be used. Required
parameters include the eventName and listener parameters, which are the same as the
required parameters for the addEventListener() method. Recall that you can listen for
events during all event phases by calling addEventListener() twice, once with useCapture
set to true, and then again with it set to false. To remove both event listeners, you would
need to call removeEventListener() twice, once with useCapture set to true, and then
again with it set to false.
334 Handling events
Dispatching events
The dispatchEvent() method can be used by advanced programmers to dispatch a custom
event object into the event flow. The only parameter accepted by this method is a reference to
an event object, which must be an instance of the Event class or a subclass of the Event class.
Once dispatched, the target property of the event object is set to the object on which
dispatchEvent() was called.
Checking for existing event listeners
The final two methods of the IEventDispatcher interface provide useful information about
the existence of event listeners. The hasEventListener() method returns true if an event
listener is found for a specific event type on a particular display list object. The
willTrigger() method also returns true if a listener is found for a particular display list
object, but willTrigger() checks for listeners not only on that display object, but also on all
of that display list object’s ancestors for all phases of the event flow.
Error events without listeners
Exceptions, rather than events, are the primary mechanism for error handling in ActionScript
3.0, but exception handling does not work for asynchronous operations such as loading files.
If an error occurs during such an asynchronous operation, Flash Player dispatches an error
event object. If you do not create a listener for the error event, the debugger version of Flash
Player will bring up a dialog box with information about the error. For example, using an
invalid URL when loading a file produces this dialog box in the debugger version of Flash
Player:
Example: Alarm Clock 335
Most error events are based on the ErrorEvent class, and as such will have a property named
text that is used to store the error message that Flash Player displays. The two exceptions are
the StatusEvent and NetStatusEvent classes. Both of these classes have a level property
(StatusEvent.level and NetStatusEvent.info.level). When the value of the level
property is "error", these event types are considered to be error events.
An error event will not cause a SWF file to stop running. It will manifest only as a dialog box
on the debugger versions of the browser plug-ins and stand-alone players, as a message in the
output panel in the authoring player, and as an entry in the log file for Adobe Flex Builder 2.
It will not manifest at all in the release versions of Flash Player.
Example: Alarm Clock
The Alarm Clock example consists of a clock that allows the user to specify a time at which an
alarm will go off, as well as a message to be displayed at that time. The Alarm Clock example
builds on the SimpleClock application from Chapter 5, “Working with dates and times.”
Alarm Clock illustrates several aspects of working with events in ActionScript 3.0, including:
■ Listening and responding to an event
■ Notifying listeners of an event
■ Creating a custom event type
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The Alarm Clock application files can be found in the
Samples/AlarmClock folder. The application includes these files:
File Description
AlarmClockApp.mxml
or
AlarmClockApp.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/clock/
AlarmClock.as
A class which extends the SimpleClock class,
adding alarm clock functionality.
com/example/programmingas3/clock/
AlarmEvent.as
A custom event class (a subclass of
flash.events.Event) which serves as the event
object for the AlarmClock class’s alarm event.
com/example/programmingas3/clock/
AnalogClockFace.as
Draws a round clock face and hour, minute, and
seconds hands based on the time (described in
the SimpleClock example).
com/example/programmingas3/clock/
SimpleClock.as
A clock interface component with simple
timekeeping functionality (described in the
SimpleClock example).
336 Handling events
Alarm Clock overview
The primary functionality of the clock in this example, including tracking the time and
displaying the clock face, reuses the SimpleClock application code, which is described in
“Example: Simple analog clock” on page 199. The AlarmClock class extends the SimpleClock
class from that example by adding the functionality required for an alarm clock, including
setting the alarm time and providing notification when the alarm “goes off.”
Providing notification when something happens is the job that events are made for. The
AlarmClock class exposes the Alarm event, which other objects can listen for in order to
perform desired actions. In addition, the AlarmClock class uses an instance of the Timer class
to determine when to trigger its alarm. Like the AlarmClock class, the Timer class provides an
event to notify other objects (an AlarmClock instance, in this case) when a certain amount of
time has passed. As with most ActionScript applications, events form an important part of the
functionality of the Alarm Clock sample application.
Triggering the alarm
As mentioned previously, the only functionality that the AlarmClock class actually provides
relates to setting and triggering the alarm. The built-in Timer class (flash.utils.Timer)
provides a way for a developer to define code that will be executed after a specified amount of
time. The AlarmClock class uses a Timer instance to determine when to set off the alarm.
import flash.events.TimerEvent;
import flash.utils.Timer;
/**
* The Timer that will be used for the alarm.
*/
public var alarmTimer:Timer;
...
/**
* Instantiates a new AlarmClock of a given size.
*/
public override function initClock(faceSize:Number = 200):void
{
super.initClock(faceSize);
alarmTimer = new Timer(0, 1);
alarmTimer.addEventListener(TimerEvent.TIMER, onAlarm);
}
Example: Alarm Clock 337
The Timer instance defined in the AlarmClock class is named alarmTimer. The
initClock() method, which performs necessary setup operations for the AlarmClock
instance, does two things with the alarmTimer variable. First, the variable is instantiated with
parameters instructing the Timer instance to wait 0 milliseconds and only trigger its timer
event one time. After instantiating alarmTimer, the code calls that variable’s
addEventListener() method to indicate that it wants to listen to that variable’s timer event.
A Timer instance works by dispatching its timer event after a specified amount of time has
passed. The AlarmClock class will need to know when the timer event is dispatched in order
to set off its own alarm. By calling addEventListener(), the AlarmClock code registers itself
as a listener with alarmTimer. The two parameters indicate that the AlarmClock class wants
to listen for the timer event (indicated by the constant TimerEvent.TIMER), and that when
the event happens, the AlarmClock class’s onAlarm() method should be called in response to
the event.
In order to actually set the alarm, the AlarmClock class’s setAlarm() method is called, as
follows:
/**
* Sets the time at which the alarm should go off.
* @param hour The hour portion of the alarm time.
* @param minutes The minutes portion of the alarm time.
* @param message The message to display when the alarm goes off.
* @return The time at which the alarm will go off.
*/
public function setAlarm(hour:Number = 0, minutes:Number = 0,
message:String = "Alarm!"):Date
{
this.alarmMessage = message;
var now:Date = new Date();
// Create this time on today's date.
alarmTime = new Date(now.fullYear, now.month, now.date, hour, minutes);
// Determine if the specified time has already passed today.
if (alarmTime <= now)
{
alarmTime.setTime(alarmTime.time + MILLISECONDS_PER_DAY);
}
// Stop the alarm timer if it's currently set.
alarmTimer.reset();
// Calculate how many milliseconds should pass before the alarm should
// go off (the difference between the alarm time and now) and set that
// value as the delay for the alarm timer.
alarmTimer.delay = Math.max(1000, alarmTime.time - now.time);
alarmTimer.start();
338 Handling events
return alarmTime;
}
This method does several things, including storing the alarm message and creating a Date
object (alarmTime) representing the actual moment in time when the alarm is to go off. Of
most relevance to the current discussion, in the final several lines of the method, the
alarmTimer variable’s timer is set and activated. First, its reset() method is called, stopping
the timer and resetting it in case it is already running. Next, the current time (represented by
the now variable) is subtracted from the alarmTime variable’s value to determine how many
milliseconds need to pass before the alarm goes off. The Timer class doesn’t trigger its timer
event at an absolute time, so it is this relative time difference that is assigned to the delay
property of alarmTimer. Finally, the start() method is called to actually start the timer.
Once the specified amount of time has passed, alarmTimer dispatches the timer event.
Because the AlarmClock class registered its onAlarm() method as a listener for that event,
when the timer event happens, onAlarm() is called.
/**
* Called when the timer event is dispatched.
*/
public function onAlarm(event:TimerEvent):void
{
trace("Alarm!");
var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage);
this.dispatchEvent(alarm);
}
A method that is registered as an event listener must be defined with the appropriate signature
(that is, the set of parameters and return type of the method). To be a listener for the Timer
class’s timer event, a method must define one parameter whose data type is TimerEvent
(flash.events.TimerEvent), a subclass of the Event class. When the Timer instance calls its
event listeners, it passes a TimerEvent instance as the event object.
Notifying others of the alarm
Like the Timer class, the AlarmClock class provides an event that allows other code to receive
notifications when the alarm goes off. For a class to use the event-handling framework built
into ActionScript, that class must implement the flash.events.IEventDispatcher interface.
Most commonly, this is done by extending the flash.events.EventDispatcher class, which
provides a standard implementation of IEventDispatcher (or by extending one of
EventDispatcher’s subclasses). As described previously, the AlarmClock class extends the
SimpleClock class, which in turn extends the Sprite class, which (through a chain of
inheritance) extends the EventDispatcher class. All of this means that the AlarmClock class
already has built-in functionality to provide its own events.
Example: Alarm Clock 339
Other code can register to be notified of the AlarmClock class’s alarm event by calling the
addEventListener() method that AlarmClock inherits from EventDispatcher. When an
AlarmClock instance is ready to notify other code that its alarm event has been raised, it does
so by calling the dispatchEvent() method, which is also inherited from EventDispatcher.
var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage);
this.dispatchEvent(alarm);
These lines of code are taken from the AlarmClock class’s onAlarm() method (shown in its
entirety previously). The AlarmClock instance’s dispatchEvent() method is called, which in
turn notifies all the registered listeners that the AlarmClock instance’s alarm event has been
triggered. The parameter that is passed to dispatchEvent() is the event object that will be
passed along to the listener methods. In this case, it is an instance of the AlarmEvent class, an
Event subclass created specifically for this example.
Providing a custom alarm event
All event listeners receive an event object parameter with information about the particular
event being triggered. In many cases, the event object is an instance of the Event class.
However, in some cases it is useful to provide additional information to event listeners. As
described earlier in the chapter, a common way to accomplish this is to define a new class, a
subclass of the Event class, and use an instance of that class as the event object. In this
example, an AlarmEvent instance is used as the event object when the AlarmClock class’s
alarm event is dispatched. The AlarmEvent class, shown here, provides additional
information about the alarm event, specifically the alarm message:
import flash.events.Event;
/**
* This custom Event class adds a message property to a basic Event.
*/
public class AlarmEvent extends Event
{
/**
* The name of the new AlarmEvent type.
*/
public static const ALARM:String = "alarm";
/**
* A text message that can be passed to an event handler
* with this event object.
*/
public var message:String;
340 Handling events
/**
* Constructor.
* @param message The text to display when the alarm goes off.
*/
public function AlarmEvent(message:String = "ALARM!")
{
super(ALARM);
this.message = message;
}
...
}
The best way to create a custom event object class is to define a class that extends the Event
class, as shown in the preceding example. To supplement the inherited functionality, the
AlarmEvent class defines a property message that contains the text of the alarm message
associated with the event; the message value is passed in as a parameter in the AlarmEvent
constructor. The AlarmEvent class also defines the constant ALARM, which can be used to refer
to the specific event (alarm) when calling the AlarmClock class’s addEventListener()
method.
In addition to adding custom functionality, every Event subclass must override the inherited
clone() method as part of the ActionScript event-handling framework. Event subclasses can
also optionally override the inherited toString() method to include the custom event’s
properties in the value returned when the toString() method is called.
/**
* Creates and returns a copy of the current instance.
* @return A copy of the current instance.
*/
public override function clone():Event
{
return new AlarmEvent(message);
}
/**
* Returns a String containing all the properties of the current
* instance.
* @return A string representation of the current instance.
*/
public override function toString():String
{
return formatToString("AlarmEvent", "type", "bubbles", "cancelable",
"eventPhase", "message");
}
Example: Alarm Clock 341
The overridden clone() method needs to return a new instance of the custom Event subclass,
with all the custom properties set to match the current instance. In the overridden
toString() method, the utility method formatToString() (inherited from Event) is used
to provide a string with the name of the custom type, as well as the names and values of all its
properties.
342 Handling events
343
11
CHAPTER 11
Working with XML
ActionScript 3.0 includes a group of classes based on the ECMAScript for XML (E4X)
specification (ECMA-357 edition 2). These classes include powerful and easy-to-use
functionality for working with XML data. Using E4X, you will be able to develop code with
XML data faster than was possible with previous programming techniques. As an added
benefit, the code you produce will be easier to read.
This chapter describes how to use E4X to process XML data.
Contents
Basics of XML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
The E4X approach to XML processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .348
XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
XMLList objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .353
Initializing XML variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .354
Assembling and transforming XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .356
Traversing XML structures. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358
Using XML namespaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .363
XML type conversion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364
Reading external XML documents. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .366
Example: Loading RSS data from the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .367
344 Working with XML
Basics of XML
Introduction to working with XML
XML is a standard way of representing structured information so that it is easy for computers
to work with and reasonably easy for people to write and understand. XML is an abbreviation
for eXtensible Markup Language. The XML standard is available at www.w3.org/XML/.
XML offers a standard and convenient way to categorize data, to make it easier to read, access,
and manipulate. XML uses a tree structure and tag structure that is similar to HTML. Here is
a simple example of XML data:
<song>
<title>What you know?</title>
<artist>Steve and the flubberblubs</artist>
<year>1989</year>
<lastplayed>2006-10-17-08:31</lastplayed>
</song>
XML data can also be more complex, with tags nested in other tags as well as attributes and
other structural components. Here is a more complex example of XML data:
<album>
<title>Questions, unanswered</title>
<artist>Steve and the flubberblubs</artist>
<year>1989</year>
<tracks>
<song tracknumber="1" length="4:05">
<title>What do you know?</title>
<artist>Steve and the flubberblubs</artist>
<lastplayed>2006-10-17-08:31</lastplayed>
</song>
<song tracknumber="2" length="3:45">
<title>Who do you know?</title>
<artist>Steve and the flubberblubs</artist>
<lastplayed>2006-10-17-08:35</lastplayed>
</song>
<song tracknumber="3" length="5:14">
<title>When do you know?</title>
<artist>Steve and the flubberblubs</artist>
<lastplayed>2006-10-17-08:39</lastplayed>
</song>
<song tracknumber="4" length="4:19">
<title>Do you know?</title>
<artist>Steve and the flubberblubs</artist>
<lastplayed>2006-10-17-08:44</lastplayed>
Basics of XML 345
</song>
</tracks>
</album>
Notice that this XML document contains other complete XML structures within it (such as
the song tags with their children). It also demonstrates other XML structures such as
attributes (tracknumber and length in the song tags), and tags that contain other tags rather
than containing data (such as the tracks tag).
Getting started with XML
If you have little or no experience with XML, here is a brief description of the most common
aspects of XML data. XML data is written in plain-text form, with a specific syntax for
organizing the information into a structured format. Generally, a single set of XML data is
known as an XML document. In XML format, data is organized into elements (which can be
single data items or containers for other elements) using a hierarchical structure. Every XML
document has a single element as the top level or main item; inside this root element there
may be a single piece of information, although there are more likely to be other elements,
which in turn contain other elements, and so forth. For example, this XML document
contains the information about a music album:
<song tracknumber="1" length="4:05">
<title>What do you know?</title>
<artist>Steve and the flubberblubs</artist>
<mood>Happy</mood>
<lastplayed>2006-10-17-08:31</lastplayed>
</song>
Each element is distinguished by a set of tags—the element’s name wrapped in angle brackets
(less-than and greater-than signs). The opening tag, indicating the start of the element, has
the element name:
<title>
The closing tag, which marks the end of the element, has a forward slash before the element’s
name:
</title>
If an element contains no content, it can be written as an empty element (sometimes called a
self-closing element). In XML, this element:
<lastplayed/>
is identical to this element:
<lastplayed></lastplayed>
346 Working with XML
In addition to the element’s content contained between the opening and closing tags, an
element can also include other values, known as attributes, defined in the element’s opening
tag. For example, this XML element defines a single attribute named length, with the value
“4:19”:
<song length="4:19"></song>
Each XML element has content, which is either a single value, one or more XML elements, or
nothing (for an empty element).
Learning more about XML
To learn more about working with XML, there are a number of additional books and
resources for learning more about XML, including these web sites:
■ W3Schools XML Tutorial: http://w3schools.com/xml/
■ XML.com: http://www.xml.com/
■ XMLpitstop tutorials, discussion lists, and more: http://xmlpitstop.com/
ActionScript classes for working with XML
ActionScript 3.0 includes several classes that are used for working with XML-structured
information. The two main classes are as follows:
■ XML: Represents a single XML element, which can be an XML document with multiple
children or a single-value element within a document.
■ XMLList: Represents a set of XML elements. An XMLList object is used when there are
multiple XML elements that are “siblings” (at the same level, and contained by the same
parent, in the XML document’s hierarchy). For instance, an XMLList instance would be
the easiest way to work with this set of XML elements (presumably contained in an XML
document):
<artist type="composer">Fred Wilson</artist>
<artist type="conductor">James Schmidt</artist>
<artist type="soloist">Susan Harriet Thurndon</artist>
For more advanced uses involving XML namespaces, ActionScript also includes the
Namespace and QName classes. For more information, see “Using XML namespaces”
on page 363.
In addition to the built-in classes for working with XML, ActionScript 3.0 also includes
several operators that provide specific functionality for accessing and manipulating XML data.
This approach to working with XML using these classes and operators is known as
ECMAScript for XML (E4X), as defined by the ECMA-357 edition 2 specification.
Basics of XML 347
Common XML tasks
When you work with XML in ActionScript, you are likely to do the following tasks:
■ Constructing XML documents (adding elements and values)
■ Accessing XML elements, values, and attributes
■ Filtering (searching in) XML elements
■ Looping over a set of XML elements
■ Converting data between XML classes and the String class
■ Working with XML namespaces
■ Loading external XML files
Important concepts and terms
The following reference list contains important terms used in this chapter:
■ Element: A single item in an XML document, identified as the content contained between
a starting tag and an ending tag (including the tags). XML elements can contain text data
or other elements, or can be empty.
■ Empty element: An XML element that contains no child elements. Empty elements are
often written as self-closing tags (such as <element/>).
■ Document: A single XML structure. An XML document can contain any number of
elements (or can consist only of a single empty element); however, an XML document
must have a single top-level element that contains all the other elements in the document.
■ Node: Another name for an XML element.
■ Attribute: A named value associated with an element that is written into the opening tag
of the element in attributename="value" format, rather than being written as a
separate child element nested inside the element.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Essentially all the code listings in this chapter already include the
appropriate trace() function call. To test the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
348 Working with XML
You will see the results of the trace() function in the Output panel.
This and other techniques for testing example code listings are described in more detail in
“Testing in-chapter example code listings” on page 63.
The E4X approach to XML processing
The ECMAScript for XML specification defines a set of classes and functionality for working
with XML data. These classes and functionality are known collectively as E4X. ActionScript
3.0 includes the following E4X classes: XML, XMLList, QName, and Namespace.
The methods, properties, and operators of the E4X classes are designed with the following
goals:
■ Simplicity—Where possible, E4X makes it easier to write and understand code for
working with XML data.
■ Consistency—The methods and reasoning behind E4X are internally consistent and
consistent with other parts of ActionScript.
■ Familiarity—You manipulate XML data with well-known operators, such as the dot (.)
operator.
Here is an example of manipulating data with E4X:
var myXML:XML =
<order>
<item id='1'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<item id='2'>
<menuName>fries</menuName>
<price>1.45</price>
</item>
</order>
NOTE
There was an XML class in ActionScript 2.0. In ActionScript 3.0 it has been renamed
XMLDocument, so that it does not conflict with the ActionScript 3.0 XML class that is
part of E4X. In ActionScript 3.0, the legacy classes—XMLDocument, XMLNode,
XMLParser, and XMLTag—are included in the flash.xml package primarily for legacy
support. The new E4X classes are core classes; you need not import a package to use
them. This chapter does not go into detail on the legacy ActionScript 2.0 XML classes.
For details on these, see the flash.xml package in the ActionScript 3.0 Language and
Components Reference.
The E4X approach to XML processing 349
Often, your application will load XML data from an external source, such as a web service or
a RSS feed. However, for clarity, the examples in this chapter assign XML data as literals.
As the following code shows, E4X includes some intuitive operators, such as the dot (.) and
attribute identifier (@) operators, for accessing properties and attributes in the XML:
trace(myXML.item[0].menuName); // Output: burger
trace(myXML.item.(@id==2).menuName); // Output: fries
trace(myXML.item.(menuName=="burger").price); // Output: 3.95
Use the appendChild() method to assign a new child node to the XML, as the following
snippet shows:
var newItem:XML =
<item id="3">
<menuName>medium cola</menuName>
<price>1.25</price>
</item>
myXML.appendChild(newItem);
Use the @ and . operators not only to read data, but also to assign data, as in the following:
myXML.item[0].menuName="regular burger";
myXML.item[1].menuName="small fries";
myXML.item[2].menuName="medium cola";
myXML.item.(menuName=="regular burger").@quantity = "2";
myXML.item.(menuName=="small fries").@quantity = "2";
myXML.item.(menuName=="medium cola").@quantity = "2";
Use a for loop to iterate through nodes of the XML, as follows:
var total:Number = 0;
for each (var property:XML in myXML.item)
{
var q:int = Number(property.@quantity);
var p:Number = Number(property.price);
var itemTotal:Number = q * p;
total += itemTotal;
trace(q + " " + property.menuName + " $" + itemTotal.toFixed(2))
}
trace("Total: $", total.toFixed(2));
350 Working with XML
XML objects
An XML object may represent an XML element, attribute, comment, processing instruction,
or text element.
An XML object is classified as having either simple content or complex content. An XML object
that has child nodes is classified as having complex content. An XML object is said to have
simple content if it is any one of the following: an attribute, a comment, a processing
instruction, or a text node.
For example, the following XML object contains complex content, including a comment and
a processing instruction:
XML.ignoreComments = false;
XML.ignoreProcessingInstructions = false;
var x1:XML =
<order>
<!--This is a comment. -->
<?PROC_INSTR sample ?>
<item id='1'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<item id='2'>
<menuName>fries</menuName>
<price>1.45</price>
</item>
</order>
As the following example shows, you can now use the comments() and
processingInstructions() methods to create new XML objects, a comment and a
processing instruction:
var x2:XML = x1.comments()[0];
var x3:XML = x1.processingInstructions()[0];
XML properties
The XML class has five static properties:
■ The ignoreComments and ignoreProcessingInstructions properties determine
whether comments or processing instructions are ignored when the XML object is parsed.
■ The ignoreWhitespace property determines whether white space characters are ignored
in element tags and embedded expressions that are separated only by white space
characters.
■ The prettyIndent and prettyPrinting properties are used to format the text that is
returned by the toString() and toXMLString() methods of the XML class.
XML objects 351
For details on these properties, see the ActionScript 3.0 Language and Components Reference.
XML methods
The following methods allow you to work with the hierarchical structure of XML objects:
■ appendChild()
■ child()
■ childIndex()
■ children()
■ descendants()
■ elements()
■ insertChildAfter()
■ insertChildBefore()
■ parent()
■ prependChild()
The following methods allow you to work with XML object attributes:
■ attribute()
■ attributes()
The following methods allow you to you work with XML object properties:
■ hasOwnProperty()
■ propertyIsEnumerable()
■ replace()
■ setChildren()
The following methods are for working with qualified names and namespaces:
■ addNamespace()
■ inScopeNamespaces()
■ localName()
■ name()
■ namespace()
■ namespaceDeclarations()
■ removeNamespace()
■ setLocalName()
■ setName()
■ setNamespace()
352 Working with XML
The following methods are for working with and determining certain types of XML content:
■ comments()
■ hasComplexContent()
■ hasSimpleContent()
■ nodeKind()
■ processingInstructions()
■ text()
The following methods are for conversion to strings and for formatting XML objects:
■ defaultSettings()
■ setSettings()
■ settings()
■ normalize()
■ toString()
■ toXMLString()
There are a few additional methods:
■ contains()
■ copy()
■ valueOf()
■ length()
For details on these methods, see the ActionScript 3.0 Language and Components Reference.
XMLList objects 353
XMLList objects
An XMLList instance represents an arbitrary collection of XML objects. It can contain full
XML documents, XML fragments, or the results of an XML query.
The following methods allow you to work with the hierarchical structure of XMLList objects:
■ child()
■ children()
■ descendants()
■ elements()
■ parent()
The following methods allow you to work with XMLList object attributes:
■ attribute()
■ attributes()
The following methods allow you to you work with XMLList properties:
■ hasOwnProperty()
■ propertyIsEnumerable()
The following methods are for working with and determining certain types of XML content:
■ comments()
■ hasComplexContent()
■ hasSimpleContent()
■ processingInstructions()
■ text()
The following are for conversion to strings and for formatting the XMLList object:
■ normalize()
■ toString()
■ toXMLString()
There are a few additional methods:
■ contains()
■ copy()
■ length()
■ valueOf()
For details on these methods, see the ActionScript 3.0 Language and Components Reference.
354 Working with XML
For an XMLList object that contains exactly one XML element, you can use all properties and
methods of the XML class, because an XMLList with one XML element is treated the same as
an XML object. For example, in the following code, because doc.div is an XMLList object
containing one element, you can use the appendChild() method from the XML class:
var doc:XML =
<body>
<div>
<p>Hello</p>
</div>
</body>;
doc.div.appendChild(<p>World</p>);
For a list of XML properties and methods, see “XML objects” on page 350.
Initializing XML variables
You can assign an XML literal to an XML object, as follows:
var myXML:XML =
<order>
<item id='1'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<item id='2'>
<menuName>fries</menuName>
<price>1.45</price>
</item>
</order>
As the following snippet shows, you can also use the new constructor to create an instance of
an XML object from a string that contains XML data:
var str:String = "<order><item id='1'><menuName>burger</menuName>"
+ "<price>3.95</price></item></order>";
var myXML:XML = new XML(str);
Initializing XML variables 355
If the XML data in the string is not well formed (for example, if a closing tag is missing), you
will see a run-time error.
You can also pass data by reference (from other variables) into an XML object, as the
following example shows:
var tagname:String = "item";
var attributename:String = "id";
var attributevalue:String = “5”;
var content:String = "Chicken";
var x:XML = <{tagname} {attributename}={attributevalue}>{content}</
{tagname}>;
trace(x.toXMLString())
// Output: <item id="5">Chicken</item>
To load XML data from a URL, use the URLLoader class, as the following example shows:
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
var externalXML:XML;
var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest("xmlFile.xml");
loader.load(request);
loader.addEventListener(Event.COMPLETE, onComplete);
function onComplete(event:Event):void
{
var loader:URLLoader = event.target as URLLoader;
if (loader != null)
{
externalXML = new XML(loader.data);
trace(externalXML.toXMLString());
}
else
{
trace("loader is not a URLLoader!");
}
}
To read XML data from a socket connection, use the XMLSocket class. For more
information, see the XMLSocket entry in the ActionScript 3.0 Language and Components
Reference.
356 Working with XML
Assembling and transforming XML
objects
Use the prependChild() method or the appendChild() method to add a property to the
beginning or end of an XML object’s list of properties, as the following example shows:
var x1:XML = <p>Line 1</p>
var x2:XML = <p>Line 2</p>
var x:XML = <body></body>
x = x.appendChild(x1);
x = x.appendChild(x2);
x = x.prependChild(<p>Line 0</p>);
// x == <body><p>Line 0</p><p>Line 1</p><p>Line 2</p></body>
Use the insertChildBefore() method or the insertChildAfter() method to add a
property before or after a specified property, as follows:
var x:XML =
<body>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</body>
var newNode:XML = <p>Paragraph 1.5</p>
x = x.insertChildAfter(x.p[0], newNode)
x = x.insertChildBefore(x.p[2], <p>Paragraph 1.75</p>)
As the following example shows, you can also use curly brace operators ( { and } ) to pass data
by reference (from other variables) when constructing XML objects:
var ids:Array = [121, 122, 123];
var names:Array = [["Murphy","Pat"], ["Thibaut","Jean"], ["Smith","Vijay"]]
var x:XML = new XML("<employeeList></employeeList>");
for (var i:int = 0; i < 3; i++)
{
var newnode:XML = new XML();
newnode =
<employee id={ids[i]}>
<last>{names[i][0]}</last>
<first>{names[i][1]}</first>
</employee>;
x = x.appendChild(newnode)
}
Assembling and transforming XML objects 357
You can assign properties and attributes to an XML object by using the = operator, as in the
following:
var x:XML =
<employee>
<lastname>Smith</lastname>
</employee>
x.firstname = "Jean";
x.@id = "239";
This sets the XML object x to the following:
<employee id="239">
<lastname>Smith</lastname>
<firstname>Jean</firstname>
</employee>
You can use the + and += operators to concatenate XMLList objects:
var x1:XML = <a>test1</a>
var x2:XML = <b>test2</b>
var xList:XMLList = x1 + x2;
xList += <c>test3</c>
This sets the XMLList object xList to the following:
<a>test1</a>
<b>test2</b>
<c>test3</c>
358 Working with XML
Traversing XML structures
One of the powerful features of XML is its ability to provide complex, nested data via a linear
string of text characters. When you load data into an XML object, ActionScript parses the
data and loads its hierarchical structure into memory (or it sends a run-time error if the XML
data is not well formed).
The operators and methods of the XML and XMLList objects make it easy to traverse the
structure of XML data.
Use the dot (.) operator and the descendent accessor (..) operator to access child properties of
an XML object. Consider the following XML object:
var myXML:XML =
<order>
<book ISBN="0942407296">
<title>Baking Extravagant Pastries with Kumquats</title>
<author>
<lastName>Contino</lastName>
<firstName>Chuck</firstName>
</author>
<pageCount>238</pageCount>
</book>
<book ISBN="0865436401">
<title>Emu Care and Breeding</title>
<editor>
<lastName>Case</lastName>
<firstName>Justin</firstName>
</editor>
<pageCount>115</pageCount>
</book>
</order>
The object myXML.book is an XMLList object containing child properties of the myXML object
that have the name book. These are two XML objects, matching the two book properties of
the myXML object.
The object myXML..lastName is an XMLList object containing any descendent properties
with the name lastName. These are two XML objects, matching the two lastName of the
myXML object.
The object myXML.book.editor.lastName is an XMLList object containing any children
with the name lastName of children with the name editor of children with the name book
of the myXML object: in this case, an XMLList object containing only one XML object (the
lastName property with the value "Case").
Traversing XML structures 359
Accessing parent and child nodes
The parent() method returns the parent of an XML object.
You can use the ordinal index values of a child list to access specific child objects. For example,
consider an XML object myXML that has two child properties named book. Each child
property named book has an index number associated with it:
myXML.book[0]
myXML.book[1]
To access a specific grandchild, you can specify index numbers for both the child and
grandchild names:
myXML.book[0].title[0]
However, if there is only one child of x.book[0] that has the name title, you can omit the
index reference, as follows:
myXML.book[0].title
Similarly, if there is only one book child of the object x, and if that child object has only one
title object, you can omit both index references, like this:
myXML.book.title
You can use the child() method to navigate to children with names based on a variable or
expression, as the following example shows:
var myXML:XML =
<order>
<book>
<title>Dictionary</title>
</book>
</order>;
var childName:String = "book";
trace(myXML.child(childName).title) // output: Dictionary
360 Working with XML
Accessing attributes
Use the @ symbol (the attribute identifier operator) to access attributes in an XML or
XMLList object, as shown in the following code:
var employee:XML =
<employee id="6401" code="233">
<lastName>Wu</lastName>
<firstName>Erin</firstName>
</employee>;
trace(employee.@id); // 6401
You can use the * wildcard symbol with the @ symbol to access all attributes of an XML or
XMLList object, as in the following code:
var employee:XML =
<employee id="6401" code="233">
<lastName>Wu</lastName>
<firstName>Erin</firstName>
</employee>;
trace(employee.@*.toXMLString());
// 6401
// 233
You can use the attribute() or attributes() method to access a specific attribute or all
attributes of an XML or XMLList object, as in the following code:
var employee:XML =
<employee id="6401" code="233">
<lastName>Wu</lastName>
<firstName>Erin</firstName>
</employee>;
trace(employee.attribute("id")); // 6401
trace(employee.attribute("*").toXMLString());
// 6401
// 233
trace(employee.attributes().toXMLString());
// 6401
// 233
Note that you can also use the following syntax to access attributes, as the following example
shows:
employee.attribute("id")
employee["@id"]
employee.@["id"]
These are each equivalent to employee.@id. However, the syntax employee.@id is the
preferred approach.
Traversing XML structures 361
Filtering by attribute or element value
You can use the parentheses operators— ( and ) —to filter elements with a specific element
name or attribute value. Consider the following XML object:
var x:XML =
<employeeList>
<employee id="347">
<lastName>Zmed</lastName>
<firstName>Sue</firstName>
<position>Data analyst</position>
</employee>
<employee id="348">
<lastName>McGee</lastName>
<firstName>Chuck</firstName>
<position>Jr. data analyst</position>
</employee>
</employeeList>
The following expressions are all valid:
■ x.employee.(lastName == "McGee")—This is the second employee node.
■ x.employee.(lastName == "McGee").firstName—This is the firstName property of
the second employee node.
■ x.employee.(lastName == "McGee").@id—This is the value of the id attribute of the
second employee node.
■ x.employee.(@id == 347)—The first employee node.
■ x.employee.(@id == 347).lastName—This is the lastName property of the first
employee node.
■ x.employee.(@id > 300)—This is an XMLList with both employee properties.
■ x.employee.(position.toString().search("analyst") > -1)—This is an
XMLList with both position properties.
If you try to filter on attributes or elements that may not exist, Adobe Flash Player will throw
an exception. For example, the final line of following code generates an error, because there is
no id attribute in the second p element:
var doc:XML =
<body>
<p id='123'>Hello, <b>Bob</b>.</p>
<p>Hello.</p>
</body>;
trace(doc.p.(@id == '123'));
362 Working with XML
Similarly, the final line of following code generates an error because there is no b property of
the second p element:
var doc:XML =
<body>
<p id='123'>Hello, <b>Bob</b>.</p>
<p>Hello.</p>
</body>;
trace(doc.p.(b == 'Bob'));
To avoid these errors, you can identify the properties that have the matching attributes or
elements by using the attribute() and elements() methods, as in the following code:
var doc:XML =
<body>
<p id='123'>Hello, <b>Bob</b>.</p>
<p>Hello.</p>
</body>;
trace(doc.p.(attribute('id') == '123'));
trace(doc.p.(elements('b') == 'Bob'));
You can also use the hasOwnProperty() method, as in the following code:
var doc:XML =
<body>
<p id='123'>Hello, <b>Bob</b>.</p>
<p>Hello.</p>
</body>;
trace(doc.p.(hasOwnProperty('@id') && @id == '123'));
trace(doc.p.(hasOwnProperty('b') && b == 'Bob'));
Using the for..in and the for each..in statements
ActionScript 3.0 includes the for..in statement and the for each..in statement for
iterating through XMLList objects. For example, consider the following XML object, myXML,
and the XMLList object, myXML.item. The XMLList object, myXML.item, consists of the two
item nodes of the XML object.
var myXML:XML =
<order>
<item id='1' quantity='2'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<item id='2' quantity='2'>
<menuName>fries</menuName>
<price>1.45</price>
</item>
</order>;
Using XML namespaces 363
The for..in statement lets you iterate over a set of property names in an XMLList:
var total:Number = 0;
for (var pname:String in myXML.item)
{
total += myXML.item.@quantity[pname] * myXML.item.price[pname];
}
The for each..in statement lets you iterate through the properties in the XMLList:
var total2:Number = 0;
for each (var prop:XML in myXML.item)
{
total2 += prop.@quantity * prop.price;
}
Using XML namespaces
Namespaces in an XML object (or document) identify the type of data that the object
contains. For example, in sending and delivering XML data to a web service that uses the
SOAP messaging protocol, you declare the namespace in the opening tag of the XML:
var message:XML =
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body xmlns:w="http://www.test.com/weather/">
<w:getWeatherResponse>
<w:tempurature >78</w:tempurature>
</w:getWeatherResponse>
</soap:Body>
</soap:Envelope>;
The namespace has a prefix, soap, and a URI that defines the namespace, http://
schemas.xmlsoap.org/soap/envelope/.
ActionScript 3.0 includes the Namespace class for working with XML namespaces. For the
XML object in the previous example, you can use the Namespace class as follows:
var soapNS:Namespace = message.namespace("soap");
trace(soapNS); // Output: http://schemas.xmlsoap.org/soap/envelope/
var wNS:Namespace = new Namespace("w", "http://www.test.com/weather/");
message.addNamespace(wNS);
var encodingStyle:XMLList = message.@soapNS::encodingStyle;
var body:XMLList = message.soapNS::Body;
message.soapNS::Body.wNS::GetWeatherResponse.wNS::tempurature = "78";
364 Working with XML
The XML class includes the following methods for working with namespaces:
addNamespace(), inScopeNamespaces(), localName(), name(), namespace(),
namespaceDeclarations(), removeNamespace(), setLocalName(), setName(), and
setNamespace().
The default xml namespace directive lets you assign a default namespace for XML objects.
For example, in the following, both x1 and x2 have the same default namespace:
var ns1:Namespace = new Namespace("http://www.example.com/namespaces/");
default xml namespace = ns1;
var x1:XML = <test1 />;
var x2:XML = <test2 />;
XML type conversion
You can convert XML objects and XMLList objects to String values. Similarly, you can
convert strings to XML objects and XMLList objects. Also, keep in mind that all XML
attribute values, names, and text values are strings. The following sections discuss all these
forms of XML type conversion.
Converting XML and XMLList objects to strings
The XML and XMLList classes include a toString() method and a toXMLString()
method. The toXMLString() method returns a string that includes all tags, attributes,
namespace declarations, and content of the XML object. For XML objects with complex
content (child elements), the toString() method does exactly the same as the
toXMLString() method. For XML objects with simple content (those that contain only one
text element), the toString() method returns only the text content of the element, as the
following example shows:
var myXML:XML =
<order>
<item id='1' quantity='2'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<order>;
trace(myXML.item[0].menuName.toXMLString());
// <menuName>burger</menuName>
trace(myXML.item[0].menuName.toString());
// burger
XML type conversion 365
If you use the trace() method without specifying toString() or toXMLString(), the data
is converted using the toString() method by default, as this code shows:
var myXML:XML =
<order>
<item id='1' quantity='2'>
<menuName>burger</menuName>
<price>3.95</price>
</item>
<order>;
trace(myXML.item[0].menuName);
// burger
When using the trace() method to debug code, you will often want to use the
toXMLString() method so that the trace() method outputs more complete data.
Converting strings to XML objects
You can use the new XML() constructor to create an XML object from a string, as follows:
var x:XML = new XML("<a>test</a>");
If you attempt to convert a string to XML from a string that represents invalid XML or XML
that is not well formed, a run-time error is thrown, as follows:
var x:XML = new XML("<a>test"); // throws an error
Converting attribute values, names, and text values
from strings
All XML attribute values, names, and text values are String data types, and you may need to
convert these to other data types. For example, the following code uses the Number() function
to convert text values to numbers:
var myXML:XML =
<order>
<item>
<price>3.95</price>
</item>
<item>
<price>1.00</price>
</item>
</order>;
366 Working with XML
var total:XML = <total>0</total>;
myXML.appendChild(total);
for each (var item:XML in myXML.item)
{
myXML.total.children()[0] = Number(myXML.total.children()[0])
+ Number(item.price.children()[0]);
}
trace(myXML.total); // 4.35;
If this code did not use the Number() function, the code would interpret the + operator as the
string concatenation operator, and the trace() method in the last line would output the
following:
01.003.95
Reading external XML documents
You can use the URLLoader class to load XML data from a URL. To use the following code in
your applications, replace the XML_URL value in the example with a valid URL:
var myXML:XML = new XML();
var XML_URL:String = "http://www.example.com/Sample3.xml";
var myXMLURL:URLRequest = new URLRequest(XML_URL);
var myLoader:URLLoader = new URLLoader(myXMLURL);
myLoader.addEventListener("complete", xmlLoaded);
function xmlLoaded(event:Event):void
{
myXML = XML(myLoader.data);
trace("Data loaded.");
}
You can also use the XMLSocket class to set up an asynchronous XML socket connection
with a server. For more information, see the ActionScript 3.0 Language and Components
Reference.
Example: Loading RSS data from the Internet 367
Example: Loading RSS data from the
Internet
The RSSViewer sample application shows a number of features of working with XML in
ActionScript, including the following:
■ Using XML methods to traverse XML data in the form of an RSS feed.
■ Using XML methods to assemble XML data in the form of HTML to use in a text field.
The RSS format is widely used to syndicate news via XML. A simple RSS data file may look
like the following:
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>Alaska - Weather</title>
<link>http://www.nws.noaa.gov/alerts/ak.html</link>
<description>Alaska - Watches, Warnings and Advisories</description>
<item>
<title>
Short Term Forecast - Taiya Inlet, Klondike Highway (Alaska)
</title>
<link>
http://www.nws.noaa.gov/alerts/ak.html#A18.AJKNK.1900
</link>
<description>
Short Term Forecast Issued At: 2005-04-11T19:00:00
Expired At: 2005-04-12T01:00:00 Issuing Weather Forecast Office
Homepage: http://pajk.arh.noaa.gov
</description>
</item>
<item>
<title>
Short Term Forecast - Haines Borough (Alaska)
</title>
<link>
http://www.nws.noaa.gov/alerts/ak.html#AKZ019.AJKNOWAJK.190000
</link>
<description>
Short Term Forecast Issued At: 2005-04-11T19:00:00
Expired At: 2005-04-12T01:00:00 Issuing Weather Forecast Office
Homepage: http://pajk.arh.noaa.gov
</description>
</item>
</channel>
</rss>
368 Working with XML
The SimpleRSS application reads RSS data from the Internet, parses the data for headlines
(titles), links, and descriptions, and returns that data. The SimpleRSSUI class provides the UI
and calls the SimpleRSS class, which does all of the XML processing.
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The RSSViewer application files can be found in the
folder Samples/RSSViewer. The application consists of the following files:
Reading and parsing XML data
The RSSParser class includes an xmlLoaded() method that converts the input RSS data,
stored in the rssXML variable, into an string containing HTML-formatted output,
rssOutput.
Near the beginning of the method, code sets the default XML namespace if the source RSS
data includes a default namespace:
if (rssXML.namespace("") != undefined)
{
default xml namespace = rssXML.namespace("");
}
The next lines then loop through the contents of the source XML data, examining each
descendant property named item:
for each (var item:XML in rssXML..item)
{
File Description
RSSViewer.mxml
or
RSSViewer.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/rssViewer/
RSSParser.as
A class that contains methods that use E4X to
traverse RSS (XML) data and generate a
corresponding HTML representation.
RSSData/ak.rss A sample RSS file. The application is set up to
read RSS data from the web, at a Flex RSS
feed hosted by Adobe. However, you can easily
change the application to read RSS data from
this document, which uses a slightly different
schema than that of the Flex RSS feed.
Example: Loading RSS data from the Internet 369
var itemTitle:String = item.title.toString();
var itemDescription:String = item.description.toString();
var itemLink:String = item.link.toString();
outXML += buildItemHTML(itemTitle,
itemDescription,
itemLink);
}
The first three lines simply set string variables to represent the title, description and link
properties of the item property of the XML data. The next line then calls the
buildItemHTML() method to get HTML data in the form of an XMLList object, using the
three new string variables as parameters.
Assembling XMLList data
The HTML data (an XMLList object) is of the following form:
<b>itemTitle</b>
<p>
itemDescription
<br />
<a href="link">
<font color="#008000">More...</font>
</a>
</p>
The first lines of the method clear the default xml namespace:
default xml namespace = new Namespace();
The default xml namespace directive has function block-level scope. This means that the
scope of this declaration is the buildItemHTML() method.
The lines that follow assemble the XMLList, based on the string arguments passed to the
function:
var body:XMLList = new XMLList();
body += new XML("<b>" + itemTitle + "</b>");
var p:XML = new XML("<p>" + itemDescription + "</p>");
var link:XML = <a></a>;
link.@href = itemLink; // <link href="itemLinkString"></link>
link.font.@color = "#008000";
// <font color="#008000"></font></a>
// 0x008000 = green
link.font = "More...";
p.appendChild(<br/>);
p.appendChild(link);
body += p;
370 Working with XML
This XMLList object represents string data suitable for an ActionScript HTML text field.
The xmlLoaded() method uses the return value of the buildItemHTML() method and
converts it to a string:
XML.prettyPrinting = false;
rssOutput = outXML.toXMLString();
Extracting the title of the RSS feed and sending a
custom event
The xmlLoaded() method sets a rssTitle string variable, based on information in the source
RSS XML data:
rssTitle = rssXML.channel.title.toString();
Finally, the xmlLoaded() method generates an event, which notifies the application that the
data is parsed and available:
dataWritten = new Event("dataWritten", true);
371
12
CHAPTER 12
Display programming
Display programming in ActionScript 3.0 allows you to work with elements that appear on
the Stage of Adobe Flash Player 9. This chapter describes the basic concepts for working with
on-screen elements. You’ll learn the details about programmatically organizing visual
elements. You’ll also learn about creating your own custom classes for display objects.
Contents
Basics of display programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .372
Core display classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Advantages of the display list approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .379
Working with display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .382
Manipulating display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Masking display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
Animating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
Example: SpriteArranger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .423
372 Display programming
Basics of display programming
Introduction to display programming
Each application built with ActionScript 3.0 has a hierarchy of displayed objects known as the
display list. The display list contains all the visible elements in the application. Display
elements fall into one or more of the following groups:
■ The Stage
The Stage is the base container of display objects. Each application has one Stage object,
which contains all on-screen display objects. The Stage is the top-level container and is at
the top of the display list hierarchy:
Display Object
Container
Display ObjectDisplay Object
Container
Display Object
Container
Instance of
the main class of
the SWF file
StageStage
Display Object
ContainerDisplay Object
Display Object
Basics of display programming 373
Each SWF file has an associated ActionScript class, known as the main class of the SWF file.
When Flash Player opens a SWF file in an HTML page, Flash Player calls the constructor
function for that class and the instance that is created (which is always a type of display
object) is added as a child of the Stage object. The main class of a SWF file always extends
the Sprite class (for more information, see “Advantages of the display list approach”
on page 379).
You can access the Stage through the stage property of any DisplayObject instance. For
more information, see “Setting Stage properties” on page 389.
■ Display objects
In ActionScript 3.0, all elements that appear on screen in an application are types of
display objects. The flash.display package includes a DisplayObject class, which is a base
class extended by a number of other classes. These different classes represent different
types of display objects, such as vector shapes, movie clips, and text fields, to name a few.
For an overview of these classes, see “Advantages of the display list approach” on page 379.
■ Display object containers
Display object containers are special types of display objects that, in addition to having
their own visual representation, can also contain child objects that are also display objects.
The DisplayObjectContainer class is a subclass of the DisplayObject class. A
DisplayObjectContainer object can contain multiple display objects in its child list. For
example, the following illustration shows a type of DisplayObjectContainer object known
as a Sprite that contains various display objects:
In the context of discussing display objects, DisplayObjectContainer objects are also
known as display object containers or simply containers.
A SimpleButton object. This
type of display object has
different “up,” “down,” and
“over” states.
A Bitmap object. In this case, the
Bitmap object was loaded from
an external JPEG through a
Loader object.
A Shape object. The “picture
frame” contains a rounded
rectangle that is drawn in
ActionScript. This Shape object
has a Drop Shadow filter applied
to it.
A TextField object.
374 Display programming
Although all visible display objects inherit from the DisplayObject class, the type of each
is of a specific subclass of DisplayObject class. For example, there is a constructor function
for the Shape class or the Video class, but there is no constructor function for the
DisplayObject class.
As noted earlier, the Stage is a display object container.
Common display programming tasks
Since so much of ActionScript programming involves creating and manipulating visual
elements, there are numerous tasks that are related to display programming. This chapter
describes common tasks that apply to all display objects, including:
■ Working with the display list and display object containers
■ Adding display objects to the display list
■ Removing objects from the display list
■ Moving objects among display containers
■ Moving objects in front of or behind other objects
■ Working with the Stage
■ Setting the frame rate
■ Controlling Stage scaling
■ Working with full-screen mode
■ Handling display object events
■ Positioning display objects, including creating drag-and-drop interaction
■ Resizing, scaling, and rotating display objects
■ Applying blending modes, color transformations, and transparency to display objects
■ Masking display objects
■ Animating display objects
■ Loading external display content (such as SWF files or images)
Later chapters in this manual describe additional tasks for working with display objects. These
tasks include both tasks that apply to any display object and tasks associated with specific
types of display objects:
■ Drawing vector graphics with ActionScript on display objects, described in Chapter 14,
“Using the drawing API,” on page 447
■ Applying geometric transformations to display objects, described in Chapter 13,
“Working with geometry,” on page 431
Basics of display programming 375
■ Applying graphical filter effects such as blur, glow, drop shadow and more to display
objects, described in Chapter 15, “Filtering display objects,” on page 465
■ Working with MovieClip-specific characteristics, described in Chapter 16, “Working with
movie clips,” on page 491
■ Working with TextField objects, described in Chapter 17, “Working with text,” on
page 509
■ Working with bitmap graphics, described in Chapter 18, “Working with bitmaps,” on
page 539
■ Working with video elements, described in Chapter 19, “Working with video,” on
page 555
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Alpha: The color value representing the amount of transparency (or more correctly, the
amount of opacity) in a color. For example, a color with an alpha channel value of 60%
only shows 60% of its full strength, and is 40% transparent.
■ Bitmap graphic: A graphic that is defined in the computer as a grid (rows and columns) of
colored pixels. Commonly bitmap graphics include digital photos and similar images.
■ Blending mode: A specification of how the contents of two overlapping images should
interact. Commonly an opaque image on top of another image simply blocks the image
underneath so that it isn’t visible at all; however, different blending modes cause the colors
of the images to blend together in different ways so the resulting content is some
combination of the two images.
■ Display list: The hierarchy of display objects that will be rendered as visible screen content
by Flash Player. The Stage is the root of the display list, and all the display objects that are
attached to the Stage or one of its children form the display list (even if the object isn’t
actually rendered, for example if it’s outside the boundaries of the Stage).
■ Display object: An object which represents some type of visual content in Flash Player.
Only display objects can be included in the display list, and all display object classes are
subclasses of the DisplayObject class.
■ Display object container: A special type of display object which can contain child display
objects in addition to (generally) having its own visual representation.
■ Main class of the SWF file: The class that defines the behavior for the outermost display
object in a SWF file, which conceptually is the class for the SWF file itself. For instance, a
SWF created in Flash authoring has a “main timeline” which contains all other timelines;
the main class of the SWF file is the class of which the main timeline is an instance.
376 Display programming
■ Masking: A technique of hiding from view certain parts of an image (or conversely, only
allowing certain parts of an image to display). The hidden portions of the image become
transparent, so content underneath shows through. The term is related to painter’s
masking tape that is used to prevent paint from being applied to certain areas.
■ Stage: The visual container that is the base or background of all visual content in a SWF.
■ Transformation: An adjustment to a visual characteristic of a graphic, such as rotating the
object, altering its scale, skewing or distorting its shape, or altering its color.
■ Vector graphic: A graphic that is defined in the computer as lines and shapes drawn with
particular characteristics (such as thickness, length, size, angle, and position).
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because this chapter is about creating and manipulating visual content,
essentially all the code listings in this chapter create visual objects and display them on the
screen; testing the sample will involve viewing the result in Flash Player rather than viewing
values of variables as in previous chapters. To test the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the code displayed on the screen, and any trace() function calls
will display in the Output panel.
Techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
Core display classes 377
Core display classes
The ActionScript 3.0 flash.display package includes classes for visual objects that can appear
in Flash Player. The following illustration shows the subclass relationships of these core
display object classes.
The illustration shows the class inheritance of display object classes. Note that some of these
classes, specifically StaticText, TextField, and Video, are not in the flash.display package, but
they still inherit from the DisplayObject class.
All classes that extend the DisplayObject class inherit its methods and properties. For more
information, see “Properties and methods of the DisplayObject class” on page 382.
You can instantiate objects of the following classes contained in the flash.display package:
■ Bitmap—You use the Bitmap class to define bitmap objects, either loaded from external
files or rendered through ActionScript. You can load bitmaps from external files through
the Loader class. You can load GIF, JPG, or PNG files. You can also create a BitmapData
object with custom data and then create a Bitmap object that uses that data. You can use
the methods of the BitmapData class to alter bitmaps, whether they are loaded or created
in ActionScript. For more information, see “Loading display objects” on page 419 and
Chapter 18, “Working with bitmaps,” on page 539.
■ Loader—You use the Loader class to load external assets (either SWF files or graphics).
For more information, see “Loading display content dynamically” on page 418.
■ Shape—You use the Shape class to create vector graphics, such as rectangles, lines, circles,
and so on. For more information, see Chapter 14, “Using the drawing API,” on page 447.
■ SimpleButton—A SimpleButton object is the ActionScript representation of a Flash
button symbol. A SimpleButton instance has three button states: up, down, and over.
MorphShapeAVM1Movie Shape StaticText
DisplayObjectContainer TextField
Bitmap InteractiveObject
SimpleButton
StageLoader
MovieClip
Sprite
378 Display programming
■ Sprite—A Sprite object can contain graphics of its own, and it can contain child display
objects. (The Sprite class extends the DisplayObjectContainer class). For more
information, see “Working with display object containers” on page 383 and Chapter 14,
“Using the drawing API,” on page 447.
■ MovieClip—A MovieClip object is the ActionScript form of a movie clip symbol created
in Flash authoring. In practice, a MovieClip is similar to a Sprite object, except that it also
has a timeline. For more information, see Chapter 16, “Working with movie clips,” on
page 491.
The following classes, which are not in the flash.display package, are subclasses of the
DisplayObject class:
■ The TextField class, included in the flash.text package, is a display object for text display
and input. For more information, see Chapter 17, “Working with text,” on page 509.
■ The Video class, included in the flash.media package, is the display object used for
displaying video files. For more information, see Chapter 19, “Working with video,” on
page 555.
The following classes in the flash.display package extend the DisplayObject class, but you
cannot create instances of them. Instead, they serve as parent classes for other display objects,
combining common functionality into a single class.
■ AVM1Movie—The AVM1Movie class is used to represent loaded SWF files that are
authored in ActionScript 1.0 and 2.0.
■ DisplayObjectContainer—The Loader, Stage, Sprite, and MovieClip classes each extend
the DisplayObjectContainer class. For more information, see “Working with display
object containers” on page 383.
■ InteractiveObject—InteractiveObject is the base class for all objects used to interact with
the mouse and keyboard. SimpleButton, TextField, Video, Loader, Sprite, Stage, and
MovieClip objects are all subclasses of the InteractiveObject class. For more information
on creating mouse and keyboard interaction, see Chapter 21, “Capturing user input,” on
page 631.
■ MorphShape—These objects are created when you create a shape tween in the Flash
authoring tool. You cannot instantiate them using ActionScript, but they can be accessed
from the display list.
■ Stage—The Stage class extends the DisplayObjectContainer class. There is one Stage
instance for an application, and it is at the top of the display list hierarchy. To access the
Stage, use the stage property of any DisplayObject instance. For more information, see
“Setting Stage properties” on page 389.
Advantages of the display list approach 379
Also, the StaticText class, in the flash.text package, extends the DisplayObject class, but you
cannot create an instance of it in code. Static text fields are created only in Adobe Flash CS3
Professional.
Advantages of the display list approach
In ActionScript 3.0, there are separate classes for different types of display objects. In
ActionScript 1.0 and 2.0, many of the same types of objects are all included in one class: the
MovieClip class.
This individualization of classes and the hierarchical structure of display lists have the
following benefits:
■ More efficient rendering and reduced memory usage
■ Improved depth management
■ Full traversal of the display list
■ Off-list display objects
■ Easier subclassing of display objects
These benefits are described in the next sections.
More efficient rendering and smaller file sizes
In ActionScript 1.0 and 2.0, you could draw shapes only in a MovieClip object. In
ActionScript 3.0, there are simpler display object classes in which you can draw shapes.
Because these ActionScript 3.0 display object classes do not include the full set of methods
and properties that a MovieClip object includes, they are less taxing on memory and processor
resources.
For example, each MovieClip object includes properties for the timeline of the movie clip,
whereas a Shape object does not. The properties for managing the timeline can use a lot of
memory and processor resources. In ActionScript 3.0, using the Shape object results in better
performance. The Shape object has less overhead than the more complex MovieClip object.
Flash Player does not need to manage unused MovieClip properties, which improves speed
and reduces the memory footprint the object uses.
380 Display programming
Improved depth management
In ActionScript 1.0 and 2.0, depth was managed through a linear depth management scheme
and methods such as getNextHighestDepth().
ActionScript 3.0 includes the DisplayObjectContainer class, which has more convenient
methods and properties for managing the depth of display objects.
In ActionScript 3.0, when you move a display object to a new position in the child list of a
DisplayObjectContainer instance, the other children in the display object container are
repositioned automatically and assigned appropriate child index positions in the display
object container.
Also, in ActionScript 3.0 it is always possible to discover all of the child objects of any display
object container. Every DisplayObjectContainer instance has a numChildren property, which
lists the number of children in the display object container. And since the child list of a
display object container is always an indexed list, you can examine every object in the list from
index position 0 through the last index position (numChildren - 1). This was not possible
with the methods and properties of a MovieClip object in ActionScript 1.0 and 2.0.
In ActionScript 3.0, you can easily traverse the display list sequentially; there are no gaps in
the index numbers of a child list of a display object container. Traversing the display list and
managing the depth of objects is much easier than was possible in ActionScript 1.0 and 2.0.
In ActionScript 1.0 and 2.0, a movie clip could contain objects with intermittent gaps in the
depth order, which could make it difficult to traverse the list of object. In ActionScript 3.0,
each child list of a display object container is cached internally as an array, resulting in very
fast lookups (by index). Looping through all children of a display object container is also very
fast.
In ActionScript 3.0, you can also access children in a display object container by using the
getChildByName() method of the DisplayObjectContainer class.
Advantages of the display list approach 381
Full traversal of the display list
In ActionScript 1.0 and 2.0, you could not access some objects, such as vector shapes, that
were drawn in the Flash authoring tool. In ActionScript 3.0, you can access all objects on the
display list—both those created using ActionScript and all display objects created in the Flash
authoring tool. For details, see “Traversing the display list” on page 387.
Off-list display objects
In ActionScript 3.0, you can create display objects that are not on the visible display list.
These are known as off-list display objects. A display object is added to the visible display list
only when you call the addChild() or addChildAt() method of a DisplayObjectContainer
instance that has already been added to the display list.
You can use off-list display objects to assemble complex display objects, such as those that
have multiple display object containers containing multiple display objects. By keeping
display objects off-list, you can assemble complicated objects without using the processing
time to render these display objects. You can then add an off-list display object to the display
list when it is needed. Also, you can move a child of a display object container on and off the
display list and to any desired position in the display list at will.
Easier subclassing of display objects
In ActionScript 1.0 and 2.0, you would often have to add new MovieClip objects to a SWF
file to create basic shapes or to display bitmaps. In ActionScript 3.0, the DisplayObject class
includes many built-in subclasses, including Shape and Bitmap. Because the classes in
ActionScript 3.0 are more specialized for specific types of objects, it is easier to create basic
subclasses of the built-in classes.
For example, in order to draw a circle in ActionScript 2.0, you could create a CustomCircle
class that extends the MovieClip class when an object of the custom class is instantiated.
However, that class would also include a number of properties and methods from the
MovieClip class (such as totalFrames) that do not apply to the class. In ActionScript 3.0,
however, you can create a CustomCircle class that extends the Shape object, and as such does
not include the unrelated properties and methods that are contained in the MovieClip class.
The following code shows an example of a CustomCircle class:
import flash.display.*;
private class CustomCircle extends Shape
{
382 Display programming
var xPos:Number;
var yPos:Number;
var radius:Number;
var color:uint;
public function CustomCircle(xInput:Number,
yInput:Number,
rInput:Number,
colorInput:uint)
{
xPos = xInput;
yPos = yInput;
radius = rInput;
color = colorInput;
this.graphics.beginFill(color);
this.graphics.drawCircle(xPos, yPos, radius);
}
}
Working with display objects
Now that you understand the basic concepts of the Stage, display objects, display object
containers, and the display list, this section provides you with some more specific information
about working with display objects in ActionScript 3.0.
Properties and methods of the DisplayObject class
All display objects are subclasses of the DisplayObject class, and as such they inherit the
properties and methods of the DisplayObject class. The properties inherited are basic
properties that apply to all display objects. For example, each display object has an x property
and a y property that specifies the object’s position in its display object container.
You cannot create a DisplayObject instance using the DisplayObject class constructor. You
must create another type of object (an object that is a subclass of the DisplayObject class),
such as a Sprite, to instantiate an object with the new operator. Also, if you want to create a
custom display object class, you must create a subclass of one of the display object subclasses
that has a usable constructor function (such as the Shape class or the Sprite class). For more
information, see the DisplayObject class description in the ActionScript 3.0 Language and
Components Reference.
Working with display objects 383
Adding display objects to the display list
When you instantiate a display object, it will not appear on-screen (on the Stage) until you
add the display object instance to a display object container that is on the display list. For
example, in the following code, the myText TextField object would not be visible if you
omitted the last line of code. In the last line of code, the this keyword must refer to a display
object container that is already added to the display list.
import flash.display.*;
import flash.text.TextField;
var myText:TextField = new TextField();
myText.text = "Buenos dias.";
this.addChild(myText);
When you add any visual element to the Stage, that element becomes a child of the Stage
object. The first SWF file loaded in an application (for example, the one that you embed in an
HTML page) is automatically added as a child of the Stage. It can be an object of any type
that extends the Sprite class.
Any display objects that you create without using ActionScript—for example, by adding an
MXML tag in Adobe Flex Builder 2 or by placing an item on the Stage in Flash—are added to
the display list. Although you do not add these display objects through ActionScript, you can
access them through ActionScript. For example, the following code adjusts the width of an
object named button1 that was added in the authoring tool (not through ActionScript):
button1.width = 200;
Working with display object containers
If a DisplayObjectContainer object is deleted from the display list, or if it is moved or
transformed in some other way, each display object in the DisplayObjectContainer is also
deleted, moved, or transformed.
384 Display programming
A display object container is itself a type of display object—it can be added to another display
object container. For example, the following image shows a display object container,
pictureScreen, that contains one outline shape and four other display object containers (of
type PictureFrame):
In order to have a display object appear in the display list, you must add it to a display object
container that is on the display list. You do this by using the addChild() method or the
addChildAt() method of the container object. For example, without the final line of the
following code, the myTextField object would not be displayed:
var myTextField:TextField = new TextField();
myTextField.text = "hello";
this.root.addChild(myTextField);
In this code sample, this.root points to the MovieClip display object container that
contains the code. In your actual code, you may specify a different container.
Use the addChildAt() method to add the child to a specific position in the child list of the
display object container. These zero-based index positions in the child list relate to the
layering (the front-to-back order) of the display objects. For example, consider the following
three display objects. Each object was created from a custom class called Ball.
A shape defining
the border of the
pictureScreen
display object
container
Four display object
containers that are
children of the
pictureScreen
object
Working with display objects 385
The layering of these display objects in their container can be adjusted using the
addChildAt() method. For example, consider the following code:
ball_A = new Ball(0xFFCC00, "a");
ball_A.name = "ball_A";
ball_A.x = 20;
ball_A.y = 20;
container.addChild(ball_A);
ball_B = new Ball(0xFFCC00, "b");
ball_B.name = "ball_B";
ball_B.x = 70;
ball_B.y = 20;
container.addChild(ball_B);
ball_C = new Ball(0xFFCC00, "c");
ball_C.name = "ball_C";
ball_C.x = 40;
ball_C.y = 60;
container.addChildAt(ball_C, 1);
After executing this code, the display objects are positioned as follows in the container
DisplayObjectContainer object. Notice the layering of the objects.
To reposition an object to the top of the display list, simply re-add it to the list. For example,
after the previous code, to move ball_A to the top of the stack, use this line of code:
container.addChild(ball_A);
This code effectively removes ball_A from its location in container’s display list, and re-
adds it to the top of the list—which has the end result of moving it to the top of the stack.
You can use the getChildAt() method to verify the layer order of the display objects. The
getChildAt() method returns child objects of a container based on the index number you
pass it. For example, the following code reveals names of display objects at different positions
in the child list of the container DisplayObjectContainer object:
trace(container.getChildAt(0).name); // ball_A
trace(container.getChildAt(1).name); // ball_C
trace(container.getChildAt(2).name); // ball_B
386 Display programming
If you remove a display object from the parent container’s child list, the higher elements on
the list each move down a position in the child index. For example, continuing with the
previous code, the following code shows how the display object that was at position 2 in the
container DisplayObjectContainer moves to position 1 if a display object that is lower in
the child list is removed:
container.removeChild(ball_C);
trace(container.getChildAt(0).name); // ball_A
trace(container.getChildAt(1).name); // ball_B
The removeChild() and removeChildAt() methods do not delete a display object instance
entirely. They simply remove it from the child list of the container. The instance can still be
referenced by another variable. (Use the delete operator to completely remove an object.)
Because a display object has only one parent container, you can add an instance of a display
object to only one display object container. For example, the following code shows that the
display object tf1 can exist in only one container (in this case, a Sprite, which extends the
DisplayObjectContainer class):
tf1:TextField = new TextField();
tf2:TextField = new TextField();
tf1.name = "text 1";
tf2.name = "text 2";
container1:Sprite = new Sprite();
container2:Sprite = new Sprite();
container1.addChild(tf1);
container1.addChild(tf2);
container2.addChild(tf1);
trace(container1.numChildren); // 1
trace(container1.getChildAt(0).name); // text 2
trace(container2.numChildren); // 1
trace(container2.getChildAt(0).name); // text 1
If you add a display object that is contained in one display object container to another display
object container, it is removed from the first display object container’s child list.
In addition to the methods described above, the DisplayObjectContainer class defines several
methods for working with child display objects, including the following:
■ contains(): Determines whether a display object is a child of a DisplayObjectContainer.
■ getChildByName(): Retrieves a display object by name.
■ getChildIndex(): Returns the index position of a display object.
■ setChildIndex(): Changes the position of a child display object.
Working with display objects 387
■ swapChildren(): Swaps the front-to-back order of two display objects.
■ swapChildrenAt(): Swaps the front-to-back order of two display objects, specified by
their index values.
For more information, see the relevant entries in the ActionScript 3.0 Language and
Components Reference.
Recall that a display object that is off the display list—one that is not included in a display
object container that is a child of the Stage—is known as an off-list display object.
Traversing the display list
As you’ve seen, the display list is a tree structure. At the top of the tree is the Stage, which can
contain multiple display objects. Those display objects that are themselves display object
containers can contain other display objects, or display object containers.
Display Object
Container
Display ObjectDisplay Object
Container
Display Object
Container
Instance of
the main class of
the SWF file
StageStage
Display Object
ContainerDisplay Object
Display Object
388 Display programming
The DisplayObjectContainer class includes properties and methods for traversing the display
list, by means of the child lists of display object containers. For example, consider the
following code, which adds two display objects, title and pict, to the container object
(which is a Sprite, and the Sprite class extends the DisplayObjectContainer class):
var container:Sprite = new Sprite();
var title:TextField = new TextField();
title.text = "Hello";
var pict:Loader = new Loader();
var url:URLRequest = new URLRequest("banana.jpg");
pict.load(url);
pict.name = "banana loader";
container.addChild(title);
container.addChild(pict);
The getChildAt() method returns the child of the display list at a specific index position:
trace(container.getChildAt(0) is TextField); // true
You can also access child objects by name. Each display object has a name property, and if you
don’t assign it, Flash Player assigns a default value, such as "instance1". For example, the
following code shows how to use the getChildByName() method to access a child display
object with the name "banana loader":
trace(container.getChildByName("banana loader") is Loader); // true
Using the getChildByName() method can result in slower performance than using the
getChildAt() method.
Since a display object container can contain other display object containers as child objects in
its display list, you can traverse the full display list of the application as a tree. For example, in
the code excerpt shown earlier, once the load operation for the pict Loader object is
complete, the pict object will have one child display object, which is the bitmap, loaded. To
access this bitmap display object, you can write pict.getChildAt(0). You can also write
container.getChildAt(0).getChildAt(0) (since container.getChildAt(0) == pict).
The following function provides an indented trace() output of the display list from a display
object container:
function traceDisplayList(container:DisplayObjectContainer,
indentString:String = ""):void
{
var child:DisplayObject;
for (var i:uint=0; i < container.numChildren; i++)
{
child = container.getChildAt(i);
trace(indentString, child, child.name);
if (container.getChildAt(i) is DisplayObjectContainer)
{
Working with display objects 389
traceDisplayList(DisplayObjectContainer(child), indentString + "")
}
}
}
Setting Stage properties
The Stage class overrides most properties and methods of the DisplayObject class. If you call
one of these overridden properties or methods, Flash Player throws an exception. For example,
the Stage object does not have x or y properties, since its position is fixed as the main
container for the application. The x and y properties refer to the position of a display object
relative to its container, and since the Stage is not contained in another display object
container, these properties do not apply.
Controlling the playback frame rate
The framerate property of the Stage class is used to set the frame rate for all SWF files loaded
into the application. For more information, see the ActionScript 3.0 Language and Components
Reference.
Controlling Stage scaling
When a Flash Player screen is resized, Flash Player automatically adjusts the Stage contents to
compensate. The Stage class’s scaleMode property determines how the Stage contents are
adjusted. This property can be set to four different values, defined as constants in the
flash.display.StageScaleMode class.
For three of the scaleMode values (StageScaleMode.EXACT_FIT,
StageScaleMode.SHOW_ALL, and StageScaleMode.NO_BORDER), Flash Player will scale the
contents of the Stage to fit within its boundaries.The three options differ in determining how
the scaling is accomplished:
■ StageScaleMode.EXACT_FIT scales the SWF proportionally.
■ StageScaleMode.SHOW_ALL determines whether a border appears, like the black bars
that appear when viewing a wide-screen movie on a standard television.
■ StageScaleMode.NO_BORDER determines whether the content can be partially cropped
or not.
NOTE
Some properties and methods of the Stage class are not available to display objects that
are not in the same security sandbox as the first SWF file loaded. For details, see “Stage
security” on page 781.
390 Display programming
Alternatively, if scaleMode is set to StageScaleMode.NO_SCALE, the Stage contents maintain
their defined size when the viewer resizes the Flash Player window. In this scale mode only, the
width and height properties of the Stage class can be used to determine the actual pixel
dimensions of the resized Flash Player window. (In the other scale modes, the stageWidth
and stageHeight properties always reflect the original width and height of the SWF.) In
addition, when scaleMode is set to StageScaleMode.NO_SCALE and the SWF file is resized,
the Stage class’s resize event is dispatched, allowing you to make adjustments accordingly.
Consequently, having scaleMode set to StageScaleMode.NO_SCALE allows you to have
greater control over how the screen contents adjust to the window resizing if you desire. For
example, in a SWF containing a video and a control bar, you might want to make the control
bar stay the same size when the Stage is resized, and only change the size of the video window
to accommodate the Stage size change. This is demonstrated in the following example:
// videoScreen is a display object (e.g. a Video instance) containing a
// video; it is positioned at the top-left corner of the Stage, and
// it should resize when the SWF resizes.
// controlBar is a display object (e.g. a Sprite) containing several
// buttons; it should stay positioned at the bottom-left corner of the
// Stage (below videoScreen) and it should not resize when the SWF
// resizes.
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
var swfStage:Stage = videoScreen.stage;
swfStage.scaleMode = StageScaleMode.NO_SCALE;
swfStage.align = StageAlign.TOP_LEFT;
function resizeDisplay(event:Event):void
{
var swfWidth:int = swfStage.stageWidth;
var swfHeight:int = swfStage.stageHeight;
// Resize the video window.
var newVideoHeight:Number = swfHeight - controlBar.height;
videoScreen.height = newVideoHeight;
videoScreen.scaleX = videoScreen.scaleY;
// Reposition the control bar.
controlBar.y = newVideoHeight;
}
swfStage.addEventListener(Event.RESIZE, resizeDisplay);
Working with display objects 391
Working with full-screen mode
Full-screen mode allows you to make a SWF fill a viewer’s entire monitor, without any
borders, menu bars, and so forth. The Stage class’s displayState property is used to toggle
full-screen mode on and off for a SWF. The displayState property can be set to one of the
values defined by the constants in the flash.display.StageDisplayState class. To turn on full-
screen mode, set displayState to StageDisplayState.FULL_SCREEN:
// mySprite is a Sprite instance, already added to the display list
mySprite.stage.displayState = StageDisplayState.FULL_SCREEN;
To exit full-screen mode, set the displayState property to StageDisplayState.NORMAL:
mySprite.stage.displayState = StageDisplayState.NORMAL;
In addition, a user can choose to leave full-screen mode by switching focus to a different
window or by using one of several key combinations: the Esc key (all platforms), Ctrl-W
(Windows), Command-W (Mac), or Alt-F4 (Windows).
Stage scaling behavior for full-screen mode is the same as under normal mode; the scaling is
controlled by the Stage class’s scaleMode property. As always, if the scaleMode property is set
to StageScaleMode.NO_SCALE, the Stage’s stageWidth and stageHeight properties change
to reflect the size of the screen area occupied by the SWF (the entire screen, in this case).
You can use the Stage class’s fullScreen event to detect and respond when full-screen mode
is turned on or off. For example, you might want to reposition, add, or remove items from the
screen when entering or leaving full-screen mode, as in this example:
import flash.events.FullScreenEvent;
function fullScreenRedraw(event:FullScreenEvent):void
{
if (event.fullScreen)
{
// Remove input text fields.
// Add a button that closes full-screen mode.
}
else
{
// Re-add input text fields.
// Remove the button that closes full-screen mode.
}
}
mySprite.stage.addEventListener(FullScreenEvent.FULL_SCREEN,
fullScreenRedraw);
As this code shows, the event object for the fullScreen event is an instance of the
flash.events.FullScreenEvent class, which includes a fullScreen property indicating whether
full-screen mode is enabled (true) or not (false).
392 Display programming
When working with full-screen mode in ActionScript, you’ll want to keep the following
considerations in mind:
■ Full-screen mode can only be initiated through ActionScript in response to a mouse click
(including right-click) or keypress.
■ For users with multiple monitors, the SWF content will expand to fill only one monitor.
Flash Player uses a metric to determine which monitor contains the greatest portion of the
SWF, and uses that monitor for full-screen mode.
■ For a SWF file embedded in an HTML page, the HTML code to embed Flash Player
must include a param tag and embed attribute with the name allowFullScreen and value
true, like this:
<object>
...
<param name="allowFullScreen" value="true" />
<embed ... allowfullscreen="true" />
</object>
If you are using JavaScript in a web page to generate the SWF-embedding tags, you must
alter the JavaScript to add the allowFullScreen param tag and attribute. For example, if
your HTML page uses the AC_FL_RunContent() function (which is used by both Flex
Builder and Flash-generated HTML pages), you should add the allowFullScreen
parameter to that function call as follows:
AC_FL_RunContent(
...
'allowFullScreen','true',
...
); //end AC code
This does not apply to SWF files running in the stand-alone Flash Player.
■ All keyboard-related ActionScript, such as keyboard events and text entry in TextField
instances, is disabled in full-screen mode. The exception is the keyboard shortcuts that
close full-screen mode.
There are a few additional security-related restrictions you’ll want to understand, too. These
are described in “Security sandboxes” on page 770.
Handling events for display objects
The DisplayObject class inherits from the EventDispatcher class. This means that every
display object can participate fully in the event model (described in Chapter 10, “Handling
events,” on page 313). Every display object can use its addEventListener() method—
inherited from the EventDispatcher class—to listen for a particular event, but only if the
listening object is part of the event flow for that event.
Working with display objects 393
When Flash Player dispatches an event object, that event object makes a round-trip journey
from the Stage to the display object where the event occurred. For example, if a user clicks on
a display object named child1, Flash Player dispatches an event object from the Stage
through the display list hierarchy down to the child1 display object.
The event flow is conceptually divided into three phases, as illustrated in this diagram:
For more information, see Chapter 10, “Handling events,” on page 313.
One important issue to keep in mind when working with display object events is the effect
that event listeners can have on whether display objects are automatically removed from
memory (garbage collected) when they’re removed from the display list. If a display object has
objects subscribed as listeners to its events, that display object will not be removed from
memory even when it’s removed from the display list, because it will still have references to
those listener objects. For more information, see “Managing event listeners” on page 331.
Choosing a DisplayObject subclass
With several options to choose from, one of the important decisions you’ll make when you’re
working with display objects is which display object to use for what purpose. Here are some
guidelines to help you decide. These same suggestions apply whether you need an instance of
a class or you’re choosing a base class for a class you’re creating:
■ If you don’t need an object that can be a container for other display objects (that is, you
just need one that serves as a stand-alone screen element), choose one of these
DisplayObject or InteractiveObject subclasses, depending on what it will be used for:
■ Bitmap for displaying a bitmap image.
■ TextField for adding text.
■ Video for displaying video.
Stage
Parent Node
Child1 Node Child2 Node
Capture
Phase
Bubbling
Phase
Target Phase
394 Display programming
■ Shape for a “canvas” for drawing content on-screen. In particular, if you want to create
an instance for drawing shapes on the screen, and it won’t be a container for other
display objects, you’ll gain significant performance benefits using Shape instead of
Sprite or MovieClip.
■ MorphShape, StaticText, or SimpleButton for Flash authoring-specific items. (You
can’t create instances of these classes programmatically, but you can create variables
with these data types to refer to items created using the Flash authoring program.)
■ If you need a variable to refer to the main Stage, use the Stage class as its data type.
■ If you need a container for loading an external SWF file or image file, use a Loader
instance. The loaded content will be added to the display list as a child of the Loader
instance. Its data type will depend on the nature of the loaded content, as follows:
■ A loaded image will be a Bitmap instance.
■ A loaded SWF file written in ActionScript 3.0 will be a Sprite or MovieClip instance
(or an instance of a subclass of those classes, as specified by the content creator).
■ A loaded SWF file written in ActionScript 1.0 or ActionScript 2.0 will be an
AVM1Movie instance.
■ If you need an object to serve as a container for other display objects (whether or not
you’ll also be drawing onto the display object using ActionScript), choose one of the
DisplayObjectContainer subclasses:
■ Sprite if the object will be created using only ActionScript, or as the base class for a
custom display object that will be created and manipulated solely with ActionScript.
■ MovieClip if you’re creating a variable to refer to a movie clip symbol created in the
Flash authoring tool.
■ If you are creating a class that will be associated with a movie clip symbol in the Flash
library, choose one of these DisplayObjectContainer subclasses as your class’s base class:
■ MovieClip if the associated movie clip symbol has content on more than one frame
■ Sprite if the associated movie clip symbol has content only on the first frame
Manipulating display objects 395
Manipulating display objects
Regardless of which display object you choose to use, there are a number of manipulations
that all display objects have in common as elements that are displayed on the screen. For
example, they can all be positioned on the screen, moved forward or backward in the stacking
order of display objects, scaled, rotated, and so forth. Because all display objects inherit this
functionality from their common base class (DisplayObject), this functionality behaves the
same whether you’re manipulating a TextField instance, a Video instance, a Shape instance, or
any other display object. The following sections detail several of these common display object
manipulations.
Changing position
The most basic manipulation to any display object is positioning it on the screen. To set a
display object’s position, change the object’s x and y properties.
myShape.x = 17;
myShape.y = 212;
The display object positioning system treats the Stage as a Cartesian coordinate system (the
common grid system with a horizontal x axis and vertical y axis). The origin of the coordinate
system (the 0,0 coordinate where the x and y axes meet) is at the top-left corner of the Stage.
From there, x values are positive going right and negative going left, while (in contrast to
typical graphing systems) y values are positive going down and negative going up. For
example, the previous lines of code move the object myShape to the x coordinate 17 (17 pixels
to the right of the origin) and y coordinate 212 (212 pixels below the origin).
By default, when a display object is created using ActionScript, the x and y properties are both
set to 0, placing the object at the top-left corner of its parent content.
Changing position relative to the Stage
It’s important to remember that the x and y properties always refer to the position of the
display object relative to the 0,0 coordinate of its parent display object’s axes. So for a Shape
instance (such as a circle) contained inside a Sprite instance, setting the Shape object’s x and y
properties to 0 will place the circle at the top-left corner of the Sprite, which is not necessarily
the top-left corner of the Stage. To position an object relative to the global Stage coordinates,
you can use the globalToLocal() method of any display object to convert coordinates from
global (Stage) coordinates to local (display object container) coordinates, like this:
// Position the shape at the top-left corner of the Stage,
// regardless of where its parent is located.
396 Display programming
// Create a Sprite, positioned at x:200 and y:200.
var mySprite:Sprite = new Sprite();
mySprite.x = 200;
mySprite.y = 200;
this.addChild(mySprite);
// Draw a dot at the Sprite's 0,0 coordinate, for reference.
mySprite.graphics.lineStyle(1, 0x000000);
mySprite.graphics.beginFill(0x000000);
mySprite.graphics.moveTo(0, 0);
mySprite.graphics.lineTo(1, 0);
mySprite.graphics.lineTo(1, 1);
mySprite.graphics.lineTo(0, 1);
mySprite.graphics.endFill();
// Create the circle Shape instance.
var circle:Shape = new Shape();
mySprite.addChild(circle);
// Draw a circle with radius 50 and center point at x:50, y:50 in the Shape.
circle.graphics.lineStyle(1, 0x000000);
circle.graphics.beginFill(0xff0000);
circle.graphics.drawCircle(50, 50, 50);
circle.graphics.endFill();
// Move the Shape so its top-left corner is at the Stage's 0, 0 coordinate.
var stagePoint:Point = new Point(0, 0);
var targetPoint:Point = mySprite.globalToLocal(stagePoint);
circle.x = targetPoint.x;
circle.y = targetPoint.y;
You can likewise use the DisplayObject class’s localToGlobal() method to convert local
coordinates to Stage coordinates.
Creating drag-and-drop interaction
One common reason for moving a display object is to create a drag-and-drop interaction, so
that when the user clicks an object, the object moves as the mouse moves, until the mouse
button is released. Drag-and-drop interaction can be created in two ways in ActionScript. In
either case, two mouse events are used: when the mouse button is pressed down, the object is
told to follow the mouse cursor, and when it’s released, the object is told to stop following the
mouse cursor.
The first way, using the startDrag() method, is simpler, but more limited. When the mouse
button is pressed, the startDrag() method of the display object to be dragged is called.
When the mouse button is released, the stopDrag() method is called.
// This code creates a drag-and-drop interaction using the startDrag()
Manipulating display objects 397
// technique.
// square is a DisplayObject (e.g. a MovieClip or Sprite instance).
import flash.events.MouseEvent;
// This function is called when the mouse button is pressed.
function startDragging(event:MouseEvent):void
{
square.startDrag();
}
// This function is called when the mouse button is released.
function stopDragging(event:MouseEvent):void
{
square.stopDrag();
}
square.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
square.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
This technique suffers from one fairly significant limitation: only one item at a time can be
dragged using startDrag(). If one display object is being dragged and the startDrag()
method is called on another display object, the first display object stops following the mouse
immediately. For example, if the startDragging() function is changed as shown here, only
the circle object will be dragged, in spite of the square.startDrag() method call:
function startDragging(event:MouseEvent):void
{
square.startDrag();
circle.startDrag();
}
As a consequence of the fact that only one object can be dragged at a time using
startDrag(), the stopDrag() method can be called on any display object and it stops
whatever object is currently being dragged.
If you need to drag more than one display object, or to avoid the possibility of conflicts where
more than one object might potentially use startDrag(), it’s best to use the mouse-following
technique to create the dragging effect. With this technique, when the mouse button is
pressed, a function is subscribed as a listener to the mouseMove event of the Stage. This
function, which is then called every time the mouse moves, causes the dragged object to jump
to the x, y coordinate of the mouse. Once the mouse button is released, the function is
unsubscribed as a listener, meaning it is no longer called when the mouse moves and the
object stops following the mouse cursor. Here is some code that demonstrates this technique:
// This code creates a drag-and-drop interaction using the mouse-following
// technique.
// circle is a DisplayObject (e.g. a MovieClip or Sprite instance).
398 Display programming
import flash.events.MouseEvent;
var offsetX:Number;
var offsetY:Number;
// This function is called when the mouse button is pressed.
function startDragging(event:MouseEvent):void
{
// Record the difference (offset) between where
// the cursor was when the mouse button was pressed and the x, y
// coordinate of the circle when the mouse button was pressed.
offsetX = event.stageX - circle.x;
offsetY = event.stageY - circle.y;
// tell Flash Player to start listening for the mouseMove event
stage.addEventListener(MouseEvent.MOUSE_MOVE, dragCircle);
}
// This function is called when the mouse button is released.
function stopDragging(event:MouseEvent):void
{
// Tell Flash Player to stop listening for the mouseMove event.
stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragCircle);
}
// This function is called every time the mouse moves,
// as long as the mouse button is pressed down.
function dragCircle(event:MouseEvent):void
{
// Move the circle to the location of the cursor, maintaining
// the offset between the cursor’s location and the
// location of the dragged object.
circle.x = event.stageX - offsetX;
circle.y = event.stageY - offsetY;
// Instruct Flash Player to refresh the screen after this event.
event.updateAfterEvent();
}
circle.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
circle.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
Manipulating display objects 399
In addition to making a display object follow the mouse cursor, a common part of drag-and-
drop interaction includes moving the dragged object to the front of the display, so that it
appears to be floating above all the other objects. For example, suppose you have two objects,
a circle and a square, that both have a drag-and-drop interaction. If the circle happens to be
below the square on the display list, and you click and drag the circle so that the cursor is over
the square, the circle will appear to slide behind the square, which breaks the drag-and-drop
illusion. Instead, you can make it so that when the circle is clicked, it moves to the top of the
display list, and thus always appears on top of any other content.
The following code (adapted from the previous example) creates a drag-and-drop interaction
for two display objects, a circle and a square. Whenever the mouse button is pressed over
either one, that item is moved to the top of the Stage’s display list, so that the dragged item
always appears on top. Code that is new or changed from the previous listing appears in
boldface.
// This code creates a drag-and-drop interaction using the mouse-following
// technique.
// circle and square are DisplayObjects (e.g. MovieClip or Sprite
// instances).
import flash.display.DisplayObject;
import flash.events.MouseEvent;
var offsetX:Number;
var offsetY:Number;
var draggedObject:DisplayObject;
// This function is called when the mouse button is pressed.
function startDragging(event:MouseEvent):void
{
// remember which object is being dragged
draggedObject = DisplayObject(event.target);
// Record the difference (offset) between where the cursor was when
// the mouse button was pressed and the x, y coordinate of the
// dragged object when the mouse button was pressed.
offsetX = event.stageX - draggedObject.x;
offsetY = event.stageY - draggedObject.y;
// move the selected object to the top of the display list
stage.addChild(draggedObject);
// Tell Flash Player to start listening for the mouseMove event.
stage.addEventListener(MouseEvent.MOUSE_MOVE, dragObject);
}
// This function is called when the mouse button is released.
function stopDragging(event:MouseEvent):void
400 Display programming
{
// Tell Flash Player to stop listening for the mouseMove event.
stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragObject);
}
// This function is called every time the mouse moves,
// as long as the mouse button is pressed down.
function dragObject(event:MouseEvent):void
{
// Move the dragged object to the location of the cursor, maintaining
// the offset between the cursor’s location and the location
// of the dragged object.
draggedObject.x = event.stageX - offsetX;
draggedObject.y = event.stageY - offsetY;
// Instruct Flash Player to refresh the screen after this event.
event.updateAfterEvent();
}
circle.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
circle.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
square.addEventListener(MouseEvent.MOUSE_DOWN, startDragging);
square.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
To extend this effect further, such as for a game where tokens or cards are moved among piles,
you could add the dragged object to the Stage’s display list when it’s “picked up,” and then add
it to another display list—such as the “pile” where it is dropped—when the mouse button is
released.
Finally, to enhance the effect, you could apply a drop shadow filter to the display object when
it is clicked (when you start dragging it) and remove the drop shadow when the object is
released. For details on using the drop shadow filter and other display object filters in
ActionScript, see Chapter 15, “Filtering display objects,” on page 465.
Panning and scrolling display objects
If you have a display object that is too large for the area in which you want it to display it, you
can use the scrollRect property to define the viewable area of the display object. In
addition, by changing the scrollRect property in response to user input, you can cause the
content to pan left and right or scroll up and down.
Manipulating display objects 401
The scrollRect property is an instance of the Rectangle class, which is a class that combines
the values needed to define a rectangular area as a single object. To initially define the viewable
area of the display object, create a new Rectangle instance and assign it to the display object’s
scrollRect property. Later, to scroll or pan, you read the scrollRect property into a
separate Rectangle variable, and change the desired property (for instance, change the
Rectangle instance’s x property to pan or y property to scroll). Then you reassign that
Rectangle instance to the scrollRect property to notify the display object of the changed
value.
For example, the following code defines the viewable area for a TextField object named
bigText that is too tall to fit in the SWF file’s boundaries. When the two buttons named up
and down are clicked, they call functions that cause the contents of the TextField object to
scroll up or down by modifying the y property of the scrollRect Rectangle instance.
import flash.events.MouseEvent;
import flash.geom.Rectangle;
// Define the initial viewable area of the TextField instance:
// left: 0, top: 0, width: TextField’s width, height: 350 pixels.
bigText.scrollRect = new Rectangle(0, 0, bigText.width, 350);
// Cache the TextField as a bitmap to improve performance.
bigText.cacheAsBitmap = true;
// called when the "up" button is clicked
function scrollUp(event:MouseEvent):void
{
// Get access to the current scroll rectangle.
var rect:Rectangle = bigText.scrollRect;
// Decrease the y value of the rectangle by 20, effectively
// shifting the rectangle down by 20 pixels.
rect.y -= 20;
// Reassign the rectangle to the TextField to "apply" the change.
bigText.scrollRect = rect;
}
// called when the "down" button is clicked
function scrollDown(event:MouseEvent):void
{
// Get access to the current scroll rectangle.
var rect:Rectangle = bigText.scrollRect;
// Increase the y value of the rectangle by 20, effectively
// shifting the rectangle up by 20 pixels.
rect.y += 20;
// Reassign the rectangle to the TextField to "apply" the change.
bigText.scrollRect = rect;
}
402 Display programming
up.addEventListener(MouseEvent.CLICK, scrollUp);
down.addEventListener(MouseEvent.CLICK, scrollDown);
As this example illustrates, when you work with the scrollRect property of a display object,
it’s best to specify that Flash Player should cache the display object’s content as a bitmap, using
the cacheAsBitmap property. When you do so, Flash Player doesn’t have to re-draw the entire
contents of the display object each time it is scrolled, and can instead use the cached bitmap
to render the necessary portion directly to the screen. For details, see “Caching display
objects” on page 405.
Manipulating size and scaling objects
You can measure and manipulate the size of a display object in two ways, using either the
dimension properties (width and height) or the scale properties (scaleX and scaleY).
Every display object has a width property and a height property, which are initially set to the
size of the object in pixels. You can read the values of those properties to measure the size of
the display object. You can also specify new values to change the size of the object, as follows:
// Resize a display object.
square.width = 420;
square.height = 420;
// Determine the radius of a circle display object.
var radius:Number = circle.width / 2;
Changing the height or width of a display object causes the object to scale, meaning its
contents stretch or squeeze to fit in the new area. If the display object contains only vector
shapes, those shapes will be redrawn at the new scale, with no loss in quality. Any bitmap
graphic elements in the display object will be scaled rather than redrawn. So, for example, a
digital photo whose width and height are increased beyond the actual dimensions of the pixel
information in the image will be pixelated, making it look jagged.
When you change the width or height properties of a display object, Flash Player updates
the scaleX and scaleY properties of the object as well. These properties represent the relative
size of the display object compared to its original size. The scaleX and scaleY properties use
fraction (decimal) values to represent percentage. For example, if a display object’s width has
been changed so that it’s half as wide as its original size, the object’s scaleX property will have
the value .5, meaning 50 percent. If its height has been doubled, its scaleY property will
have the value 2, meaning 200 percent.
// circle is a display object whose width and height are 150 pixels.
// At original size, scaleX and scaleY are 1 (100%).
trace(circle.scaleX); // output: 1
trace(circle.scaleY); // output: 1
Manipulating display objects 403
// When you change the width and height properties,
// Flash Player changes the scaleX and scaleY properties accordingly.
circle.width = 100;
circle.height = 75;
trace(circle.scaleX); // output: 0.6622516556291391
trace(circle.scaleY); // output: 0.4966887417218543
Size changes are not proportional. In other words, if you change the height of a square but
not its width, its proportions will no longer be the same, and it will be a rectangle instead of a
square. If you want to make relative changes to the size of a display object, you can set the
values of the scaleX and scaleY properties to resize the object, as an alternative to setting the
width or height properties. For example, this code changes the width of the display object
named square, and then alters the vertical scale (scaleY) to match the horizontal scale, so
that the size of the square stays proportional.
// Change the width directly.
square.width = 150;
// Change the vertical scale to match the horizontal scale,
// to keep the size proportional.
square.scaleY = square.scaleX;
Controlling distortion when scaling
Normally when a display object is scaled (for example, stretched horizontally), the resulting
distortion is spread equally across the object, so that each part is stretched the same amount.
For graphics and design elements, this is probably what you want. However, sometimes it’s
preferable to have control over which portions of the display object stretch and which
portions remain unchanged. One common example of this is a button that’s a rectangle with
rounded corners. With normal scaling, the corners of the button will stretch, making the
corner radius change as the button resizes.
404 Display programming
However, in this case it would be preferable to have control over the scaling—to be able to
designate certain areas which should scale (the straight sides and middle) and areas which
shouldn’t (the corners)—so that scaling happens without visible distortion.
You can use 9-slice scaling (Scale-9) to create display objects where you have control over how
the objects scale. With 9-slice scaling, the display object is divided into nine separate
rectangles (a 3 by 3 grid, like the grid of a tic-tac-toe board). The rectangles aren’t necessarily
the same size—you designate where the grid lines are placed. Any content that lies in the four
corner rectangles (such as the rounded corners of a button) will not be stretched or
compressed when the display object scales. The top-center and bottom-center rectangles will
scale horizontally but not vertically, while the left-middle and right-middle rectangles will
scale vertically but not horizontally. The center rectangle will scale both horizontally and
vertically.
Keeping this in mind, if you’re creating a display object and you want certain content to never
scale, you just have to make sure that the dividing lines of the 9-slice scaling grid are placed so
that the content ends up in one of the corner rectangles.
In ActionScript, setting a value for the scale9Grid property of a display object turns on 9-
slice scaling for the object and defines the size of the rectangles in the object’s Scale-9 grid. You
use an instance of the Rectangle class as the value for the scale9Grid property, as follows:
myButton.scale9Grid = new Rectangle(32, 27, 71, 64);
Manipulating display objects 405
The four parameters of the Rectangle constructor are the x coordinate, y coordinate, width,
and height. In this example, the rectangle’s top-left corner is placed at the point x: 32, y: 27 on
the display object named myButton. The rectangle is 71 pixels wide and 65 pixels tall (so its
right edge is at the x coordinate 103 on the display object and its bottom edge is at the y
coordinate 92 on the display object).
The actual area contained in the region defined by the Rectangle instance represents the
center rectangle of the Scale-9 grid. The other rectangles are calculated by Flash Player by
extending the sides of the Rectangle instance, as shown here:
In this case, as the button scales up or down, the rounded corners will not stretch or compress,
but the other areas will adjust to accommodate the scaling.
Caching display objects
As your designs in Flash grow in size, whether you are creating an application or complex
scripted animations, you need to consider performance and optimization. When you have
content that remains static (such as a rectangle Shape instance), Flash does not optimize the
content. Therefore, when you change the position of the rectangle, Flash redraws the entire
Shape instance.
myButton.width = 131;
myButton.height = 106;
myButton.width = 73;
myButton.height = 69;
myButton.width = 54;
myButton.height = 141;
406 Display programming
You can cache specified display objects to improve the performance of your SWF file. The
display object is a surface, essentially a bitmap version of the instance’s vector data, which is
data that you do not intend to change much over the course of your SWF file. Therefore,
instances with caching turned on are not continually redrawn as the SWF file plays, letting
the SWF file render quickly.
Setting a display object’s cacheAsBitmap property to true makes the display object cache a
bitmap representation of itself. Flash creates a surface object for the instance, which is a
cached bitmap instead of vector data. If you change the bounds of the display object, the
surface is recreated instead of resized. Surfaces can nest within other surfaces. The child
surface copies its bitmap onto its parent surface. For more information, see “Enabling bitmap
caching” on page 408.
The DisplayObject class’s opaqueBackground property and scrollRect property are related
to bitmap caching using the cacheAsBitmap property. Although these three properties are
independent of each other, the opaqueBackground and scrollRect properties work best
when an object is cached as a bitmap—you see performance benefits for the
opaqueBackground and scrollRect properties only when you set cacheAsBitmap to true.
For more information about scrolling display object content, see “Panning and scrolling
display objects” on page 400. For more information about setting an opaque background, see
“Setting an opaque background color” on page 409.
For information on alpha channel masking, which requires you to set the cacheAsBitmap
property to true, see “Alpha channel masking” on page 415.
When to enable caching
Enabling caching for a display object creates a surface, which has several advantages, such as
helping complex vector animations to render fast. There are several scenarios in which you
will want to enable caching. It might seem as though you would always want to enable
caching to improve the performance of your SWF files; however, there are situations in which
enabling caching does not improve performance, or can even decrease it. This section
describes scenarios in which caching should be used, and when to use regular display objects.
NOTE
You can update the vector data, at which time the surface is recreated. Therefore, the
vector data cached in the surface does not need to remain the same for the entire SWF
file.
Manipulating display objects 407
Overall performance of cached data depends on how complex the vector data of your
instances are, how much of the data you change, and whether or not you set the
opaqueBackground property. If you are changing small regions, the difference between using
a surface and using vector data could be negligible. You might want to test both scenarios with
your work before you deploy the application.
When to use bitmap caching
The following are typical scenarios in which you might see significant benefits when you
enable bitmap caching.
■ Complex background image: An application that contains a detailed and complex
background image of vector data (perhaps an image where you applied the trace bitmap
command, or artwork that you created in Adobe Illustrator®). You might animate
characters over the background, which slows the animation because the background needs
to continuously regenerate the vector data. To improve performance, you can set the
opaqueBackground property of the background display object to true. The background
is rendered as a bitmap and can be redrawn quickly, so that your animation plays much
faster.
■ Scrolling text field: An application that displays a large amount of text in a scrolling text
field. You can place the text field in a display object that you set as scrollable with scrolling
bounds (the scrollRect property). This enables fast pixel scrolling for the specified
instance. When a user scrolls the display object instance, Flash shifts the scrolled pixels up
and generates the newly exposed region instead of regenerating the entire text field.
■ Windowing system: An application with a complex system of overlapping windows. Each
window can be open or closed (for example, web browser windows). If you mark each
window as a surface (by setting the cacheAsBitmap property to true), each window is
isolated and cached. Users can drag the windows so that they overlap each other, and each
window doesn’t need to regenerate the vector content.
■ Alpha channel masking: When you are using alpha channel masking, you must set the
cacheAsBitmap property to true. For more information, see “Alpha channel masking”
on page 415.
Enabling bitmap caching in all of these scenarios improves the responsiveness and
interactivity of the application by optimizing the vector graphics.
In addition, whenever you apply a filter to a display object, cacheAsBitmap is automatically
set to true by Flash Player, even if you explicitly set it to false. If you clear all the filters from
the display object, the cacheAsBitmap property returns to the value it was last set to.
408 Display programming
When to avoid using bitmap caching
Misusing this feature could negatively affect your SWF file. When you use bitmap caching,
remember the following guidelines:
■ Do not overuse surfaces (display objects with caching enabled). Each surface uses more
memory than a regular display object, which means that you should only enable surfaces
when you need to improve rendering performance.
A cached bitmap can use significantly more memory than a regular display object. For
example, if a Sprite instance on the Stage is 250 pixels by 250 pixels in size, when cached
it might use 250 KB instead of 1 KB when it’s a regular (un-cached) Sprite instance.
■ Avoid zooming into cached surfaces. If you overuse bitmap caching, a large amount of
memory is consumed (see previous bullet), especially if you zoom in on the content.
■ Use surfaces for display object instances that are largely static (non-animating). You can
drag or move the instance, but the contents of the instance should not animate or change
a lot. (Animation or changing content are more likely with a MovieClip instance
containing animation or a Video instance.) For example, if you rotate or transform an
instance, the instance changes between the surface and vector data, which is difficult to
process and negatively affects your SWF file.
■ If you mix surfaces with vector data, it increases the amount of processing that Flash
Player (and sometimes the computer) needs to do. Group surfaces together as much as
possible—for example, when you create windowing applications.
Enabling bitmap caching
To enable bitmap caching for a display object, you set its cacheAsBitmap property to true:
mySprite.cacheAsBitmap = true;
After you set the cacheAsBitmap property to true, you might notice that the display object
automatically pixel-snaps to whole coordinates. When you test the SWF file, you should
notice that any animation performed on a complex vector image renders much faster.
A surface (cached bitmap) is not created, even if cacheAsBitmap is set to true, if one or more
of the following occurs:
■ The bitmap is greater than 2880 pixels in height or width.
■ The bitmap fails to allocate (because of an out-of-memory error).
Manipulating display objects 409
Setting an opaque background color
You can set an opaque background for a display object. For example, when your SWF has a
background that contains complex vector art, you can set the opaqueBackground property to
a specified color (typically the same color as the Stage). The color is specified as a number
(commonly a hexadecimal color value). The background is then treated as a bitmap, which
helps optimize performance.
When you set cacheAsBitmap to true, and also set the opaqueBackground property to a
specified color, the opaqueBackground property allows the internal bitmap to be opaque and
rendered faster. If you do not set cacheAsBitmap to true, the opaqueBackground property
adds an opaque vector-square shape to the background of the display object. It does not create
a bitmap automatically.
The following example shows how to set the background of a display object to optimize
performance:
myShape.cacheAsBitmap = true;
myShape.opaqueBackground = 0xFF0000;
In this case, the background color of the Shape named myShape is set to red (0xFF0000).
Assuming the Shape instance contains a drawing of a green triangle, on a Stage with a white
background, this would show up as a green triangle with red in the empty space in the Shape
instance’s bounding box (the rectangle that completely encloses the Shape).
Of course, this code would make more sense if it were used with a Stage with a solid red
background. On another colored background, that color would be specified instead. For
example, in a SWF with a white background, the opaqueBackground property would most
likely be set to 0xFFFFFF, or pure white.
Applying blending modes
Blending modes involve combining the colors of one image (the base image) with the colors
of another image (the blend image) to produce a third image—the resulting image is the one
that is actually displayed on the screen. Each pixel value in an image is processed with the
corresponding pixel value of the other image to produce a pixel value for that same position in
the result.
410 Display programming
Every display object has a blendMode property that can be set to one of the following
blending modes. These are constants defined in the BlendMode class. Alternatively, you can
use the String values (in parentheses) that are the actual values of the constants.
■ BlendMode.ADD ("add"): Commonly used to create an animated lightening dissolve effect
between two images.
■ BlendMode.ALPHA ("alpha"): Commonly used to apply the transparency of the
foreground on the background.
■ BlendMode.DARKEN ("darken"): Commonly used to superimpose type.
■ BlendMode.DIFFERENCE ("difference"): Commonly used to create more vibrant colors.
■ BlendMode.ERASE ("erase"): Commonly used to cut out (erase) part of the background
using the foreground alpha.
■ BlendMode.HARDLIGHT ("hardlight"): Commonly used to create shading effects.
■ BlendMode.INVERT ("invert"): Used to invert the background.
■ BlendMode.LAYER ("layer"): Used to force the creation of a temporary buffer for
precomposition for a particular display object.
■ BlendMode.LIGHTEN ("lighten"): Commonly used to superimpose type.
■ BlendMode.MULTIPLY ("multiply"): Commonly used to create shadows and depth
effects.
■ BlendMode.NORMAL ("normal"): Used to specify that the pixel values of the blend image
override those of the base image.
■ BlendMode.OVERLAY ("overlay"): Commonly used to create shading effects.
■ BlendMode.SCREEN ("screen"): Commonly used to create highlights and lens flares.
■ BlendMode.SUBTRACT ("subtract"): Commonly used to create an animated darkening
dissolve effect between two images.
Adjusting DisplayObject colors
You can use the methods of the ColorTransform class (flash.geom.ColorTransform) to adjust
the color of a display object. Each display object has a transform property, which is an
instance of the Transform class, and contains information about various transformations that
are applied to the display object (such as rotation, changes in scale or position, and so forth).
In addition to its information about geometric transformations, the Transform class also
includes a colorTransform property, which is an instance of the ColorTransform class, and
provides access to make color adjustments to the display object. To access the color
transformation information of a display object, you can use code such as this:
var colorInfo:ColorTransform = myDisplayObject.transform.colorTransform;
Manipulating display objects 411
Once you’ve created a ColorTransform instance, you can read its property values to find out
what color transformations have already been applied, or you can set those values to make
color changes to the display object. To update the display object after any changes, you must
reassign the ColorTransform instance back to the transform.colorTransform property.
var colorInfo:ColorTransform = my DisplayObject.transform.colorTransform;
// Make some color transformations here.
// Commit the change.
myDisplayObject.transform.colorTransform = colorInfo;
Setting color values with code
The color property of the ColorTransform class can be used to assign a specific red, green,
blue (RGB) color value to the display object. The following example uses the color property
to change the color of the display object named square to blue, when the user clicks a button
named blueBtn:
// square is a display object on the Stage.
// blueBtn, redBtn, greenBtn, and blackBtn are buttons on the Stage.
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
// Get access to the ColorTransform instance associated with square.
var colorInfo:ColorTransform = square.transform.colorTransform;
// This function is called when blueBtn is clicked.
function makeBlue(event:MouseEvent):void
{
// Set the color of the ColorTransform object.
colorInfo.color = 0x003399;
// apply the change to the display object
square.transform.colorTransform = colorInfo;
}
blueBtn.addEventListener(MouseEvent.CLICK, makeBlue);
Note that when you change a display object’s color using the color property, it completely
changes the color of the entire object, regardless of whether the object previously had multiple
colors. For example, if there is a display object containing a green circle with black text on top,
setting the color property of that object’s associated ColorTransform instance to a shade of
red will make the entire object, circle and text, turn red (so the text will no longer be
distinguishable from the rest of the object).
412 Display programming
Altering color and brightness effects with code
Suppose you have a display object with multiple colors (for example, a digital photo) and you
don’t want to completely recolor the object; you just want to adjust the color of a display
object based on the existing colors. In this scenario, the ColorTransform class includes a series
of multiplier and offset properties that you can use to make this type of adjustment. The
multiplier properties, named redMultiplier, greenMultiplier, blueMultiplier, and
alphaMultiplier, work like colored photographic filters (or colored sunglasses), amplifying
or diminishing certain colors in the display object. The offset properties (redOffset,
greenOffset, blueOffset, and alphaOffset) can be used to add extra amounts of a certain
color to the object, or to specify the minimum value that a particular color can have.
These multiplier and offset properties are identical to the advanced color settings that are
available for movie clip symbols in the Flash authoring tool when you choose Advanced from
the Color pop-up menu on the Property inspector.
The following code loads a JPEG image and applies a color transformation to it, which
adjusts the red and green channels as the mouse pointer moves along the x axis and y axis. In
this case, because no offset values are specified, the color value of each color channel displayed
on screen will be a percentage of the original color value in the image—meaning that the most
red or green displayed in any given pixel will be the original amount of red or green in that
pixel.
import flash.display.Loader;
import flash.events.MouseEvent;
import flash.geom.Transform;
import flash.geom.ColorTransform;
import flash.net.URLRequest;
// Load an image onto the Stage.
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/
images/image1.jpg");
loader.load(url);
this.addChild(loader);
// This function is called when the mouse moves over the loaded image.
function adjustColor(event:MouseEvent):void
{
// Access the ColorTransform object for the Loader (containing the image)
var colorTransformer:ColorTransform = loader.transform.colorTransform;
// Set the red and green multipliers according to the mouse position.
// The red value ranges from 0% (no red) when the cursor is at the left
// to 100% red (normal image appearance) when the cursor is at the right.
// The same applies to the green channel, except it's controlled by the
// position of the mouse in the y axis.
Manipulating display objects 413
colorTransformer.redMultiplier = (loader.mouseX / loader.width) * 1;
colorTransformer.greenMultiplier = (loader.mouseY / loader.height) * 1;
// Apply the changes to the display object.
loader.transform.colorTransform = colorTransformer;
}
loader.addEventListener(MouseEvent.MOUSE_MOVE, adjustColor);
Rotating objects
Display objects can be rotated using the rotation property. You can read this value to find
out whether an object has been rotated, or to rotate the object you can set this property to a
number (in degrees) representing the amount of rotation to be applied to the object. For
instance, this line of code rotates the object named square 45 degrees (one eighth of one
complete revolution):
square.rotation = 45;
Alternatively, you can rotate a display object using a transformation matrix, described in
Chapter 13, “Working with geometry,” on page 431.
Fading objects
You can control the transparency of a display object to make it partially (or completely
transparent), or change the transparency to make the object appear to fade in or out. The
DisplayObject class’s alpha property defines the transparency (or more accurately, the
opacity) of a display object. The alpha property can be set to any value between 0 and 1,
where 0 is completely transparent, and 1 is completely opaque. For example, these lines of
code make the object named myBall partially (50 percent) transparent when it is clicked with
the mouse:
function fadeBall(event:MouseEvent):void
{
myBall.alpha = .5;
}
myBall.addEventListener(MouseEvent.CLICK, fadeBall);
You can also alter the transparency of a display object using the color adjustments available
through the ColorTransform class. For more information, see “Adjusting DisplayObject
colors” on page 410.
414 Display programming
Masking display objects
You can use a display object as a mask to create a hole through which the contents of another
display object are visible.
Defining a mask
To indicate that a display object will be the mask for another display object, set the mask
object as the mask property of the display object to be masked:
// Make the object maskSprite be a mask for the object mySprite.
mySprite.mask = maskSprite;
The masked display object is revealed under all opaque (nontransparent) areas of the display
object acting as the mask. For instance, the following code creates a Shape instance containing
a red 100 by 100 pixel square and a Sprite instance containing a blue circle with a radius of 25
pixels. When the circle is clicked, it is set as the mask for the square, so that the only part of
the square that shows is the part that is covered by the solid part of the circle. In other words,
only a red circle will be visible.
// This code assumes it’s being run within a display object container
// such as a MovieClip or Sprite instance.
import flash.display.Shape;
// Draw a square and add it to the display list.
var square:Shape = new Shape();
square.graphics.lineStyle(1, 0x000000);
square.graphics.beginFill(0xff0000);
square.graphics.drawRect(0, 0, 100, 100);
square.graphics.endFill();
this.addChild(square);
// Draw a circle and add it to the display list.
var circle:Sprite = new Sprite();
circle.graphics.lineStyle(1, 0x000000);
circle.graphics.beginFill(0x0000ff);
circle.graphics.drawCircle(25, 25, 25);
circle.graphics.endFill();
this.addChild(circle);
function maskSquare(event:MouseEvent):void
{
square.mask = circle;
circle.removeEventListener(MouseEvent.CLICK, maskSquare);
}
Manipulating display objects 415
circle.addEventListener(MouseEvent.CLICK, maskSquare);
The display object that is acting as a mask can be draggable, animated, resized dynamically,
and can use separate shapes within a single mask. The mask display object doesn’t necessarily
need to be added to the display list. However, if you want the mask object to scale when the
Stage is scaled or if you want to enable user interaction with the mask (such as user-controlled
dragging and resizing), the mask object must be added to the display list. The actual z-index
(front-to-back order) of the display objects doesn’t matter, as long as the mask object is added
to the display list. (The mask object will not appear on the screen except as a mask.) If the
mask object is a MovieClip instance with multiple frames, it plays all the frames in its
timeline, the same as it would if it were not serving as a mask. You can remove a mask by
setting the mask property to null:
// remove the mask from mySprite
mySprite.mask = null;
You cannot use a mask to mask another mask. You cannot set the alpha property of a mask
display object. Only fills are used in a display object that is used as a mask; strokes are ignored.
About masking device fonts
You can use a display object to mask text that is set in a device font. When you use a display
object to mask text set in a device font, the rectangular bounding box of the mask is used as
the masking shape. That is, if you create a non-rectangular display object mask for device font
text, the mask that appears in the SWF file is the shape of the rectangular bounding box of the
mask, not the shape of the mask itself.
Alpha channel masking
Alpha channel masking is supported if both the mask and the masked display objects use
bitmap caching, as shown here:
// maskShape is a Shape instance which includes a gradient fill.
mySprite.cacheAsBitmap = true;
maskShape.cacheAsBitmap = true;
mySprite.mask = maskShape;
For instance, one application of alpha channel masking is to use a filter on the mask object
independently of a filter that is applied to the masked display object.
In the following example, an external image file is loaded onto the Stage. That image (or more
accurately, the Loader instance it is loaded into) will be the display object that is masked. A
gradient oval (solid black center fading to transparent at the edges) is drawn over the image;
this will be the alpha mask. Both display objects have bitmap caching turned on. The oval is
set as a mask for the image, and it is then made draggable.
416 Display programming
// This code assumes it’s being run within a display object container
// such as a MovieClip or Sprite instance.
import flash.display.GradientType;
import flash.display.Loader;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.net.URLRequest;
// Load an image and add it to the display list.
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/
images/image1.jpg");
loader.load(url);
this.addChild(loader);
// Create a Sprite.
var oval:Sprite = new Sprite();
// Draw a gradient oval.
var colors:Array = [0x000000, 0x000000];
var alphas:Array = [1, 0];
var ratios:Array = [0, 255];
var matrix:Matrix = new Matrix();
matrix.createGradientBox(200, 100, 0, -100, -50);
oval.graphics.beginGradientFill(GradientType.RADIAL,
colors,
alphas,
ratios,
matrix);
oval.graphics.drawEllipse(-100, -50, 200, 100);
oval.graphics.endFill();
// add the Sprite to the display list
this.addChild(oval);
// Set cacheAsBitmap = true for both display objects.
loader.cacheAsBitmap = true;
oval.cacheAsBitmap = true;
// Set the oval as the mask for the loader (and its child, the loaded image)
loader.mask = oval;
// Make the oval draggable.
oval.startDrag(true);
Animating objects
Animation is the process of making something move, or alternatively, of making something
change over time. Scripted animation is a fundamental part of video games, and is often used
to add polish and useful interaction clues to other applications.
Animating objects 417
The fundamental idea behind scripted animation is that a change needs to take place, and that
change needs to be divided into increments over time. It’s easy to make something repeat in
ActionScript, using a common looping statement. However, a loop will run through all its
iterations before updating the display. To create scripted animation, you need to write
ActionScript that performs some action repeatedly over time and also updates the screen each
time it runs.
For example, imagine you want to create a simple animation, such as making a ball travel
across the screen. ActionScript includes a simple mechanism that allows you to track the
passage of time and update the screen accordingly—meaning you could write code that moves
the ball a small amount each time, until it reaches its destination. After each move the screen
would update, making the cross-Stage motion visible to the viewer.
From a practical standpoint, it makes sense to synchronize scripted animation with the SWF
file’s frame rate (in other words, make one animation change each time a new frame displays
or would display), since that is how fast Flash Player updates the screen. Each display object
has an enterFrame event that is dispatched according to the frame rate of the SWF file—one
event per frame. Most developers who create scripted animation use the enterFrame event as
a way to create actions that repeat over time. You could write code that listens to the
enterFrame event, moving the animated ball a certain amount each frame, and as the screen
is updated (each frame), the ball would be redrawn in its new location, creating motion.
In the following example, a circle Sprite instance, named circle, is created on the Stage.
When the user clicks the circle, a scripted animation sequence begins, causing circle to fade
(its alpha property is decreased) until it is completely transparent:
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
// draw a circle and add it to the display list
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x990000);
circle.graphics.drawCircle(50, 50, 50);
circle.graphics.endFill();
addChild(circle);
// When this animation starts, this function is called every frame.
// The change made by this function (updated to the screen every
NOTE
Another way to perform an action repeatedly over time is to use the Timer class. A Timer
instance triggers an event notification each time a specified amount of time has past.
You could write code that performs animation by handling the Timer class’s timer event,
setting the time interval to a small one (some fraction of a second). For more information
about using the Timer class, see “Controlling time intervals” on page 196.
418 Display programming
// frame) is what causes the animation to occur.
function fadeCircle(event:Event):void
{
circle.alpha -= .05;
if (circle.alpha <= 0)
{
circle.removeEventListener(Event.ENTER_FRAME, fadeCircle);
}
}
function startAnimation(event:MouseEvent):void
{
circle.addEventListener(Event.ENTER_FRAME, fadeCircle);
}
circle.addEventListener(MouseEvent.CLICK, startAnimation);
When the user clicks the circle, the function fadeCircle() is subscribed as a listener of the
enterFrame event, meaning it begins to be called once per frame. That function fades circle
by changing its alpha property, so once per frame the circle’s alpha decreases by .05 (5
percent) and the screen is updated. Eventually, when the alpha value is 0 (circle is
completely transparent), the fadeCircle() function is removed as an event listener, ending
the animation.
The same code could be used, for example, to create animated motion instead of fading. By
substituting a different property for alpha in the function that is an enterFrame event
listener, that property will be animated instead. For example, changing this line
circle.alpha -= .05;
to this code
circle.x += 5;
will animate the x property, causing the circle to move to the right across the Stage. The
condition that ends the animation could be changed to end the animation (that is,
unsubscribe the enterFrame listener) when the desired x coordinate is reached.
Loading display content dynamically
You can load any of the following external display assets into an ActionScript 3.0 application:
■ A SWF file authored in ActionScript 3.0—This file can be a Sprite, MovieClip, or any
class that extends Sprite.
■ An image file—This includes JPG, PNG, and GIF files.
■ An AVM1 SWF file—This is a SWF file written in ActionScript 1.0 or 2.0.
Loading display content dynamically 419
You load these assets by using the Loader class.
Loading display objects
Loader objects are used to load SWF files and graphics files into an application. The Loader
class is a subclass of the DisplayObjectContainer class. A Loader object can contain only one
child display object in its display list—the display object representing the SWF or graphic file
that it loads. When you add a Loader object to the display list, as in the following code, you
also add the loaded child display object to the display list once it loads:
var pictLdr:Loader = new Loader();
var pictURL:String = "banana.jpg"
var pictURLReq:URLRequest = new URLRequest(pictURL);
pictLdr.load(pictURLReq);
this.addChild(pictLdr);
Once the SWF file or image is loaded, you can move the loaded display object to another
display object container, such as the container DisplayObjectContainer object in this
example:
import flash.display.*;
import flash.net.URLRequest;
import flash.events.Event;
var container:Sprite = new Sprite();
addChild(container);
var pictLdr:Loader = new Loader();
var pictURL:String = "banana.jpg"
var pictURLReq:URLRequest = new URLRequest(pictURL);
pictLdr.load(pictURLReq);
pictLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoaded);
function imgLoaded(event:Event):void
{
container.addChild(pictLdr.content);
}
420 Display programming
Monitoring loading progress
Once the file has started loading, a LoaderInfo object is created. A LoaderInfo object provides
information such as load progress, the URLs of the loader and loadee, the number of bytes
total for the media, and the nominal height and width of the media. A LoaderInfo object also
dispatches events for monitoring the progress of the load.
The following diagram shows the different uses of the LoaderInfo object—for the instance of
the main class of the SWF file, for a Loader object, and for an object loaded by the Loader
object:
The LoaderInfo object can be accessed as a property of both the Loader object and the loaded
display object. As soon as loading begins, the LoaderInfo object can be accessed through the
contentLoaderInfo property of the Loader object. Once the display object has finished
loading, the LoaderInfo object can also be accessed as a property of the loaded display object
through the display object’s loaderInfo property. The loaderInfo property of the loaded
display object refers to the same LoaderInfo object as the contentLoaderInfo property of the
Loader object. In other words, a LoaderInfo object is shared between a loaded object and the
Loader object that loaded it (between loader and loadee).
In order to access properties of loaded content, you will want to add an event listener to the
LoaderInfo object, as in the following code:
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
Stage
Instance of
the main class of
the SWF file
Loader object
LoaderInfo object
content
contentLoaderInfo property
loaderInfo property
LoaderInfo object
loaderInfo property
Loading display content dynamically 421
var ldr:Loader = new Loader();
var urlReq:URLRequest = new URLRequest("Circle.swf");
ldr.load(urlReq);
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
addChild(ldr);
function loaded(event:Event):void
{
var content:Sprite = event.target.content;
content.scaleX = 2;
}
For more information, see Chapter 10, “Handling events,” on page 313.
Specifying loading context
When you load an external file into Flash Player through the load() or loadBytes()
method of the Loader class, you can optionally specify a context parameter. This parameter
is a LoaderContext object.
The LoaderContext class includes three properties that let you define the context of how the
loaded content can be used:
■ checkPolicyFile: Use this property only when loading an image file (not a SWF file). If
you set this property to true, the Loader checks the origin server for a cross-domain
policy file (see “Website controls (cross-domain policy files)” on page 765). This is
necessary only for content originating from domains other than that of the SWF file
containing the Loader object. If the server grants permission to the Loader domain,
ActionScript from SWF files in the Loader domain can access data in the loaded image; in
other words, you can use the BitmapData.draw() command to access data in the loaded
image.
Note that a SWF file from other domains than that of the Loader object can call
Security.allowDomain() to permit a specific domain.
■ securityDomain: Use this property only when loading a SWF file (not an image). Specify
this for a SWF file from a domain other than that of the file containing the Loader object.
When you specify this option, Flash Player checks for the existence of a cross-domain
policy file, and if one exists, SWF files from the domains permitted in the cross-policy file
can cross-script the loaded SWF content. You can specify
flash.system.SecurityDomain.currentDomain as this parameter.
422 Display programming
■ applicationDomain: Use this property only when loading a SWF file written in
ActionScript 3.0 (not an image or a SWF file written in ActionScript 1.0 or 2.0). When
loading the file, you can specify that the file be included in the same application domain
as that of the Loader object, by setting the applicationDomain parameter to
flash.system.ApplicationDomain.currentDomain. By putting the loaded SWF file in
the same application domain, you can access its classes directly. This can be useful if you
are loading a SWF file that contains embedded media, which you can access via their
associated class names. For more information, see “Using the ApplicationDomain class”
on page 700.
Here’s an example of checking for a cross-domain policy file when loading a bitmap from
another domain:
var context:LoaderContext = new LoaderContext();
context.checkPolicyFile = true;
var urlReq:URLRequest = new URLRequest("http://www.[your_domain_here].com/
photo11.jpg");
var ldr:Loader = new Loader();
ldr.load(urlReq, context);
Here’s an example of checking for a cross-domain policy file when loading a SWF from
another domain, in order to place the file in the same security sandbox as the Loader object.
Additionally, the code adds the classes in the loaded SWF file to the same application domain
as that of the Loader object:
var context:LoaderContext = new LoaderContext();
context.securityDomain = SecurityDomain.currentDomain;
context.applicationDomain = ApplicationDomain.currentDomain;
var urlReq:URLRequest = new URLRequest("http://www.[your_domain_here].com/
library.swf");
var ldr:Loader = new Loader();
ldr.load(urlReq, context);
For more information, see the LoaderContext class in the ActionScript 3.0 Language and
Components Reference.
Example: SpriteArranger 423
Example: SpriteArranger
The SpriteArranger sample application builds upon the Geometric Shapes sample application
described separately (see “Example: GeometricShapes” on page 180).
The SpriteArranger sample application illustrates a number of concepts for dealing with
display objects:
■ Extending display object classes
■ Adding objects to the display list
■ Layering display objects and working with display object containers
■ Responding to display object events
■ Using properties and methods of display objects
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The SpriteArranger application files can be found in
the folder Examples/SpriteArranger. The application consists of the following files:
File Description
SpriteArranger.mxml
or
SpriteArranger.fla
The main application file in Flash (FLA) or Flex
(MXML).
com/example/programmingas3/
SpriteArranger/CircleSprite.as
A class defining a type of Sprite object that renders
a circle on-screen.
com/example/programmingas3/
SpriteArranger/DrawingCanvas.as
A class defining the canvas, which is a display
object container that contains GeometricSprite
objects.
com/example/programmingas3/
SpriteArranger/SquareSprite.as
A class defining a type of Sprite object that renders
a square on-screen.
com/example/programmingas3/
SpriteArranger/TriangleSprite.as
A class defining a type of Sprite object that renders
a triangle on-screen.
com/example/programmingas3/
SpriteArranger/GeometricSprite.as
A class that extends the Sprite object, used to
define an on-screen shape. The CircleSprite,
SquareSprite, and TriangleSprite each extend this
class.
com/example/programmingas3/
geometricshapes/IGeometricShape.as
The base interface defining methods to be
implemented by all geometric shape classes.
com/example/programmingas3/
geometricshapes/IPolygon.as
An interface defining methods to be implemented
by geometric shape classes that have multiple
sides.
424 Display programming
Defining the SpriteArranger classes
The SpriteArranger application lets the user add a variety of display objects to the on-screen
“canvas.”
The DrawingCanvas class defines a drawing area, a type of display object container, to which
the user can add on-screen shapes. These on-screen shapes are instances of one of the
subclasses of the GeometricSprite class.
The DrawingCanvas class
The DrawingCanvas class extends the Sprite class, and this inheritance is defined in the
DrawingCanvas class declaration, as follows:
public class DrawingCanvas extends Sprite
The Sprite class is a subclass of the DisplayObjectContainer and DisplayObject classes, and
the DrawingCanvas class uses methods and properties of those classes.
The DrawingCanvas() constructor method sets up a Rectangle object, bounds, which is
property that is later used in drawing the outline of the canvas. It then calls the initCanvas()
method, as follows:
this.bounds = new Rectangle(0, 0, w, h);
initCanvas(fillColor, lineColor);
com/example/programmingas3/
geometricshapes/RegularPolygon.as
A type of geometric shape that has sides of equal
length positioned symmetrically around the shape’s
center.
com/example/programmingas3/
geometricshapes/Circle.as
A type of geometric shape that defines a circle.
com/example/programmingas3/
geometricshapes/EquilateralTriangle.as
A subclass of RegularPolygon that defines a
triangle with all sides the same length.
com/example/programmingas3/
geometricshapes/Square.as
A subclass of RegularPolygon defining a rectangle
with all four sides the same length.
com/example/programmingas3/
geometricshapes/
GeometricShapeFactory.as
A class containing a “factory method” for creating
shapes given a shape type and size.
File Description
Example: SpriteArranger 425
AS the following example shows, the initCanvas() method defines various properties of the
DrawingCanvas object, which were passed as arguments to the constructor function:
this.lineColor = lineColor;
this.fillColor = fillColor;
this.width = 500;
this.height = 200;
The initCanvas() method then calls the drawBounds() method, which draws the canvas
using the DrawingCanvas class’s graphics property. The graphics property is inherited
from the Shape class.
this.graphics.clear();
this.graphics.lineStyle(1.0, this.lineColor, 1.0);
this.graphics.beginFill(this.fillColor, 1.0);
this.graphics.drawRect(bounds.left - 1,
bounds.top - 1,
bounds.width + 2,
bounds.height + 2);
this.graphics.endFill();
The following additional methods of the DrawingCanvas class are invoked based on user
interactions with the application:
■ The addShape() and describeChildren() methods, which are described in “Adding
display objects to the canvas” on page 426
■ The moveToBack(), moveDown(), moveToFront(), and moveUp() methods, which are
described in “Rearranging display object layering” on page 429
■ The onMouseUp() method, which is described in “Clicking and dragging display objects”
on page 428
426 Display programming
The GeometricSprite class and its subclasses
Each display object the user can add to the canvas is an instance of one of the following
subclasses of the GeometricSprite class:
■ CircleSprite
■ SquareSprite
■ TriangleSprite
The GeometricSprite class extends the flash.display.Sprite class:
public class GeometricSprite extends Sprite
The GeometricSprite class includes a number of properties common to all GeometricSprite
objects. These are set in the constructor function based on parameters passed to the function.
For example:
this.size = size;
this.lineColor = lColor;
this.fillColor = fColor;
The geometricShape property of the GeometricSprite class defines an IGeometricShape
interface, which defines the mathematical properties, but not the visual properties, of the
shape. The classes that implement the IGeometricShape interface are defined in the
GeometricShapes sample application (see “Example: GeometricShapes” on page 180).
The GeometricSprite class defines the drawShape() method, which is further refined in the
override definitions in each subclass of GeometricSprite. For more information, see the
“Adding display objects to the canvas” section, which follows.
The GeometricSprite class also provides the following methods:
■ The onMouseDown() and onMouseUp() methods, which are described in “Clicking and
dragging display objects” on page 428
■ The showSelected() and hideSelected() methods, which are described in “Clicking
and dragging display objects” on page 428
Adding display objects to the canvas
When the user clicks the Add Shape button, the application calls the addShape() method of
the DrawingCanvas class. It instantiates a new GeometricSprite by calling the appropriate
constructor function of one of the GeometricSprite subclasses, as the following example
shows:
Example: SpriteArranger 427
public function addShape(shapeName:String, len:Number):void
{
var newShape:GeometricSprite;
switch (shapeName)
{
case "Triangle":
newShape = new TriangleSprite(len);
break;
case "Square":
newShape = new SquareSprite(len);
break;
case "Circle":
newShape = new CircleSprite(len);
break;
}
newShape.alpha = 0.8;
this.addChild(newShape);
}
Each constructor method calls the drawShape() method, which uses the graphics property
of the class (inherited from the Sprite class) to draw the appropriate vector graphic. For
example, the drawShape() method of the CircleSprite class includes the following code:
this.graphics.clear();
this.graphics.lineStyle(1.0, this.lineColor, 1.0);
this.graphics.beginFill(this.fillColor, 1.0);
var radius:Number = this.size / 2;
this.graphics.drawCircle(radius, radius, radius);
The second to last line of the addShape() function sets the alpha property of the display
object (inherited from the DisplayObject class), so that each display object added to the
canvas is slightly transparent, letting the user see the objects behind it.
The final line of the addChild() method adds the new display object to the child list of the
instance of the DrawingCanvas class, which is already on the display list. This causes the new
display object to appear on the Stage.
The interface for the application includes two text fields, selectedSpriteTxt and
outputTxt. The text properties of these text fields are updated with information about the
GeometricSprite objects that have been added to the canvas or selected by the user. The
GeometricSprite class handles this information-reporting task by overriding the toString()
method, as follows:
public override function toString():String
{
return this.shapeType + “ of size “ + this.size + " at " + this.x + ", "
+ this.y;
}
428 Display programming
The shapeType property is set to the appropriate value in the constructor method of each
GeometricSprite subclass. For example, the toString() method might return the following
value for a CircleSprite instance recently added to the DrawingCanvas instance:
Circle of size 50 at 0, 0
The describeChildren() method of the DrawingCanvas class loops through the canvas’s
child list, using the numChildren property (inherited from the DisplayObjectContainer class)
to set the limit of the for loop. It generates a string listing each child, as follows:
var desc:String = "";
var child:DisplayObject;
for (var i:int=0; i < this.numChildren; i++)
{
child = this.getChildAt(i);
desc += i + ": " + child + 'n';
}
The resulting string is used to set the text property of the outputTxt text field.
Clicking and dragging display objects
When the user clicks on a GeometricSprite instance, the application calls the onMouseDown()
event handler. As the following shows, this event handler is set to listen for mouse down
events in the constructor function of the GeometricSprite class:
this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
The onMouseDown() method then calls the showSelected() method of the GeometricSprite
object. If it is the first time this method has been called for the object, the method creates a
new Shape object named selectionIndicator and it uses the graphics property of the
Shape object to draw a red highlight rectangle, as follows:
this.selectionIndicator = new Shape();
this.selectionIndicator.graphics.lineStyle(1.0, 0xFF0000, 1.0);
this.selectionIndicator.graphics.drawRect(-1, -1, this.size + 1,
this.size + 1);
this.addChild(this.selectionIndicator);
If this is not the first time the onMouseDown() method is called, the method simply sets the
selectionIndicator shape’s visible property (inherited from the DisplayObject class), as
follows:
this.selectionIndicator.visible = true;
The hideSelected() method hides the selectionIndicator shape of the previously
selected object by setting its visible property to false.
Example: SpriteArranger 429
The onMouseDown() event handler method also calls the startDrag() method (inherited
from the Sprite class), which includes the following code:
var boundsRect:Rectangle = this.parent.getRect(this.parent);
boundsRect.width -= this.size;
boundsRect.height -= this.size;
this.startDrag(false, boundsRect);
This lets the user drag the selected object around the canvas, within the boundaries set by the
boundsRect rectangle.
When the user releases the mouse button, the mouseUp event is dispatched. The constructor
method of the DrawingCanvas sets up the following event listener:
this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
This event listener is set for the DrawingCanvas object, rather than for the individual
GeometricSprite objects. This is because when the GeometricSprite object is dragged, it could
end up behind another display object (another GeometricSprite object) when the mouse is
released. The display object in the foreground would receive the mouse up event but the
display object the user is dragging would not. Adding the listener to the DrawingCanvas
object ensures that the event is always handled.
The onMouseUp() method calls the onMouseUp() method of the GeometricSprite object,
which in turn calls the stopDrag() method of the GeometricSprite object.
Rearranging display object layering
The user interface for the application includes buttons labeled Move Back, Move Down,
Move Up, and Move to Front. When the user clicks one of these buttons, the application calls
the corresponding method of the DrawingCanvas class: moveToBack(), moveDown(),
moveUp(), or moveToFront(). For example, the moveToBack() method includes the
following code:
public function moveToBack(shape:GeometricSprite):void
{
var index:int = this.getChildIndex(shape);
if (index > 0)
{
this.setChildIndex(shape, 0);
}
}
The method uses the setChildIndex() method (inherited from the DisplayObjectContainer
class) to position the display object in index position 0 in the child list of the DrawingCanvas
instance (this).
430 Display programming
The moveDown() method works similarly, except that it decrements the index position of the
display object by 1 in the child list of the DrawingCanvas instance:
public function moveDown(shape:GeometricSprite):void
{
var index:int = this.getChildIndex(shape);
if (index > 0)
{
this.setChildIndex(shape, index - 1);
}
}
The moveUp() and moveToFront() methods work similarly to the moveToBack() and
moveDown() methods.
431
13
CHAPTER 13
Working with geometry
The flash.geom package contains classes that define geometric objects such as points,
rectangles, and transformation matrixes. You use these classes to define the properties of
objects that are used in other classes.
Contents
Basics of geometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
Using Point objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
Using Rectangle objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .436
Using Matrix objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
Example: Applying a matrix transformation to a display object . . . . . . . . . . . . . . . 442
Basics of geometry
Introduction to working with geometry
Geometry may be a subject many people try to get through in school while retaining as little
as possible, but a little geometry knowledge can be a powerful tool in ActionScript.
The flash.geom package contains classes that define geometric objects such as points,
rectangles, and transformation matrixes. These classes don’t necessarily provide functionality
by themselves; however, they are used to define the properties of objects that are used in other
classes.
All the geometry classes are based around the notion that locations on the screen are
represented as a two-dimensional plane. The screen is treated like a flat graph with a
horizontal (x) axis and a vertical (y) axis. Any location (or point) on the screen can be
represented as a pair of x and y values—the coordinates of that location.
432 Working with geometry
Every display object, include the Stage, has its own coordinate space—essentially its own graph
for plotting the locations of child display objects, drawings, and so forth. Commonly, the
origin (the place with the coordinate 0, 0 where the x and y axes meet) is placed at the top-left
corner of the display object. While this is always true for the Stage, it is not necessarily true for
any other display object. As in standard two-dimensional coordinate systems, values on the x
axis get bigger going toward the right, and smaller going toward the left—for locations to the
left of the origin, the x coordinate is negative. However, contrary to traditional coordinate
systems, in ActionScript values on the y axis get bigger going down and smaller going up the
screen (with values above the origin having a negative y coordinate). Since the top-left corner
of the Stage is the origin of its coordinate space, any object on the Stage will have an x
coordinate greater than 0 and smaller than the Stage width, and will have a y coordinate larger
than 0 and smaller than the Stage height.
You can use Point class instances to represent individual points in a coordinate space. You can
create a Rectangle instance to represent a rectangular region in a coordinate space. For
advanced users, you can use a Matrix instance to apply multiple or complex transformations
to a display object. Many simple transformations, such as rotation, position, and scale
changes, can be applied directly to a display object using that object’s properties. For more
information on applying transformations using display object properties, see “Manipulating
display objects” on page 395.
Common geometry tasks
The following tasks are things you’ll likely want to accomplish using the geometry classes in
ActionScript:
■ Calculating distance between two points
■ Determining coordinates of a point in different coordinate spaces
■ Moving a display object using angle and distance
■ Working with Rectangle instances:
■ Repositioning a Rectangle instance
■ Resizing a Rectangle instance
■ Determining combined size or overlapping areas of Rectangle instances
■ Creating Matrix objects
■ Using a Matrix object to apply transformations to a display object
Basics of geometry 433
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Cartesian coordinates: Coordinates are commonly written as a pair of number (like 5, 12
or 17, -23). The two numbers are the x coordinate and the y coordinate, respectively.
■ Coordinate space: The graph of coordinates contained in a display object, on which its
child elements are positioned.
■ Origin: The point in a coordinate space where the x axis meets the y axis. This point has
the coordinate 0, 0.
■ Point: A single location in a coordinate space. In the 2-d coordinate system used in
ActionScript, a point is defined by its location along the x axis and the y axis (the point’s
coordinates).
■ Registration point: In a display object, the origin (0, 0 coordinate) of its coordinate space.
■ Scale: The size of an object relative to its original size. When used as a verb, to scale an
object means to change its size by stretching or shrinking the object.
■ Translate: To change a point’s coordinates from one coordinate space to another.
■ Transformation: An adjustment to a visual characteristic of a graphic, such as rotating the
object, altering its scale, skewing or distorting its shape, or altering its color.
■ X axis: The horizontal axis in the 2-d coordinate system used in ActionScript.
■ Y axis: The vertical axis in the 2-d coordinate system used in ActionScript.
Working through in-chapter examples
Many of the examples in this chapter demonstrate calculations or changing values; most of
those examples include the appropriate trace() function calls to demonstrate the results of
the code. To test these examples, do the following:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the code listing’s trace() functions in the Output panel.
Some of the chapter’s examples demonstrate applying transformations to display objects. For
those examples, the results of the example will be seen visually rather than through text
output. To test the transformation examples, do the following:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
434 Working with geometry
3. Open the Actions panel and copy the code listing into the Script pane.
4. Create a movie clip symbol instance on the Stage. For example, draw a shape, select it,
choose Modify > Convert to symbol, and give the symbol a name.
5. With the Stage movie clip selected, in the Property inspector, give the instance an instance
name. The name should match the name used for the display object in the example code
listing—for example, if the code listing applies a transformation to an object named
myDisplayObject, you should name your movie clip instance myDisplayObject as well.
6. Run the program using Control > Test Movie.
On the screen you will see the results of the transformations applied to the object as
specified in the code listing.
Techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
Using Point objects
A Point object defines a Cartesian pair of coordinates. It represents location in a two-
dimensional coordinate system, where x represents the horizontal axis and y represents the
vertical axis.
To define a Point object, you set its x and y properties, as follows:
import flash.geom.*;
var pt1:Point = new Point(10, 20); // x == 10; y == 20
var pt2:Point = new Point();
pt2.x = 10;
pt2.y = 20;
Finding the distance between two points
You can use the distance() method of the Point class to find the distance between two
points in a coordinate space. For example, the following code finds the distance between the
registration points of two display objects, circle1 and circle2, in the same display object
container:
import flash.geom.*;
var pt1:Point = new Point(circle1.x, circle1.y);
var pt2:Point = new Point(circle2.x, circle2.y);
var distance:Number = Point.distance(pt1, pt2);
Using Point objects 435
Translating coordinate spaces
If two display objects are in different display object containers, they may be in different
coordinate spaces. You can use the localToGlobal() method of the DisplayObject class to
translate the coordinates to the same (global) coordinate space, that of the Stage. For example,
the following code finds the distance between the registration points of two display objects,
circle1 and circle2, in the different display object containers:
import flash.geom.*;
var pt1:Point = new Point(circle1.x, circle1.y);
pt1 = circle1.localToGlobal(pt1);
var pt2:Point = new Point(circle1.x, circle1.y);
pt2 = circle2.localToGlobal(pt2);
var distance:Number = Point.distance(pt1, pt2);
Similarly, to find the distance of the registration point of a display object named target from
a specific point on the Stage, you can use the localToGlobal() method of the DisplayObject
class:
import flash.geom.*;
var stageCenter:Point = new Point();
stageCenter.x = this.stage.stageWidth / 2;
stageCenter.y = this.stage.stageHeight / 2;
var targetCenter:Point = new Point(target.x, target.y);
targetCenter = target.localToGlobal(targetCenter);
var distance:Number = Point.distance(stageCenter, targetCenter);
Moving a display object by a specified angle and
distance
You can use the polar() method of the Point class to move a display object a specific distance
by a specific angle. For example, the following code moves the myDisplayObject object 100
pixels by 60 degrees:
import flash.geom.*;
var distance:Number = 100;
var angle:Number = 2 * Math.PI * (90 / 360);
var translatePoint:Point = Point.polar(distance, angle);
myDisplayObject.x += translatePoint.x;
myDisplayObject.y += translatePoint.y;
436 Working with geometry
Other uses of the Point class
You can use Point objects with the following methods and properties:
Using Rectangle objects
A Rectangle object defines a rectangular area. A Rectangle object has a position, defined by
the x and y coordinates of its top-left corner, a width property, and a height property. You
can define these properties for a new Rectangle object by invoking the Rectangle()
constructor function, as follows:
import flash.geom.Rectangle;
var rx:Number = 0;
var ry:Number = 0;
var rwidth:Number = 100;
var rheight:Number = 50;
var rect1:Rectangle = new Rectangle(rx, ry, rwidth, rheight);
Class Methods or properties Description
DisplayObjectContainer areInaccessibleObjectsUnderPoint()
getObjectsUnderPoint()
Used to return a list of
objects under a point in
a display object
container.
BitmapData hitTest() Used to define the pixel
in the BitmapData
object as well as the
point that you are
checking for a hit.
BitmapData applyFilter()
copyChannel()
merge()
paletteMap()
pixelDissolve()
threshold()
Used to define the
positions of rectangles
that define the
operations.
Matrix deltaTransformPoint()
transformPoint()
Used to define points
for which you want to
apply a transformation.
Rectangle bottomRight
size
topLeft
Used to define these
properties.
Using Rectangle objects 437
Resizing and repositioning Rectangle objects
There are a number of ways to resize and reposition Rectangle objects.
You can directly reposition the Rectangle object by changing its x and y properties. This has
no effect on the width or height of the Rectangle object.
import flash.geom.Rectangle;
var x1:Number = 0;
var y1:Number = 0;
var width1:Number = 100;
var height1:Number = 50;
var rect1:Rectangle = new Rectangle(x1, y1, width1, height1);
trace(rect1) // (x=0, y=0, w=100, h=50)
rect1.x = 20;
rect1.y = 30;
trace(rect1); // (x=20, y=30, w=100, h=50)
As the following code shows, if you change the left or top property of a Rectangle object, it
is also repositioned, with its x and y properties matching the left and top properties,
respectively. However, the position of the bottom-left corner of the Rectangle object does not
change, so it is resized.
import flash.geom.Rectangle;
var x1:Number = 0;
var y1:Number = 0;
var width1:Number = 100;
var height1:Number = 50;
var rect1:Rectangle = new Rectangle(x1, y1, width1, height1);
trace(rect1) // (x=0, y=0, w=100, h=50)
rect1.left = 20;
rect1.top = 30;
trace(rect1); // (x=30, y=20, w=70, h=30)
Similarly, as the following example shows, if you change the bottom or right property of a
Rectangle object, the position of its top-left corner does not change, so it is resized
accordingly:
import flash.geom.Rectangle;
var x1:Number = 0;
var y1:Number = 0;
var width1:Number = 100;
var height1:Number = 50;
var rect1:Rectangle = new Rectangle(x1, y1, width1, height1);
trace(rect1) // (x=0, y=0, w=100, h=50)
rect1.right = 60;
trect1.bottom = 20;
trace(rect1); // (x=0, y=0, w=60, h=20)
438 Working with geometry
You can also reposition a Rectangle object by using the offset() method, as follows:
import flash.geom.Rectangle;
var x1:Number = 0;
var y1:Number = 0;
var width1:Number = 100;
var height1:Number = 50;
var rect1:Rectangle = new Rectangle(x1, y1, width1, height1);
trace(rect1) // (x=0, y=0, w=100, h=50)
rect1.offset(20, 30);
trace(rect1); // (x=20, y=30, w=100, h=50)
The offsetPt() method works similarly, except that it takes a Point object as its parameter,
rather than x and y offset values.
You can also resize a Rectangle object by using the inflate() method, which includes two
parameters, dx and dy. The dx parameter represents the number of pixels that the left and
right sides of the rectangle will move from the center, and the dy parameter represents the
number of pixels that the top and bottom of the rectangle will move from the center:
import flash.geom.Rectangle;
var x1:Number = 0;
var y1:Number = 0;
var width1:Number = 100;
var height1:Number = 50;
var rect1:Rectangle = new Rectangle(x1, y1, width1, height1);
trace(rect1) // (x=0, y=0, w=100, h=50)
rect1.inflate(6,4);
trace(rect1); // (x=-6, y=-4, w=112, h=58)
The inflatePt() method works similarly, except that it takes a Point object as its parameter,
rather than dx and dy values.
Finding unions and intersections of Rectangle
objects
You use the union() method to find the rectangular region formed by the boundaries of two
rectangles:
import flash.display.*;
import flash.geom.Rectangle;
var rect1:Rectangle = new Rectangle(0, 0, 100, 100);
trace(rect1); // (x=0, y=0, w=100, h=100)
var rect2:Rectangle = new Rectangle(120, 60, 100, 100);
trace(rect2); // (x=120, y=60, w=100, h=100)
trace(rect1.union(rect2)); // (x=0, y=0, w=220, h=160)
Using Rectangle objects 439
You use the intersection() method to find the rectangular region formed by the
overlapping region of two rectangles:
import flash.display.*;
import flash.geom.Rectangle;
var rect1:Rectangle = new Rectangle(0, 0, 100, 100);
trace(rect1); // (x=0, y=0, w=100, h=100)
var rect2:Rectangle = new Rectangle(80, 60, 100, 100);
trace(rect2); // (x=120, y=60, w=100, h=100)
trace(rect1.intersection(rect2)); // (x=80, y=60, w=20, h=40)
You use the intersects() method to find out whether two rectangles intersect. You can also
use the intersects() method to find out whether a display object is in a certain region of
the Stage. For example, in the following code, assume that the coordinate space of the display
object container that contains the circle object is the same as that of the Stage. The example
shows how to use the intersects() method to determine if a display object, circle,
intersects specified regions of the Stage, defined by the target1 and target2 Rectangle
objects:
import flash.display.*;
import flash.geom.Rectangle;
var circle:Shape = new Shape();
circle.graphics.lineStyle(2, 0xFF0000);
circle.graphics.drawCircle(250, 250, 100);
addChild(circle);
var circleBounds:Rectangle = circle.getBounds(stage);
var target1:Rectangle = new Rectangle(0, 0, 100, 100);
trace(circleBounds.intersects(target1)); // false
var target2:Rectangle = new Rectangle(0, 0, 300, 300);
trace(circleBounds.intersects(target2)); // true
Similarly, you can use the intersects() method to find out whether the bounding
rectangles of two display objects overlap. You can use the getRect() method of the
DisplayObject class to include any additional space that the strokes of a display object may
add to a bounding region.
440 Working with geometry
Other uses of Rectangle objects
Rectangle objects are used in the following methods and properties:
Using Matrix objects
The Matrix class represents a transformation matrix that determines how to map points from
one coordinate space to another. You can perform various graphical transformations on a
display object by setting the properties of a Matrix object, applying that Matrix object to the
matrix property of a Transform object, and then applying that Transform object as the
transform property of the display object. These transformation functions include translation
(x and y repositioning), rotation, scaling, and skewing.
Class Methods or properties Description
BitmapData applyFilter(), colorTransform(),
copyChannel(), copyPixels(),
draw(), fillRect(),
generateFilterRect(),
getColorBoundsRect(), getPixels(),
merge(), paletteMap(),
pixelDissolve(), setPixels(), and
threshold()
Used as the type for
some parameters to
define a region of the
BitmapData object.
DisplayObject getBounds(), getRect(),
scrollRect, scale9Grid
Used as the data type
for the property or the
data type returned.
PrintJob addPage() Used to define the
printArea parameter.
Sprite startDrag() Used to define the
bounds parameter.
TextField getCharBoundaries() Used as the return
value type.
Transform pixelBounds Used as the data type.
Using Matrix objects 441
Defining Matrix objects
Although you could define a matrix by directly adjusting the properties (a, b, c, d, tx, ty) of a
Matrix object, it is easier to use the createBox() method. This method includes parameters
that let you directly define the scaling, rotation, and translation effects of the resulting matrix.
For example, the following code creates a Matrix object that has the effect of scaling an object
horizontally by 2.0, scaling it vertically by 3.0, rotating it by 45 degrees, moving (translating)
it 10 pixels to the right, and moving it 20 pixels down:
var matrix:Matrix = new Matrix();
var scaleX:Number = 2.0;
var scaleY:Number = 3.0;
var rotation:Number = 2 * Math.PI * (45 / 360);
var tx:Number = 10;
var ty:Number = 20;
matrix.createBox(scaleX, scaleY, rotation, tx, ty);
You can also adjust the scaling, rotation, and translation effects of a Matrix object by using the
scale(), rotate(), and translate() methods. Note that these methods combine with the
values of the existing Matrix object. For instance, the following code sets a Matrix object that
scales an object by a factor of 4 and rotates it 60 degrees, since the scale() and rotate()
methods are called twice:
var matrix:Matrix = new Matrix();
var rotation:Number = 2 * Math.PI * (30 / 360); // 30°
var scaleFactor:Number = 2;
matrix.scale(scaleFactor, scaleFactor);
matrix.rotate(rotation);
matrix.scale(scaleX, scaleY);
matrix.rotate(rotation);
myDisplayObject.transform.matrix = matrix;
To apply a skew transformation to a Matrix object, adjust its b or c property. Adjusting the b
property skews the matrix vertically, and adjusting the c property skews the matrix
horizontally. The following code skews the myMatrix Matrix object vertically by a factor of 2:
var skewMatrix:Matrix = new Matrix();
skewMatrix.b = Math.tan(2);
myMatrix.concat(skewMatrix);
442 Working with geometry
You can apply a Matrix transformation to the transform property of a display object. For
example, the following code applies a matrix transformation to a display object named
myDisplayObject:
var matrix:Matrix = myDisplayObject.transform.matrix;
var scaleFactor:Number = 2;
var rotation:Number = 2 * Math.PI * (60 / 360); // 60°
matrix.scale(scaleFactor, scaleFactor);
matrix.rotate(rotation);
myDisplayObject.transform.matrix = matrix;
The first line sets a Matrix object to the existing transformation matrix used by the
myDisplayObject display object (the matrix property of the transformation property of
the myDisplayObject display object). This way, the Matrix class methods that you call will
have a cumulative effect on the display object’s existing position, scale, and rotation.
Example: Applying a matrix
transformation to a display object
The DisplayObjectTransformer sample application shows a number of features of using the
Matrix class to transform a display object, including the following:
■ Rotating the display object
■ Scaling the display object
■ Translating (repositioning) the display object
■ Skewing the display object
NOTE
The ColorTransform class is also included in the flash.geometry package. This class
is used to set the colorTransform property of a Transform object. Since it does not
apply any sort of geometrical transformation, it is not discussed in this chapter. For
more information, see the ColorTransform class in the ActionScript 3.0 Language
and Components Reference.
Example: Applying a matrix transformation to a display object 443
The application provides an interface for adjusting the parameters of the matrix
transformation, as follows:
When the user clicks the Transform button, the application applies the appropriate
transformation.
The original display object, and the display object rotated by -45° and scaled by 50%
444 Working with geometry
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The DisplayObjectTransformer application files can
be found in the folder Samples/DisplayObjectTransformer. The application consists of the
following files:
Defining the MatrixTransformer class
The MatrixTransformer class includes static methods that apply geometric transformations of
Matrix objects.
The transform() method
The transform() method includes parameters for each of the following:
■ sourceMatrix—The input matrix, which the method transforms
■ xScale and yScale—The x and y scale factor
■ dx and dy—The x and y translation amounts, in pixels
■ rotation—The rotation amount, in degrees
■ skew—The skew factor, as a percentage
■ skewType—The direction in which the skew, either "right" or "left"
The return value is the resulting matrix.
The transform() method calls the following static methods of the class:
■ skew()
■ scale()
■ translate()
■ rotate()
Each returns the source matrix with the applied transformation.
File Description
DisplayObjectTransformer.mxml
or
DisplayObjectTransformer.fla
The main application file in Flash (FLA) or Flex
(MXML)
com/example/programmingas3/geometry/
MatrixTransformer.as
A class that contains methods for applying
matrix transformations.
img/ A directory containing sample image files used
by the application.
Example: Applying a matrix transformation to a display object 445
The skew() method
The skew() method skews the matrix by adjusting the b and c properties of the matrix. An
optional parameter, unit, determines the units used to define the skew angle, and if necessary,
the method converts the angle value to radians:
if (unit == "degrees")
{
angle = Math.PI * 2 * angle / 360;
}
if (unit == "gradients")
{
angle = Math.PI * 2 * angle / 100;
}
A skewMatrix Matrix object is created and adjusted to apply the skew transformation.
Initially, it is the identity matrix, as follows:
var skewMatrix:Matrix = new Matrix();
The skewSide parameter determines the side to which the skew is applied. If it is set to
"right", the following code sets the b property of the matrix:
skewMatrix.b = Math.tan(angle);
Otherwise, the bottom side is skewed by adjusting the c property of the Matrix, as follows:
skewMatrix.c = Math.tan(angle);
The resulting skew is then applied to the existing matrix by concatenating the two matrixes, as
the following example shows:
sourceMatrix.concat(skewMatrix);
return sourceMatrix;
The scale() method
As the following example shows, the scale() method first adjusts the scale factor if it is
provided as a percentage, and then uses the scale() method of the matrix object:
if (percent)
{
xScale = xScale / 100;
yScale = yScale / 100;
}
sourceMatrix.scale(xScale, yScale);
return sourceMatrix;
446 Working with geometry
The translate() method
The translate() method simply applies the dx and dy translation factors by calling the
translate() method of the matrix object, as follows:
sourceMatrix.translate(dx, dy);
return sourceMatrix;
The rotate() method
The rotate() method converts the input rotation factor to radians (if it is provided in
degrees or gradients), and then calls the rotate() method of the matrix object:
if (unit == "degrees")
{
angle = Math.PI * 2 * angle / 360;
}
if (unit == "gradients")
{
angle = Math.PI * 2 * angle / 100;
}
sourceMatrix.rotate(angle);
return sourceMatrix;
Calling the MatrixTransformer.transform() method
from the application
The application contains a user interface for getting the transformation parameters from the
user. It then passes these, along with the matrix property of the transform property of the
display object, to the Matrix.transform() method, as follows:
tempMatrix = MatrixTransformer.transform(tempMatrix,
xScaleSlider.value,
yScaleSlider.value,
dxSlider.value,
dySlider.value,
rotationSlider.value,
skewSlider.value,
skewSide );
The application then applies the return value to the matrix property of the transform
property of the display object, thereby triggering the transformation:
img.content.transform.matrix = tempMatrix;
447
14
CHAPTER 14
Using the drawing API
Although imported images and artwork are important, the functionality known as the
drawing API, which allows you to draw lines and shapes in ActionScript, gives you the
freedom to start an application with the computer equivalent of a blank canvas, on which you
can create whatever images you wish. The ability to create your own graphics opens up broad
possibilities for your applications. With the techniques covered in this chapter you can create
a drawing program, make animated, interactive art, or programmatically create your own user
interface elements, among many possibilities.
Contents
Basics of using the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448
Understanding the Graphics class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Drawing lines and curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Drawing shapes using built-in methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .453
Creating gradient lines and fills . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454
Using the Math class with drawing methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
Animating with the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
Example: Algorithmic Visual Generator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .462
448 Using the drawing API
Basics of using the drawing API
Introduction to using the drawing API
The drawing API is the name for the functionality built into ActionScript that allows you to
create vector graphics—lines, curves, shapes, fills, and gradients—and display them on the
screen using ActionScript. The flash.display.Graphics class provides this functionality. You can
draw with ActionScript on any Shape, Sprite, or MovieClip instance, using the graphics
property defined in each of those classes. (Each of those classes’ graphics property is in fact
an instance of the Graphics class.)
If you’re just getting started with drawing with code, the Graphics class includes several
methods that make it easy to draw common shapes like circles, ellipses, rectangles, and
rectangles with rounded corners. You can draw them as empty lines or filled shapes. When
you need more advanced functionality, the Graphics class also includes methods for drawing
lines and quadratic Bézier curves, which you can use in conjunction with the trigonometry
functions in the Math class to create any shape you need.
Common drawing API tasks
The following tasks are things you’ll likely want to accomplish using the drawing API in
ActionScript, which are described in this chapter:
■ Defining line styles and fill styles for drawing shapes
■ Drawing straight lines and curves
■ Using methods for drawing shapes such as circles, ellipses, and rectangles
■ Drawing with gradient lines and fills
■ Defining a matrix for creating a gradient
■ Using trigonometry with the drawing API
■ Incorporating the drawing API into animation
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Anchor point: One of the two end points of a quadratic Bézier curve.
■ Control point: The point that defines the direction and amount of curve of a quadratic
Bézier curve. The curved line never reaches the control point; however, the line curves as
though being drawn toward the control point.
Basics of using the drawing API 449
■ Coordinate space: The graph of coordinates contained in a display object, on which its
child elements are positioned.
■ Fill: The solid inner portion of a shape that has a line filled in with color, or all of a shape
that has no outline.
■ Gradient: A color that consists of a gradual transition from one color to one or more other
colors (as opposed to a solid color).
■ Point: A single location in a coordinate space. In the 2-d coordinate system used in
ActionScript, a point is defined by its location along the x axis and the y axis (the point’s
coordinates).
■ Quadratic Bézier curve: A type of curve defined by a particular mathematical formula. In
this type of curve, the shape of the curve is calculated based on the positions of the anchor
points (the end points of the curve) and a control point that defines the amount and
direction of the curve.
■ Scale: The size of an object relative to its original size. When used as a verb, to scale an
object means to change its size by stretching or shrinking the object.
■ Stroke: The outline portion of a shape that has a line filled in with color, or the lines of an
un-filled shape.
■ Translate: To change a point’s coordinates from one coordinate space to another.
■ X axis: The horizontal axis in the 2-d coordinate system used in ActionScript.
■ Y axis: The vertical axis in the 2-d coordinate system used in ActionScript.
Working through in-chapter examples
While you’re working through the chapter you may want to test some of the example code
listings. Because this chapter deals with drawing visual content, testing the code listings
involves running the code and viewing the results in the SWF that’s created. To test the code
listings:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Run the program using Control > Test Movie.
You will see the results of the code listing in the SWF file that’s created.
450 Using the drawing API
Understanding the Graphics class
Each Shape, Sprite, and MovieClip object has a graphics property, which is an instance of
the Graphics class. The Graphics class includes properties and methods for drawing lines, fills,
and shapes. If you want a display object to use solely as a canvas for drawing content, you can
use a Shape instance. A Shape instance will perform better than other display objects for
drawing, because it doesn’t have the overhead of the additional functionality in the Sprite and
MovieClip classes. If you want a display object on which you can draw graphical content and
also want that object to contain other display objects, you can use a Sprite instance. For more
information on determining which display object to use for various tasks, see “Choosing a
DisplayObject subclass” on page 393.
Drawing lines and curves
All drawing that you do with a Graphics instance is based on basic drawing with lines and
curves. Consequently, all ActionScript drawing must be performed using the same series of
steps:
■ Define line and fill styles
■ Set the initial drawing position
■ Draw lines, curves, and shapes (optionally moving the drawing point)
■ If necessary, finish creating a fill
Defining line and fill styles
To draw with the graphics property of a Shape, Sprite, or MovieClip instance, you must first
define the style (line size and color, fill color) to use when drawing. Just like when you use the
drawing tools in Adobe Flash CS3 Professional or another drawing application, when you’re
using ActionScript to draw you can draw with or without a stroke, and with or without a fill
color. You specify the appearance of the stroke using the lineStyle() or
lineGradientStyle() method. To create a solid line, use the lineStyle() method. When
calling this method, the most common values you’ll specify are the first three parameters: line
thickness, color, and alpha. For example, this line of code tells the Shape named myShape to
draw lines that are 2 pixels thick, red (0x990000), and 75% opaque:
myShape.graphics.lineStyle(2, 0x990000, .75);
Drawing lines and curves 451
The default value for the alpha parameter is 1.0 (100%), so you can leave that parameter off if
you want a completely opaque line. The lineStyle() method also accepts two additional
parameters for pixel hinting and scale mode; for more information about using those
parameters see the description of the Graphics.lineStyle() method in the ActionScript 3.0
Language and Components Reference.
To create a gradient line, use the lineGradientStyle() method. This method is described in
“Creating gradient lines and fills” on page 454.
If you want to create a filled shape, you call the beginFill(), beginGradientFill(), or
beginBitmapFill() methods before starting the drawing. The most basic of these, the
beginFill() method, accepts two parameters: the fill color, and (optionally) an alpha value
for the fill color. For example, if you want to draw a shape with a solid green fill, you would
use the following code (assuming you’re drawing on an object named myShape):
myShape.graphics.beginFill(0x00FF00);
Calling any fill method implicitly ends any previous fill before starting a new one. Calling any
method that specifies a stroke style replaces the previous stroke, but does not alter a previously
specified fill, and vice versa.
Once you have specified the line style and fill properties, the next step is to indicate the
starting point for your drawing. The Graphics instance has a drawing point, like the tip of a
pen on a piece of paper. Wherever the drawing point is located, that is where the next drawing
action will begin. Initially a Graphics object begins with its drawing point at the point 0, 0 in
the coordinate space of the object on which it’s drawing. To start the drawing at a different
point, you can first call the moveTo() method before calling one of the drawing methods.
This is analogous to lifting the pen tip off of the paper and moving it to a new position.
With the drawing point in place you draw using a series of calls to the drawing methods
lineTo() (for drawing straight lines) and curveTo() (for drawing curved lines).
While drawing, if you have specified a fill color, you can tell Adobe Flash Player to close off
the fill by calling the endFill() method. If you have not drawn a closed shape (in other
words, if at the time you call endFill() the drawing point is not at the starting point of the
shape), when you call the endFill() method Flash Player automatically closes the shape by
drawing a straight line from the current drawing point to the location specified in the most
recent moveTo() call. If you have started a fill and not called endFill(), calling
beginFill() (or one of the other fill methods) closes the current fill and starts the new one.
TIP
While you are drawing, you can call the moveTo() method at any time to move the
drawing point to a new position without drawing.
452 Using the drawing API
Drawing straight lines
When you call the lineTo() method, the Graphics object draws a straight line from the
current drawing point to the coordinates you specify as the two parameters in the method call,
drawing with the line style you have specified. For example, this line of code puts the drawing
point at the point 100, 100 then draws a line to the point 200, 200:
myShape.graphics.moveTo(100, 100);
myShape.graphics.lineTo(200, 200);
The following example draws red and green triangles with a height of 100 pixels:
var triangleHeight:uint = 100;
var triangle:Shape = new Shape();
// red triangle, starting at point 0, 0
triangle.graphics.beginFill(0xFF0000);
triangle.graphics.moveTo(triangleHeight/2, 0);
triangle.graphics.lineTo(triangleHeight, triangleHeight);
triangle.graphics.lineTo(0, triangleHeight);
triangle.graphics.lineTo(triangleHeight/2, 0);
// green triangle, starting at point 200, 0
triangle.graphics.beginFill(0x00FF00);
triangle.graphics.moveTo(200 + triangleHeight/2, 0);
triangle.graphics.lineTo(200 + triangleHeight, triangleHeight);
triangle.graphics.lineTo(200, triangleHeight);
triangle.graphics.lineTo(200 + triangleHeight/2, 0);
this.addChild(triangle);
Drawing curves
The curveTo() method draws a quadratic Bézier curve. This draws an arc that connects two
points (called anchor points) while bending toward a third point (called the control point).
The Graphics object uses the current drawing position as the first anchor point. When you
call the curveTo() method, you pass four parameters: the x and y coordinates of the control
point, followed by the x and y coordinates of the second anchor point. For example, the
following code draws a curve starting at point 100, 100 and ending at point 200, 200.
Because the control point is at point 175, 125, this creates a curve that moves to the right and
then downward:
myShape.graphics.moveTo(100, 100);
myShape.graphics.curveTo(175, 125, 200, 200);
The following example draws red and green circular objects with a width and height of 100
pixels. Note that due to the nature of the quadratic Bézier equation, these are not perfect
circles:
Drawing shapes using built-in methods 453
var size:uint = 100;
var roundObject:Shape = new Shape();
// red circular shape
roundObject.graphics.beginFill(0xFF0000);
roundObject.graphics.moveTo(size / 2, 0);
roundObject.graphics.curveTo(size, 0, size, size / 2);
roundObject.graphics.curveTo(size, size, size / 2, size);
roundObject.graphics.curveTo(0, size, 0, size / 2);
roundObject.graphics.curveTo(0, 0, size / 2, 0);
// green circular shape
roundObject.graphics.beginFill(0x00FF00);
roundObject.graphics.moveTo(200 + size / 2, 0);
roundObject.graphics.curveTo(200 + size, 0, 200 + size, size / 2);
roundObject.graphics.curveTo(200 + size, size, 200 + size / 2, size);
roundObject.graphics.curveTo(200, size, 200, size / 2);
roundObject.graphics.curveTo(200, 0, 200 + size / 2, 0);
this.addChild(roundObject);
Drawing shapes using built-in methods
For convenience when drawing common shapes such as circles, ellipses, rectangles, and
rectangles with rounded corners, ActionScript 3.0 has methods that draw these common
shapes for you. These are the drawCircle(), drawEllipse(), drawRect(),
drawRoundRect(), and drawRoundRectComplex() methods of the Graphics class. These
methods may be used in place of the lineTo() and curveTo() methods. Note, however, that
you must still specify line and fill styles before calling these methods.
The following example recreates the example of drawing red, green, and blue squares with
width and height of 100 pixels. This code uses the drawRect() method, and additionally
specifies that the fill color has an alpha of 50% (0.5):
var squareSize:uint = 100;
var square:Shape = new Shape();
square.graphics.beginFill(0xFF0000, 0.5);
square.graphics.drawRect(0, 0, squareSize, squareSize);
square.graphics.beginFill(0x00FF00, 0.5);
square.graphics.drawRect(200, 0, squareSize, squareSize);
square.graphics.beginFill(0x0000FF, 0.5);
square.graphics.drawRect(400, 0, squareSize, squareSize);
square.graphics.endFill();
this.addChild(square);
454 Using the drawing API
In a Sprite or MovieClip object, the drawing content created with the graphics property
always appears behind all child display objects that are contained by the object. Also, the
graphics property content is not a separate display object so it does not appear in the list of a
Sprite or MovieClip object’s children. For example, the following Sprite object has a circle
drawn with its graphics property, and it has a TextField object in its list of child display
objects:
var mySprite:Sprite = new Sprite();
mySprite.graphics.beginFill(0xFFCC00);
mySprite.graphics.drawCircle(30, 30, 30);
var label:TextField = new TextField();
label.width = 200;
label.text = "They call me mellow yellow...";
label.x = 20;
label.y = 20;
mySprite.addChild(label);
this.addChild(mySprite);
Note that the TextField appears on top of the circle drawn with the graphics object.
Creating gradient lines and fills
The graphics object can also draw strokes and fills with gradients rather than solid colors. A
gradient stroke is created with the lineGradientStyle() method and a gradient fill is
created with the beginGradientFill() method.
Both methods accept the same parameters. The first four are required: type, colors, alphas,
and ratios. The remaining four are optional but are useful for advanced customization.
■ The first parameter specifies the type of gradient you are creating. Acceptable values are
GradientFill.LINEAR or GradientFill.RADIAL.
■ The second parameter specifies the array of the color values to use. In a linear gradient, the
colors will be arranged from left to right. In a radial gradient, they will be arranged from
inside to outside. The order of the colors of the array represents the order that the colors
will be drawn in the gradient.
■ The third parameter specifies the alpha transparency values of the corresponding colors in
the previous parameter.
■ The fourth parameter specifies ratios, or the emphasis each color has within the gradient.
Acceptable values range from 0-255. These values do not represent any width or height,
but rather the position within the gradient; 0 represents the beginning of the gradient,
255 represents the end of the gradient. The array of ratios must increase sequentially and
have the same number of entries as both the color and alpha arrays specified in the second
and third parameters.
Creating gradient lines and fills 455
Although the fifth parameter, the transformation matrix, is optional, it is commonly used
because it provides an easy and powerful way to control the gradient’s appearance. This
parameter accepts a Matrix instance. The easiest way to create a Matrix object for a gradient is
to use the Matrix class’s createGradientBox() method.
Defining a Matrix object for use with a gradient
You use the beginGradientFill() and lineGradientStyle() methods of the
flash.display.Graphics class to define gradients for use in shapes. When you define a gradient,
you supply a matrix as one of the parameters of these methods.
The easiest way to define the matrix is by using the Matrix class’s createGradientBox()
method, which creates a matrix that is used to define the gradient. You define the scale,
rotation, and position of the gradient using the parameters passed to the
createGradientBox() method. The createGradientBox() method accepts the following
parameters:
■ Gradient box width: the width (in pixels) to which the gradient will spread
■ Gradient box height: the height (in pixels) to which the gradient will spread
■ Gradient box rotation: the rotation (in radians) that will be applied to the gradient
■ Horizontal translation: how far (in pixels) the gradient is shifted horizontally
■ Vertical translation: how far (in pixels) the gradient is shifted vertically
For example, consider a gradient with the following characteristics:
■ GradientType.LINEAR
■ Two colors, green and blue, with the ratios array set to [0, 255]
■ SpreadMethod.PAD
■ InterpolationMethod.LINEAR_RGB
456 Using the drawing API
The following examples show gradients in which the rotation parameter of the
createGradientBox() method differs as indicated, but all other settings stay the same:
width = 100;
height = 100;
rotation = 0;
tx = 0;
ty = 0;
width = 100;
height = 100;
rotation = Math.PI/4; // 45°
tx = 0;
ty = 0;
width = 100;
height = 100;
rotation = Math.PI/2; // 90°
tx = 0;
ty = 0;
Creating gradient lines and fills 457
The following examples show the effects on a green-to-blue linear gradient in which the
rotation, tx, and ty parameters of the createGradientBox() method differ as indicated,
but all other settings stay the same:
The width, height, tx, and ty parameters of the createGradientBox() method affect the
size and position of a radial gradient fill as well, as the following example shows:
width = 50;
height = 100;
rotation = 0;
tx = 0;
ty = 0;
width = 50;
height = 100;
rotation = 0
tx = 50;
ty = 0;
width = 100;
height = 50;
rotation = Math.PI/2; // 90°
tx = 0;
ty = 0;
width = 100;
height = 50;
rotation = Math.PI/2; // 90°
tx = 0;
ty = 50;
width = 50;
height = 100;
rotation = 0;
tx = 25;
ty = 0;
458 Using the drawing API
The following code produces the last radial gradient illustrated:
import flash.display.Shape;
import flash.display.GradientType;
import flash.geom.Matrix;
var type:String = GradientType.RADIAL;
var colors:Array = [0x00FF00, 0x000088];
var alphas:Array = [1, 1];
var ratios:Array = [0, 255];
var spreadMethod:String = SpreadMethod.PAD;
var interp:String = InterpolationMethod.LINEAR_RGB;
var focalPtRatio:Number = 0;
var matrix:Matrix = new Matrix();
var boxWidth:Number = 50;
var boxHeight:Number = 100;
var boxRotation:Number = Math.PI/2; // 90°
var tx:Number = 25;
var ty:Number = 0;
matrix.createGradientBox(boxWidth, boxHeight, boxRotation, tx, ty);
var square:Shape = new Shape;
square.graphics.beginGradientFill(type,
colors,
alphas,
ratios,
matrix,
spreadMethod,
interp,
focalPtRatio);
square.graphics.drawRect(0, 0, 100, 100);
addChild(square);
Note that the width and height of the gradient fill is determined by the width and height of
the gradient matrix rather than the width or height that is drawn using the Graphics object.
When drawing with the Graphics object, you draw what exists at those coordinates in the
gradient matrix. Even if you use one of the shape methods of a Graphics object such as
drawRect(), the gradient does not stretch itself to the size of the shape that is drawn—the
gradient’s size must be specified in the gradient matrix itself.
Creating gradient lines and fills 459
The following illustrates the visual difference between the dimensions of the gradient matrix
and the dimensions of the draw itself:
var myShape:Shape = new Shape();
var gradientBoxMatrix:Matrix = new Matrix();
gradientBoxMatrix.createGradientBox(100, 40, 0, 0, 0);
myShape.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000,
0x00FF00, 0x0000FF], [1, 1, 1], [0, 128, 255], gradientBoxMatrix);
myShape.graphics.drawRect(0, 0, 50, 40);
myShape.graphics.drawRect(0, 50, 100, 40);
myShape.graphics.drawRect(0, 100, 150, 40);
myShape.graphics.endFill();
this.addChild(myShape);
This code draws three gradients with the same fill style, specified with an equal distribution of
red, green, and blue. The gradients are drawn using the drawRect() method with pixel
widths of 50, 100, and 150 respectively. The gradient matrix which is specified in the
beginGradientFill() method is created with a width of 100 pixels. This means that the
first gradient will encompass only half of the gradient spectrum, the second will encompass all
of it, and the third will encompass all of it and have an additional 50 pixels of blue extending
to the right.
The lineGradientStyle() method works similarly to beginGradientFill() except that in
addition to defining the gradient, you must specify the thickness of the stroke using the
lineStyle() method before drawing. The following code draws a box with a red, green, and
blue gradient stroke:
var myShape:Shape = new Shape();
var gradientBoxMatrix:Matrix = new Matrix();
gradientBoxMatrix.createGradientBox(200, 40, 0, 0, 0);
myShape.graphics.lineStyle(5, 0);
myShape.graphics.lineGradientStyle(GradientType.LINEAR, [0xFF0000,
0x00FF00, 0x0000FF], [1, 1, 1], [0, 128, 255], gradientBoxMatrix);
myShape.graphics.drawRect(0, 0, 200, 40);
this.addChild(myShape);
For more information on the Matrix class, see “Using Matrix objects” on page 440.
460 Using the drawing API
Using the Math class with drawing
methods
A Graphics object draws circles and squares, but can also draw more complex forms,
particularly when the drawing methods are used in combination with the properties and
methods of the Math class. The Math class contains constants of common mathematical
interest, such as Math.PI (approximately 3.14159265...), a constant for the ratio of the
circumference of a circle to its diameter. It also contains methods for trigonometry functions,
including Math.sin(), Math.cos(), and Math.tan() among others. Drawing shapes using
these methods and constants create more dynamic visual effects, particularly when used with
repetition or recursion.
Many methods of the Math class expect circular measurements in units of radians rather than
degrees. Converting between these two types of units is a common use of the Math class:
var degrees = 121;
var radians = degrees * Math.PI / 180;
trace(radians) // 2.111848394913139
The following example creates a sine wave and a cosine wave, to highlight the difference
between the Math.sin() and Math.cos() methods for a given value.
var sinWavePosition = 100;
var cosWavePosition = 200;
var sinWaveColor:uint = 0xFF0000;
var cosWaveColor:uint = 0x00FF00;
var waveMultiplier:Number = 10;
var waveStretcher:Number = 5;
var i:uint;
for(i = 1; i < stage.stageWidth; i++)
{
var sinPosY:Number = Math.sin(i / waveStretcher) * waveMultiplier;
var cosPosY:Number = Math.cos(i / waveStretcher) * waveMultiplier;
graphics.beginFill(sinWaveColor);
graphics.drawRect(i, sinWavePosition + sinPosY, 2, 2);
graphics.beginFill(cosWaveColor);
graphics.drawRect(i, cosWavePosition + cosPosY, 2, 2);
}
Animating with the drawing API 461
Animating with the drawing API
One advantage of creating content with the drawing API is that you are not limited to
positioning your content once. What you draw can be modified by maintaining and
modifying the variables you use to draw. You can convey animation by changing variables and
redrawing, either over a period of frames or with a timer.
For example, the following code changes the display with each passing frame (by listening to
the Event.ENTER_FRAME event), incrementing the current degree count, and directs the
graphics object to clear and redraw with the updated position.
stage.frameRate = 31;
var currentDegrees:Number = 0;
var radius:Number = 40;
var satelliteRadius:Number = 6;
var container:Sprite = new Sprite();
container.x = stage.stageWidth / 2;
container.y = stage.stageHeight / 2;
addChild(container);
var satellite:Shape = new Shape();
container.addChild(satellite);
addEventListener(Event.ENTER_FRAME, doEveryFrame);
function doEveryFrame(event:Event):void
{
currentDegrees += 4;
var radians:Number = getRadians(currentDegrees);
var posX:Number = Math.sin(radians) * radius;
var posY:Number = Math.cos(radians) * radius;
satellite.graphics.clear();
satellite.graphics.beginFill(0);
satellite.graphics.drawCircle(posX, posY, satelliteRadius);
}
function getRadians(degrees:Number):Number
{
return degrees * Math.PI / 180;
}
To produce a significantly different result, you can experiment by modifying the initial seed
variables at the beginning of the code, currentDegrees, radius, and satelliteRadius. For
example, try shrinking the radius variable and/or increasing the totalSatellites variable. This is
only one example of how the drawing API can create a visual display whose complexity
conceals the simplicity of its creation.
462 Using the drawing API
Example: Algorithmic Visual Generator
The Algorithmic Visual Generator example dynamically draws to the stage several “satellites”,
or circles moving in a circular orbit. Among the features explored are:
■ Using the drawing API to draw a basic shape with dynamic appearances
■ Connecting user interaction with the properties that are used in a draw
■ Conveying animation by clearing the stage on each frame and redrawing
The example in the previous subsection animated a solitary “satellite” using the
Event.ENTER_FRAME event. This example expands upon this, building a control panel with
series of sliders that immediately update the visual display of several satellites. This example
formalizes the code into external classes and wraps the satellite creation code into a loop,
storing a reference to each satellite in a satellites array.
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The application files can be found in the folder
Samples/AlgorithmicVisualGenerator. This folder contains the following files:
Setting the listeners
The application first creates three listeners. The first listens for a dispatched event from the
control panel that a rebuild of the satellites is necessary. The second listens to changes to the
size of the SWF file’s stage. The third listens for each passing frame in the SWF file and to
redraw using the doEveryFrame() function.
File Description
AlgorithmicVisualGenerator.fla The main application file in Flash (FLA).
com/example/programmingas3/
algorithmic/AlgorithmicVisualGenerator.as
The class that provides the main functionality of
the application, including drawing satellites on
the stage and responding to events from the
control panel to update the variables that affect
the drawing of satellites.
com/example/programmingas3/
algorithmic/ControlPanel.as
A class that manages user interaction with
several sliders and dispatching events when this
occurs.
com/example/programmingas3/
algorithmic/Satellite.as
A class which represents the display object that
rotates in an orbit around a central point and
contains properties related to its current draw
state.
Example: Algorithmic Visual Generator 463
Creating the satellites
Once these listeners are set, the build() function is called. This function first calls the
clear() function, which empties the satellites array and clears any previous draws to the
stage. This is necessary since the build() function could be recalled whenever the control
panel sends an event to do so, such as when the color settings have been changed. In such a
case, the satellites must be removed and recreated.
The function then creates the satellites, setting the initial properties needed for creation, such
as a the position variable, which starts at a random position in the orbit, and the color
variable, which in this example does not change once the satellite has been created.
As each satellite is created, a reference to it is added to the satellites array. When the
doEveryFrame() function is called, it will update to all satellites in this array.
Updating the satellite position
The doEveryFrame() function is the heart of the application’s animation process. It is called
for every frame, at a rate equal the framerate of the SWF file. Because the variables of the draw
change slightly, this conveys the appearance of animation.
The function first clears all previous draws and redraws the background. Then, it loops
through each satellite container and increments the position property of each satellite, and
updates the radius and orbitRadius properties that may have changed from user
interaction with the control panel. Finally, the satellite updates to its new position by calling
the draw() method of the Satellite class.
Note that the counter, i, only increments up to the visibleSatellites variable. This is
because if the user has limited the amount of satellites that are displayed through the control
panel, the remaining satellites in the loop should not be redrawn but should instead be
hidden. This occurs in a loop which immediately follows the loop responsible for drawing.
When the doEveryFrame() function completes, the number of visibleSatellites update
in position across the screen.
464 Using the drawing API
Responding to user interaction
User interaction occurs via the control panel, which is managed by the ControlPanel class.
This class sets a listener along with the individual minimum, maximum, and default values of
each slider. As the user moves these sliders, the changeSetting() function is called. This
function updates the properties of the control panel. If the change requires a rebuild of the
display, an event is dispatched which is then handled in the main application file. As the
control panel settings change, the doEveryFrame() function draws each satellite with the
updated variables.
Customizing further
This example is only a basic schematic of how to generate visuals using the drawing API. It
uses relatively few lines of code to create an interactive experience that appears quite complex.
Even so, this example could be extended with minor changes. A few ideas:
■ The doEveryFrame() function could increment the color value of the satellite.
■ The doEveryFrame() function could shrink or expand the satellite radius over time.
■ The satellite radius does not have to be circular; it could use the Math class to move
according to a sine wave, for example.
■ Satellites could use hit detection with other satellites.
The drawing API can be used as an alternative to creating visual effects in the Flash authoring
environment, drawing basic shapes at run time. But it can also be used to create visual effects
of a variety and scope that are not possible to create by hand. Using the drawing API and a bit
of mathematics, the ActionScript author can give life to many unexpected creations.
465
15
CHAPTER 15
Filtering display objects
Historically, the application of filter effects to bitmap images has been the domain of
specialized image editing software such as Adobe Photoshop® and Adobe Fireworks®.
ActionScript 3.0 includes the flash.filters package, which contains a series of bitmap effect
filter classes to allow developers to programmatically apply filters to bitmaps and display
objects to achieve many of the same effects that are available in graphics manipulation
applications.
Contents
Basics of filtering display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465
Creating and applying filters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467
Available display filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472
Example: Filter Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
Basics of filtering display objects
Introduction to filtering display objects
One of the ways to add polish to an application is to add simple graphic effects, such as a drop
shadow behind a photo to create the illusion of 3-d, or a glow around a button to show that it
is active. ActionScript 3.0 includes nine filters that you can apply to any display object or to a
BitmapData instance. These range from basic filters, such as the drop shadow and glow filters,
to complex filters for creating various effects, such as the displacement map filter and the
convolution filter.
466 Filtering display objects
Common filtering tasks
The following tasks are things you’ll likely want to accomplish using filters in ActionScript:
■ Creating a filter
■ Applying a filter to a display object
■ Applying a filter to the image data in a BitmapData instance
■ Removing filters from an object
■ Creating various filter effects, such as:
■ Glow
■ Blur
■ Drop shadow
■ Sharpness
■ Displacement
■ Edge detection
■ Embossing
■ and other effects
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Bevel: An edge created by lightening pixels on two sides and darkening pixels on the
opposite two sides, creating a three-dimensional border effect commonly used for raised or
indented buttons and similar graphics.
■ Convolution: Distorting pixels in an image by combining each pixel’s value with the
values of some or all of its neighboring pixels, using various ratios.
■ Displacement: Shifting or moving pixels in an image to a new position.
■ Matrix: A grid of numbers used to perform certain mathematical calculations by applying
the numbers in the grid to various values, then combining the results.
Working through in-chapter examples
While you’re working through the chapter, you may want to test the example code listings
that are provided. Because this chapter deals with creating and manipulating visual content,
testing the code involves running the code and viewing the results in the SWF that’s created.
Nearly all the examples either create content using the drawing API or load images to which
filters are applied.
Creating and applying filters 467
To test the code in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code into the Script pane.
4. Run the program using Control > Test Movie.
You see the results of the code in the SWF file that’s created.
Nearly all the example code includes code that creates a bitmap image, so you can just test the
code directly without needing to provide any bitmap content. Alternatively you can change
the code listings to load your own images and use those in place of the ones in the examples.
Creating and applying filters
Filters allow you to apply a range of effects to bitmap and display objects, ranging from drop
shadows to bevels and blurs. Each filter is defined as a class, so applying filters involves
creating instances of filter objects, which is no different from constructing any other object.
Once you’ve created an instance of a filter object, it can easily be applied to a display object by
using the object’s filters property, or in the case of a BitmapData object, by using the
applyFilter() method.
Creating a new filter
To create a new filter object, simply call the constructor method of your selected filter class.
For example, to create a new DropShadowFilter object, use the following code:
import flash.filters.DropShadowFilter;
var myFilter:DropShadowFilter = new DropShadowFilter();
Although not shown here, the DropShadowFilter() constructor (like all the filter classes’
constructors) accepts several optional parameters that can be used to customize the
appearance of the filter effect.
Applying a filter
Once you've constructed a filter object, you can apply it to a display object or a BitmapData
object; how you apply the filter depends on the object to which you’re applying it.
468 Filtering display objects
Applying a filter to a display object
When you apply filter effects to a display object, you apply them through the filters
property. The filters property of a display object is an Array instance, whose elements are
the filter objects applied to the display object. To apply a single filter to a display object, create
the filter instance, add it to an Array instance, and assign that Array object to the display
object’s filters property:
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.filters.DropShadowFilter;
// Create a bitmapData object and render it to screen
var myBitmapData:BitmapData = new BitmapData(100,100,false,0xFFFF3300);
var myDisplayObject:Bitmap = new Bitmap(myBitmapData);
addChild(myDisplayObject);
// Create a DropShadowFilter instance.
var dropShadow:DropShadowFilter = new DropShadowFilter();
// Create the filters array, adding the filter to the array by passing it as
// a parameter to the Array() constructor.
var filtersArray:Array = new Array(dropShadow);
// Assign the filters array to the display object to apply the filter.
myDisplayObject.filters = filtersArray;
If you want to assign multiple filters to the object, simply add all the filters to the Array
instance before assigning it to the filters property. You can add multiple objects to an Array
by passing them as parameters to its constructor. For example, this code applies a bevel filter
and a glow filter to the previously created display object:
import flash.filters.BevelFilter;
import flash.filters.GlowFilter;
// Create the filters and add them to an array.
var bevel:BevelFilter = new BevelFilter();
var glow:GlowFilter = new GlowFilter();
var filtersArray:Array = new Array(bevel, glow);
// Assign the filters array to the display object to apply the filter.
myDisplayObject.filters = filtersArray;
NOTE
When you’re creating the array containing the filters, you can create it using the new
Array() constructor (as shown in the previous examples) or you can use Array literal
syntax, wrapping the filters in square braces ( [] ). For instance, this line of code:
var filters:Array = new Array(dropShadow, blur);
does the same thing as this line of code:
var filters:Array = [dropShadow, blur];
Creating and applying filters 469
If you apply multiple filters to display objects, they are applied in a cumulative, sequential
manner. For example, if a filters array has two elements, a bevel filter added first and a drop
shadow filter added second, the drop shadow filter is applied to both the bevel filter and the
display object. This is because of the drop shadow filter’s second position in the filters array. If
you want to apply filters in a noncumulative manner, you must apply each filter to a new copy
of the display object.
If you’re only assigning one or a few filters to a display object, you can create the filter instance
and assign it to the object in a single statement. For instance, the following line of code
applies a blur filter to a display object called myDisplayObject:
myDisplayObject.filters = [new BlurFilter()];
The previous code creates an Array instance using Array literal syntax (square braces), creates a
new BlurFilter instance as an element in the Array, and assigns that Array to the filters
property of the display object named myDisplayObject.
Removing filters from a display object
Removing all filters from a display object is as simple as assigning a null value to the filters
property:
myDisplayObject.filters = null;
If you’ve applied multiple filters to an object and want to remove only one of the filters, you
must go through several steps to change the filters property array. For more information,
see “Changing filters at run time” on page 470.
Applying a filter to a BitmapData object
Applying a filter to a BitmapData object requires the use of the BitmapData object’s
applyFilter() method:
myBitmapData.applyFilter(sourceBitmapData);
The applyFilter() method applies a filter to a source BitmapData object, producing a new,
filtered image. This method does not modify the original source image; instead, the result of
the filter being applied to the source image is stored in the BitmapData instance on which the
applyFilter() method is called.
470 Filtering display objects
How filters work
Display object filtering works by caching a copy of the original object as a transparent bitmap.
Once a filter has been applied to an display object, Adobe Flash Player caches the object as a
bitmap for as long as the object has a valid filter list. This source bitmap is then used as the
original image for all subsequently applied filter effects.
Each display object usually contains two bitmaps: one with the original unfiltered source
display object and another for the final image after filtering. The final image is used when
rendering. As long as the display object does not change, the final image does not need
updating.
Potential issues for working with filters
There are several potential sources of confusion or trouble to keep in mind when you’re
working with filters. These are described in the following sections.
Filters and bitmap caching
To apply a filter to a display object, bitmap caching must be enabled for that object. When
you apply a filter to a display object whose cacheAsBitmap property is set to false, Flash
Player automatically sets the value of the object’s cacheAsBitmap property to true. If you
later remove all the filters from the display object, Flash Player resets the cacheAsBitmap
property to the last value it was set to.
Changing filters at run time
If a display object already has one or more filters applied to it, you can’t add additional filters
to the filters property array. Instead, to add or change the set of filters being applied, you
create a copy of the entire filters array and make your modifications to this (temporary) array.
You then reassign this array back to the filters property of the display object in order for the
filters to be applied to the object. The following code demonstrates this process. Initially, a
glow filter is applied to the display object named myDisplayObject; later, when the display
object is clicked, the function addFilters() is called. In this function, two additional filters
are applied to myDisplayObject:
import flash.events.MouseEvent;
import flash.filters.*;
myDisplayObject.filters = [new GlowFilter()];
function addFilters(event:MouseEvent):void
Creating and applying filters 471
{
// Make a copy of the filters array.
var filtersCopy:Array = myDisplayObject.filters;
// Make desired changes to the filters (in this case, adding filters).
filtersCopy.push(new BlurFilter());
filtersCopy.push(new DropShadowFilter());
// Apply the changes by re-assigning the array to the filters property.
myDisplayObject.filters = filtersCopy;
}
myDisplayObject.addEventListener(MouseEvent.CLICK, addFilters);
Filters and object transformations
No filtered region—a drop shadow, for example—outside of a display object’s bounding box
rectangle is considered to be part of the surface for the purposes of hit detection (determining
if an instance overlaps or intersects with another instance). Because the DisplayObject class’s
hit detection methods are vector-based, you cannot perform a hit detection on the bitmap
result. For example, if you apply a bevel filter to a button instance, hit detection is not
available on the beveled portion of the instance.
Scaling, rotating, and skewing are not supported by filters; if the filtered display object itself is
scaled (if scaleX and scaleY are not 100%), the filter effect does not scale with the instance.
This means that the original shape of the instance rotates, scales, or skews; however, the filter
does not rotate, scale, or skew with the instance.
You can animate an instance with a filter to create realistic effects, or nest instances and use
the BitmapData class to animate filters to achieve this effect.
Filters and Bitmap objects
When you apply any filter to a BitmapData object, the cacheAsBitmap property is
automatically set to true. In this way, the filter is actually applied to the copy of the object
rather than the original.
This copy is then placed on the main display (over the original object) as close as possible to
the nearest pixel. If the bounds of the original bitmap change, the filtered copy bitmap is
recreated from scratch, rather than being stretched or distorted.
If you clear all filters for a display object, the cacheAsBitmap property is reset to what it was
before the filter was applied.
472 Filtering display objects
Available display filters
ActionScript 3.0 includes nine filter classes that you can apply to display objects and
BitmapData objects:
■ Bevel filter (BevelFilter class)
■ Blur filter (BlurFilter class)
■ Drop shadow filter (DropShadowFilter class)
■ Glow filter (GlowFilter class)
■ Gradient bevel filter (GradientBevelFilter class)
■ Gradient glow filter (GradientGlowFilter class)
■ Color matrix filter (ColorMatrixFilter class)
■ Convolution filter (ConvolutionFilter class)
■ Displacement map filter (DisplacementMapFilter class)
The first six filters are simple filters that can be used to create one specific effect, with some
customization of the effect available. Those six filters can be applied using ActionScript, and
can also be applied to objects in Adobe Flash CS3 Professional using the Filters panel.
Consequently, even if you’re applying filters using ActionScript, if you have the Flash
authoring tool you can use the visual interface to quickly try out different filters and settings
to figure out how to create a desired effect.
The final three filters are available in ActionScript only. Those filters, the color matrix filter,
convolution filter, and displacement map filter, are much more flexible in the types of effects
that they can be used to create; rather than being optimized for a single effect, they provide
power and flexibility. For example, by selecting different values for its matrix, the convolution
filter can be used to create effects such as blurring, embossing, sharpening, finding color
edges, transformations, and more.
Each of the filters, whether simple or complex, can be customized using their properties.
Generally, you have two choices for setting filter properties. All the filters let you set the
properties by passing parameter values to the filter object’s constructor. Alternatively, whether
or not you set the filter properties by passing parameters, you can adjust the filters later by
setting values for the filter object’s properties. Most of the example code listings set the
properties directly, in order to make the example easier to follow. Nevertheless, you could
usually achieve the same result in fewer lines of code by passing the values as parameters in the
filter object’s constructor. For more details on the specifics of each filter, its properties and its
constructor parameters, see the listings for the flash.filters package in the ActionScript 3.0
Language and Components Reference.
Available display filters 473
Bevel filter
The BevelFilter class allows you to add a 3D beveled edge to the filtered object. This filter
makes the hard corners or edges of your object look like they have been chiseled, or beveled,
away.
The BevelFilter class properties allow you to customize the appearance of the bevel. You can
set highlight and shadow colors, bevel edge blurs, bevel angles, and bevel edge placement; you
can even create a knockout effect.
The following example loads an external image and applies a bevel filter to it.
import flash.display.*;
import flash.filters.BevelFilter;
import flash.filters.BitmapFilterQuality;
import flash.filters.BitmapFilterType;
import flash.net.URLRequest;
// Load an image onto the Stage.
var imageLoader:Loader = new Loader();
var url:String = "http://www.helpexamples.com/flash/images/image3.jpg";
var urlReq:URLRequest = new URLRequest(url);
imageLoader.load(urlReq);
addChild(imageLoader);
// Create the bevel filter and set filter properties.
var bevel:BevelFilter = new BevelFilter();
bevel.distance = 5;
bevel.angle = 45;
bevel.highlightColor = 0xFFFF00;
bevel.highlightAlpha = 0.8;
bevel.shadowColor = 0x666666;
bevel.shadowAlpha = 0.8;
bevel.blurX = 5;
bevel.blurY = 5;
bevel.strength = 5;
bevel.quality = BitmapFilterQuality.HIGH;
bevel.type = BitmapFilterType.INNER;
bevel.knockout = false;
// Apply filter to the image.
imageLoader.filters = [bevel];
474 Filtering display objects
Blur filter
The BlurFilter class smears, or blurs, a display object and its contents. Blur effects are useful
for giving the impression that an object is out of focus or for simulating fast movement, as in
a motion blur. By setting the quality property of the blur filter to low, you can simulate a
softly out-of-focus lens effect. Setting the quality property to high results in a smooth blur
effect similar to a Gaussian blur.
The following example creates a circle object using the drawCircle() method of the
Graphics class and applies a blur filter to it:
import flash.display.Sprite;
import flash.filters.BitmapFilterQuality;
import flash.filters.BlurFilter;
// Draw a circle.
var redDotCutout:Sprite = new Sprite();
redDotCutout.graphics.lineStyle();
redDotCutout.graphics.beginFill(0xFF0000);
redDotCutout.graphics.drawCircle(145, 90, 25);
redDotCutout.graphics.endFill();
// Add the circle to the display list.
addChild(redDotCutout);
// Apply the blur filter to the rectangle.
var blur:BlurFilter = new BlurFilter();
blur.blurX = 10;
blur.blurY = 10;
blur.quality = BitmapFilterQuality.MEDIUM;
redDotCutout.filters = [blur];
Drop shadow filter
Drop shadows give the impression that there is a separate light source situated above a target
object. The position and intensity of this light source can be modified to produce a variety of
different drop shadow effects.
The drop shadow filter uses an algorithm that is similar to the blur filter’s algorithm. The
main difference is that the drop shadow filter has a few more properties that you can modify
to simulate different light-source attributes (such as alpha, color, offset and brightness).
The drop shadow filter also allows you to apply custom transformation options on the style of
the drop shadow, including inner or outer shadow and knockout (also known as cutout)
mode.
Available display filters 475
The following code creates a square box sprite and applies a drop shadow filter to it:
import flash.display.Sprite;
import flash.filters.DropShadowFilter;
// Draw a box.
var boxShadow:Sprite = new Sprite();
boxShadow.graphics.lineStyle(1);
boxShadow.graphics.beginFill(0xFF3300);
boxShadow.graphics.drawRect(0, 0, 100, 100);
boxShadow.graphics.endFill();
addChild(boxShadow);
// Apply the drop shadow filter to the box.
var shadow:DropShadowFilter = new DropShadowFilter();
shadow.distance = 10;
shadow.angle = 25;
// You can also set other properties, such as the shadow color,
// alpha, amount of blur, strength, quality, and options for
// inner shadows and knockout effects.
boxShadow.filters = [shadow];
Glow filter
The GlowFilter class applies a lighting effect to display objects, making it appear that a light is
being shined up from underneath the object to create a soft glow.
Similar to the drop shadow filter, the glow filter includes properties to modify the distance,
angle, and color of the light source to produce varying effects. The GlowFilter also has several
options for modifying the style of the glow, including inner or outer glow and knockout
mode.
The following code creates a cross using the Sprite class and applies a glow filter to it:
import flash.display.Sprite;
import flash.filters.BitmapFilterQuality;
import flash.filters.GlowFilter;
// Create a cross graphic.
var crossGraphic:Sprite = new Sprite();
crossGraphic.graphics.lineStyle();
crossGraphic.graphics.beginFill(0xCCCC00);
crossGraphic.graphics.drawRect(60, 90, 100, 20);
crossGraphic.graphics.drawRect(100, 50, 20, 100);
crossGraphic.graphics.endFill();
addChild(crossGraphic);
476 Filtering display objects
// Apply the glow filter to the cross shape.
var glow:GlowFilter = new GlowFilter();
glow.color = 0x009922;
glow.alpha = 1;
glow.blurX = 25;
glow.blurY = 25;
glow.quality = BitmapFilterQuality.MEDIUM;
crossGraphic.filters = [glow];
Gradient bevel filter
The GradientBevelFilter class lets you apply an enhanced bevel effect to display objects or
BitmapData objects. Using a gradient color on the bevel greatly improves the spatial depth of
the bevel, giving edges a more realistic, 3D appearance.
The following code creates a rectangle object using the drawRect() method of the Shape class
and applies a gradient bevel filter to it.
import flash.display.Shape;
import flash.filters.BitmapFilterQuality;
import flash.filters.GradientBevelFilter;
// Draw a rectangle.
var box:Shape = new Shape();
box.graphics.lineStyle();
box.graphics.beginFill(0xFEFE78);
box.graphics.drawRect(100, 50, 90, 200);
box.graphics.endFill();
// Apply a gradient bevel to the rectangle.
var gradientBevel:GradientBevelFilter = new GradientBevelFilter();
gradientBevel.distance = 8;
gradientBevel.angle = 225; // opposite of 45 degrees
gradientBevel.colors = [0xFFFFCC, 0xFEFE78, 0x8F8E01];
gradientBevel.alphas = [1, 0, 1];
gradientBevel.ratios = [0, 128, 255];
gradientBevel.blurX = 8;
gradientBevel.blurY = 8;
gradientBevel.quality = BitmapFilterQuality.HIGH;
// Other properties let you set the filter strength and set options
// for inner bevel and knockout effects.
box.filters = [gradientBevel];
// Add the graphic to the display list.
addChild(box);
Available display filters 477
Gradient glow filter
The GradientGlowFilter class lets you apply an enhanced glow effect to display objects or
BitmapData objects. The effect gives you greater color control of the glow, and in turn
produces a more realistic glow effect. Additionally, the gradient glow filter allows you to apply
a gradient glow to the inner, outer, or upper edges of an object.
The following example draws a circle on the Stage, and applies a gradient glow filter to it. As
you move the mouse further to the right and down, the amount of blur increases in the
horizontal and vertical directions respectively. In addition, any time you click on the Stage,
the strength of the blur increases.
import flash.events.MouseEvent;
import flash.filters.BitmapFilterQuality;
import flash.filters.BitmapFilterType;
import flash.filters.GradientGlowFilter;
// Create a new Shape instance.
var shape:Shape = new Shape();
// Draw the shape.
shape.graphics.beginFill(0xFF0000, 100);
shape.graphics.moveTo(0, 0);
shape.graphics.lineTo(100, 0);
shape.graphics.lineTo(100, 100);
shape.graphics.lineTo(0, 100);
shape.graphics.lineTo(0, 0);
shape.graphics.endFill();
// Position the shape on the Stage.
addChild(shape);
shape.x = 100;
shape.y = 100;
// Define a gradient glow.
var gradientGlow:GradientGlowFilter = new GradientGlowFilter();
gradientGlow.distance = 0;
gradientGlow.angle = 45;
gradientGlow.colors = [0x000000, 0xFF0000];
gradientGlow.alphas = [0, 1];
gradientGlow.ratios = [0, 255];
gradientGlow.blurX = 10;
gradientGlow.blurY = 10;
gradientGlow.strength = 2;
gradientGlow.quality = BitmapFilterQuality.HIGH;
gradientGlow.type = BitmapFilterType.OUTER;
478 Filtering display objects
// Define functions to listen for two events.
function onClick(event:MouseEvent):void
{
gradientGlow.strength++;
shape.filters = [gradientGlow];
}
function onMouseMove(event:MouseEvent):void
{
gradientGlow.blurX = (stage.mouseX / stage.stageWidth) * 255;
gradientGlow.blurY = (stage.mouseY / stage.stageHeight) * 255;
shape.filters = [gradientGlow];
}
stage.addEventListener(MouseEvent.CLICK, onClick);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
Example: Combining basic filters
The following code example uses several basic filters, combined with a Timer for creating
repeating actions, to create an animated traffic light simulation.
import flash.display.Shape;
import flash.events.TimerEvent;
import flash.filters.BitmapFilterQuality;
import flash.filters.BitmapFilterType;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.filters.GradientBevelFilter;
import flash.utils.Timer;
var count:Number = 1;
var distance:Number = 8;
var angleInDegrees:Number = 225; // opposite of 45 degrees
var colors:Array = [0xFFFFCC, 0xFEFE78, 0x8F8E01];
var alphas:Array = [1, 0, 1];
var ratios:Array = [0, 128, 255];
var blurX:Number = 8;
var blurY:Number = 8;
var strength:Number = 1;
var quality:Number = BitmapFilterQuality.HIGH;
var type:String = BitmapFilterType.INNER;
var knockout:Boolean = false;
// Draw the rectangle background for the traffic light.
var box:Shape = new Shape();
box.graphics.lineStyle();
box.graphics.beginFill(0xFEFE78);
box.graphics.drawRect(100, 50, 90, 200);
box.graphics.endFill();
Available display filters 479
// Draw the 3 circles for the three lights.
var stopLight:Shape = new Shape();
stopLight.graphics.lineStyle();
stopLight.graphics.beginFill(0xFF0000);
stopLight.graphics.drawCircle(145,90,25);
stopLight.graphics.endFill();
var cautionLight:Shape = new Shape();
cautionLight.graphics.lineStyle();
cautionLight.graphics.beginFill(0xFF9900);
cautionLight.graphics.drawCircle(145,150,25);
cautionLight.graphics.endFill();
var goLight:Shape = new Shape();
goLight.graphics.lineStyle();
goLight.graphics.beginFill(0x00CC00);
goLight.graphics.drawCircle(145,210,25);
goLight.graphics.endFill();
// Add the graphics to the display list.
addChild(box);
addChild(stopLight);
addChild(cautionLight);
addChild(goLight);
// Apply a gradient bevel to the traffic light rectangle.
var gradientBevel:GradientBevelFilter = new GradientBevelFilter(distance,
angleInDegrees, colors, alphas, ratios, blurX, blurY, strength, quality,
type, knockout);
box.filters = [gradientBevel];
// Create the inner shadow (for lights when off) and glow
// (for lights when on).
var innerShadow:DropShadowFilter = new DropShadowFilter(5, 45, 0, 0.5, 3,
3, 1, 1, true, false);
var redGlow:GlowFilter = new GlowFilter(0xFF0000, 1, 30, 30, 1, 1, false,
false);
var yellowGlow:GlowFilter = new GlowFilter(0xFF9900, 1, 30, 30, 1, 1, false,
false);
var greenGlow:GlowFilter = new GlowFilter(0x00CC00, 1, 30, 30, 1, 1, false,
false);
// Set the starting state of the lights (green on, red/yellow off).
stopLight.filters = [innerShadow];
cautionLight.filters = [innerShadow];
goLight.filters = [greenGlow];
// Swap the filters based on the count value.
function trafficControl(event:TimerEvent):void
480 Filtering display objects
{
if (count == 4)
{
count = 1;
}
switch (count)
{
case 1:
stopLight.filters = [innerShadow];
cautionLight.filters = [yellowGlow];
goLight.filters = [innerShadow];
break;
case 2:
stopLight.filters = [redGlow];
cautionLight.filters = [innerShadow];
goLight.filters = [innerShadow];
break;
case 3:
stopLight.filters = [innerShadow];
cautionLight.filters = [innerShadow];
goLight.filters = [greenGlow];
break;
}
count++;
}
// Create a timer to swap the filters at a 3 second interval.
var timer:Timer = new Timer(3000, 9);
timer.addEventListener(TimerEvent.TIMER, trafficControl);
timer.start();
Color matrix filter
The ColorMatrixFilter class is used to manipulate the color and alpha values of the filtered
object. This allows you to create saturation changes, hue rotation (shifting a palette from one
range of colors to another), luminance-to-alpha changes, and other color manipulation effects
using values from one color channel and potentially applying them to other channels.
Available display filters 481
Conceptually, the filter goes through the pixels in the source image one by one and separates
each pixel into its red, green, blue, and alpha components. It then multiplies values provided
in the color matrix by each of these values, adding the results together to determine the
resulting color value that will be displayed on the screen for that pixel. The matrix property
of the filter is an array of 20 numbers that are used in calculating the final color. For details of
the specific algorithm used to calculate the color values, see the entry describing the
ColorMatrixFilter class’s matrix property in the ActionScript 3.0 Language and Components
Reference.
Further information and examples of the color matrix filter can be found in the article “Using
Matrices for Transformations, Color Adjustments, and Convolution Effects in Flash” on the
Adobe Developer Center web site.
Convolution filter
The ConvolutionFilter class can be used to apply a wide range of imaging transformations to
BitmapData objects or display objects, such as blurring, edge detection, sharpening,
embossing, and beveling.
The convolution filter conceptually goes through each pixel in the source image one by one
and determines the final color of that pixel using the value of the pixel and its surrounding
pixels. A matrix, specified as an array of numeric values, indicates to what degree the value of
each particular neighboring pixel affects the final resulting value.
Consider the most commonly used type of matrix, which is a three by three matrix. The
matrix includes nine values:
N N N
N P N
N N N
When Flash Player is applying the convolution filter to a certain pixel, it will look at the color
value of the pixel itself (“P” in the example), as well as the values of the surrounding pixels
(labelled “N” in the example). However, by setting values in the matrix, you specify how
much priority certain pixels have in affecting the resulting image.
For example, the following matrix, applied using a convolution filter, will leave an image
exactly as it was:
0 0 0
0 1 0
0 0 0
The reason the image is unchanged is because the original pixel’s value has a relative strength
of 1 in determining the final pixel color, while the surrounding pixels’ values have relative
strength of 0—meaning their colors don’t affect the final image.
482 Filtering display objects
Similarly, this matrix will cause the pixels of an image to shift one pixel to the left:
0 0 0
0 0 1
0 0 0
Notice that in this case, the pixel itself has no effect on the final value of the pixel displayed in
that location on the final image—only the value of the pixel to the right is used to determine
the pixel’s resulting value.
In ActionScript, you create the matrix as a combination of an Array instance containing the
values and two properties specifying the number of rows and columns in the matrix. The
following example loads an image and, when the image finishes loading, applies a convolution
filter to the image using the matrix in the previous listing:
// Load an image onto the Stage.
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/
images/image1.jpg");
loader.load(url);
this.addChild(loader);
function applyFilter(event:MouseEvent):void
{
// Create the convolution matrix.
var matrix:Array = [ 0, 0, 0,
0, 0, 1,
0, 0, 0 ];
var convolution:ConvolutionFilter = new ConvolutionFilter();
convolution.matrixX = 3;
convolution.matrixY = 3;
convolution.matrix = matrix;
convolution.divisor = 1;
loader.filters = [convolution];
}
loader.addEventListener(MouseEvent.CLICK, applyFilter);
Available display filters 483
Something that isn’t obvious in this code is the effect of using values other than 1 or 0 in the
matrix. For example, the same matrix, with the number 8 instead of 1 in the right-hand
position, performs the same action (shifting the pixels to the left). In addition, it affects the
colors of the image, making them 8 times brighter. This is because the final pixel color values
are calculated by multiplying the matrix values by the original pixel colors, adding the values
together, and dividing by the value of the filter’s divisor property. Notice that in the example
code, the divisor property is set to 1. As a general rule, if you want the brightness of the
colors to stay about the same as in the original image, you should make the divisor equal to
the sum of the matrix values. So with a matrix where the values add up to 8, and a divisor of
1, the resulting image is going to be roughly 8 times brighter than the original image.
Although the effect of this matrix isn’t very noticeable, other matrix values can be used to
create various effects. Here are several standard sets of matrix values for different effects using
a three by three matrix:
■ Basic blur (divisor 5):
0 1 0
1 1 1
0 1 0
■ Sharpening (divisor 1):
0, -1, 0
-1, 5, -1
0, -1, 0
■ Edge detection (divisor 1):
0, -1, 0
-1, 4, -1
0, -1, 0
■ Embossing effect (divisor 1):
-2, -1, 0
-1, 1, 1
0, 1, 2
Notice that with most of these effects, the divisor is 1. This is because the negative matrix
values added to the positive matrix values result in 1 (or 0 in the case of edge detection, but
the divisor property’s value cannot be 0).
484 Filtering display objects
Displacement map filter
The DisplacementMapFilter class uses pixel values from a BitmapData object (known as the
displacement map image) to perform a displacement effect on a new object. The displacement
map image is typically different than the actual display object or BitmapData instance to
which the filter is being applied. A displacement effect involves displacing pixels in the filtered
image—in other words, shifting them away from their original location to some extent. This
filter can be used to create a shifted, warped, or mottled effect.
The location and amount of displacement applied to a given pixel is determined by the color
value of the displacement map image. When working with the filter, in addition to specifying
the map image, you specify the following values to control how the displacement is calculated
from the map image:
■ Map point: The location on the filtered image at which the top-left corner of the
displacement filter will be applied. You can use this if you only want to apply the filter to
part of an image.
■ X component: Which color channel of the map image affects the x position of pixels.
■ Y component: Which color channel of the map image affects the y position of pixels.
■ X scale: A multiplier value that specifies how strong the x axis displacement is.
■ Y scale: A multiplier value that specifies how strong the y axis displacement is.
■ Filter mode: Determines what Flash Player should do in any empty spaces created by
pixels being shifted away. The options, defined as constants in the
DisplacementMapFilterMode class, are to display the original pixels (filter mode IGNORE),
to wrap the pixels around from the other side of the image (filter mode WRAP, which is the
default), to use the nearest shifted pixel (filter mode CLAMP), or to fill in the spaces with a
color (filter mode COLOR).
To get a basic understanding of how the displacement map filter works, consider a basic
example. In the following code, an image is loaded, and when it finishes loading it is centered
on the Stage and a displacement map filter is applied to it, causing the pixels in the entire
image to shift horizontally to the left.
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.MouseEvent;
import flash.filters.DisplacementMapFilter;
import flash.geom.Point;
import flash.net.URLRequest;
Available display filters 485
// Load an image onto the Stage.
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/
images/image3.jpg");
loader.load(url);
this.addChild(loader);
var mapImage:BitmapData;
var displacementMap:DisplacementMapFilter;
// This function is called when the image finishes loading.
function setupStage(event:Event):void
{
// Center the loaded image on the Stage.
loader.x = (stage.stageWidth - loader.width) / 2;
loader.y = (stage.stageHeight - loader.height) / 2;
// Create the displacement map image.
mapImage = new BitmapData(loader.width, loader.height, false, 0xFF0000);
// Create the displacement filter.
displacementMap = new DisplacementMapFilter();
displacementMap.mapBitmap = mapImage;
displacementMap.mapPoint = new Point(0, 0);
displacementMap.componentX = BitmapDataChannel.RED;
displacementMap.scaleX = 250;
loader.filters = [displacementMap];
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setupStage);
The properties used to define the displacement are as follows:
■ Map bitmap: The displacement bitmap is a new BitmapData instance created by the code.
Its dimensions match the dimensions of the loaded image (so the displacement is applied
to the entire image). It is filled with solid red pixels.
■ Map point: This value is set to the point 0, 0—again, causing the displacement to be
applied to the entire image.
■ X component: This value is set to the constant BitmapDataChannel.RED, meaning the
red value of the map bitmap will determine how much the pixels are displaced (how much
they move) along the x axis.
■ X scale: This value is set to 250. The full amount of displacement (from the map image
being completely red) only displaces the image by a small amount (roughly one-half of a
pixel), so if this value was set to 1 the image would only shift .5 pixels horizontally. By
setting it to 250, the image shifts by approximately 125 pixels.
486 Filtering display objects
These settings cause the filtered image’s pixels to shift 250 pixels to the left. The direction (left
or right) and amount of shift is based on the color value of the pixels in the map image.
Conceptually, Flash Player goes through the pixels of the filtered image one by one (at least,
the pixels in the region where the filter is applied, which in this case means all the pixels), and
does the following with each pixel:
1. It finds the corresponding pixel in the map image. For example, when Flash Player is
calculating the displacement amount for the pixel in the top-left corner of the filtered
image, it looks at the pixel in the top-left corner of the map image.
2. It determines the value of the specified color channel in the map pixel. In this case, the x
component color channel is the red channel, so Flash Player looks to see what the value of
the red channel of the map image is at the pixel in question. Since the map image is solid
red, the pixel’s red channel is 0xFF, or 255. This is used as the displacement value.
3. It compares the displacement value to the “middle” value (127, which is halfway between
0 and 255). If the displacement value is lower than the middle value, the pixel shifts in a
positive direction (to the right for x displacement; down for y displacement). On the other
hand, if the displacement value is higher than the middle value (as in this example), the
pixel shifts in a negative direction (to the left for x displacement; up for y displacement).
To be more precise, Flash Player subtracts the displacement value from 127, and the result
(positive or negative) is the relative amount of displacement that is applied.
4. Finally, it determines the actual amount of displacement by determining what percentage
of full displacement the relative displacement value represents. In this case, full red means
100% displacement. That percentage is then multiplied by the x scale or y scale value to
determine the number of pixels of displacement that will be applied. In this example, 100%
times a multiplier of 250 determines the amount of displacement—roughly 125 pixels to
the left.
Because no values were specified for y component and y scale, the defaults (which cause no
displacement) were used—that’s why the image doesn’t shift at all in the vertical direction.
The default filter mode setting, WRAP, is used in the example, so as the pixels shift to the left
the empty space on the right is filled in by the pixels that shifted off the left edge of the image.
You can experiment with this value to see the different effects. For instance, if you add the
following line to the portion of code where the displacement properties are being set (before
the line loader.filters = [displacementMap]), it will make the image look as though it
has been smeared across the Stage:
displacementMap.mode = DisplacementMapFilterMode.CLAMP;
Available display filters 487
For a more complex example, the following listing uses a displacement map filter to create a
magnifying glass effect on an image:
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.GradientType;
import flash.display.Loader;
import flash.display.Shape;
import flash.events.MouseEvent;
import flash.filters.DisplacementMapFilter;
import flash.filters.DisplacementMapFilterMode;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.URLRequest;
// Create the gradient circles that will together form the
// displacement map image
var radius:uint = 50;
var type:String = GradientType.LINEAR;
var redColors:Array = [ 0xFF0000, 0x000000 ];
var blueColors:Array = [ 0x0000FF, 0x000000 ];
var alphas:Array = [ 1, 1 ];
var ratios:Array = [ 0, 255 ];
var xMatrix:Matrix = new Matrix();
xMatrix.createGradientBox(radius * 2, radius * 2);
var yMatrix:Matrix = new Matrix();
yMatrix.createGradientBox(radius * 2, radius * 2, Math.PI / 2);
var xCircle:Shape = new Shape();
xCircle.graphics.lineStyle(0, 0, 0);
xCircle.graphics.beginGradientFill(type, redColors, alphas, ratios,
xMatrix);
xCircle.graphics.drawCircle(radius, radius, radius);
var yCircle:Shape = new Shape();
yCircle.graphics.lineStyle(0, 0, 0);
yCircle.graphics.beginGradientFill(type, blueColors, alphas, ratios,
yMatrix);
yCircle.graphics.drawCircle(radius, radius, radius);
// Position the circles at the bottom of the screen, for reference.
this.addChild(xCircle);
xCircle.y = stage.stageHeight - xCircle.height;
this.addChild(yCircle);
yCircle.y = stage.stageHeight - yCircle.height;
yCircle.x = 200;
488 Filtering display objects
// Load an image onto the Stage.
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/
images/image1.jpg");
loader.load(url);
this.addChild(loader);
// Create the map image by combining the two gradient circles.
var map:BitmapData = new BitmapData(xCircle.width, xCircle.height, false,
0x7F7F7F);
map.draw(xCircle);
var yMap:BitmapData = new BitmapData(yCircle.width, yCircle.height, false,
0x7F7F7F);
yMap.draw(yCircle);
map.copyChannel(yMap, yMap.rect, new Point(0, 0), BitmapDataChannel.BLUE,
BitmapDataChannel.BLUE);
yMap.dispose();
// Display the map image on the Stage, for reference.
var mapBitmap:Bitmap = new Bitmap(map);
this.addChild(mapBitmap);
mapBitmap.x = 400;
mapBitmap.y = stage.stageHeight - mapBitmap.height;
// This function creates the displacement map filter at the mouse location.
function magnify():void
{
// Position the filter.
var filterX:Number = (loader.mouseX) - (map.width / 2);
var filterY:Number = (loader.mouseY) - (map.height / 2);
var pt:Point = new Point(filterX, filterY);
var xyFilter:DisplacementMapFilter = new DisplacementMapFilter();
xyFilter.mapBitmap = map;
xyFilter.mapPoint = pt;
// The red in the map image will control x displacement.
xyFilter.componentX = BitmapDataChannel.RED;
// The blue in the map image will control y displacement.
xyFilter.componentY = BitmapDataChannel.BLUE;
xyFilter.scaleX = 35;
xyFilter.scaleY = 35;
xyFilter.mode = DisplacementMapFilterMode.IGNORE;
loader.filters = [xyFilter];
}
// This function is called when the mouse moves. If the mouse is
// over the loaded image, it applies the filter.
function moveMagnifier(event:MouseEvent):void
{
Available display filters 489
if (loader.hitTestPoint(loader.mouseX, loader.mouseY))
{
magnify();
}
}
loader.addEventListener(MouseEvent.MOUSE_MOVE, moveMagnifier);
The code first generates two gradient circles, which are combined together to form the
displacement map image. The red circle creates the x axis displacement
(xyFilter.componentX = BitmapDataChannel.RED), and the blue circle creates the y axis
displacement (xyFilter.componentY = BitmapDataChannel.BLUE). To help you
understand what the displacement map image looks like, the code adds the original circles as
well as the combined circle that serves as the map image to the bottom of the screen.
The code then loads an image and, as the mouse moves, applies the displacement filter to the
portion of the image that’s under the mouse. The gradient circles used as the displacement
map image causes the displaced region to spread out away from the mouse pointer. Notice
that the gray regions of the displacement map image don’t cause any displacement. The gray
color is 0x7F7F7F. The blue and red channels of that shade of gray exactly match the middle
shade of those color channels, so there is no displacement in a gray area of the map image.
Likewise, in the center of the circle there is no displacement. Although the color there isn’t
gray, that color’s blue channel and red channel are identical to the blue channel and red
channel of medium gray, and since blue and red are the colors that cause displacement, no
displacement happens there.
490 Filtering display objects
Example: Filter Workbench
The Filter Workbench provides a simple user interface to apply different filters to an image
and see the resulting code that can be used to generate the same effect by using ActionScript.
For a description of this example and to download the source code, see www.adobe.com/go/
learn_fl_filters.
491
16
CHAPTER 16
Working with movie clips
The MovieClip class is the core class for animation and movie clip symbols created in Adobe
Flash CS3 Professional. It has all the behaviors and functionality of display objects, but with
additional properties and methods for controlling a movie clip’s timeline. This chapter
explains how to use ActionScript to control movie clip playback and to create a movie clip
dynamically.
Contents
Basics of movie clips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
Controlling movie clip playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
Creating MovieClip objects with ActionScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
Loading an external SWF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
Example: RuntimeAssetsExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
Basics of movie clips
Introduction to working with movie clips
Movie clips are a key element for people who create animated content with the Flash
authoring tool and want to control that content with ActionScript. Whenever you create a
movie clip symbol in Flash, Flash adds the symbol to the library of that Flash document. By
default, this symbol becomes an instance of the MovieClip class, and as such has the
properties and methods of the MovieClip class.
When an instance of a movie clip symbol is placed on the Stage, the movie clip automatically
progresses through its timeline (if it has more than one frame) unless its playback is altered
using ActionScript. It is this timeline that distinguishes the MovieClip class, allowing you to
create animation through motion or shape tweens through the Flash authoring tool. By
contrast, with a display object that is an instance of the Sprite class, you can create animation
only by programmatically changing the object’s values.
492 Working with movie clips
In previous versions of ActionScript, the MovieClip class was the base class of all instances on
the Stage. In ActionScript 3.0, a movie clip is only one of many display objects that can
appear on the screen. If a timeline is not necessary for the function of a display object, using
the Shape class or Sprite class in lieu of the MovieClip class may improve rendering
performance. For more information on choosing the appropriate display object for a task, see
“Choosing a DisplayObject subclass” on page 393.
Common movie clip tasks
The following common movie clips tasks are described in this chapter:
■ Making movie clips play and stop
■ Playing movie clips in reverse
■ Moving the playhead to specific points in a movie clip’s timeline
■ Working with frame labels in ActionScript
■ Accessing scene information in ActionScript
■ Creating instances of library movie clip symbols using ActionScript
■ Loading and controlling external SWF files, including files created for previous Flash
Player versions
■ Building an ActionScript system for creating graphical assets to be loaded and used at run
time
Important concepts and terms
The following reference list contains important terms used in this chapter:
■ AVM1 SWF: A SWF file created using ActionScript 1.0 or ActionScript 2.0, usually
targeting Flash Player 8 or earlier.
■ AVM2 SWF: A SWF file created using ActionScript 3.0 for Adobe Flash Player 9.
■ External SWF: A SWF file that is created separately from the project SWF file and is
intended to be loaded into the project SWF file and played back within that SWF file.
■ Frame: The smallest division of time on the timeline. As with a motion picture filmstrip,
each frame is like a snapshot of the animation in time, and when frames are played quickly
in sequence, the effect of animation is created.
Basics of movie clips 493
■ Timeline: The metaphorical representation of the series of frames that make up a movie
clip’s animation sequence. The timeline of a MovieClip object is equivalent to the timeline
in the Flash authoring tool.
■ Playhead: A marker identifying the location (frame) in the timeline that is being displayed
at a given moment.
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because this chapter is about working with movie clips in ActionScript,
essentially all the code listings in this chapter are written with the idea of manipulating a
movie clip symbol that has been created and placed on the Stage. Testing the sample will
involve viewing the result in Flash Player to see the effects of the code on the symbol. To test
the code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Create a movie clip symbol instance on the Stage. For example, draw a shape, select it,
choose Modify > Convert to symbol, and give the symbol a name.
5. With the movie clip selected, in the Property inspector, give it an instance name. The name
should match the name used for the movie clip in the example code listing—for example,
if the code listing manipulates a movie clip named myMovieClip, you should name your
movie clip instance myMovieClip as well.
6. Run the program using Control > Test Movie.
On the screen you will see the results of the code manipulating the movie clip as specified
in the code listing.
Other techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
494 Working with movie clips
Working with MovieClip objects
When you publish a SWF file, Flash converts all movie clip symbol instances on the Stage to
MovieClip objects. You can make a movie clip symbol available to ActionScript by giving it an
instance name in the Instance Name field of the Property inspector. When the SWF file is
created, Flash generates the code that creates the MovieClip instance on the Stage and declares
a variable using the instance name. If you have named movie clips that are nested inside other
named movie clips, those child movie clips are treated like properties of the parent movie
clip—you can access the child movie clip using dot syntax. For example, if a movie clip with
the instance name childClip is nested within another clip with the instance name
parentClip, you can make the child clip’s timeline animation play by calling this code:
parentClip.childClip.play()
While some legacy methods and properties of the ActionScript 2.0 MovieClip class remain
the same, others have changed. All properties prefixed with an underscore have been renamed.
For example, _width and _height properties are now accessed as width and height, while
_xscale and _yscale are now accessed as scaleX and scaleY. For a complete list of the
properties and methods of the MovieClip class, consult the ActionScript 3.0 Language and
Components Reference.
Controlling movie clip playback
Flash uses the metaphor of a timeline to convey animation or a change in state. Any visual
element that employs a timeline must be either a MovieClip object or extend from the
MovieClip class. While ActionScript can direct any movie clip to stop, play, or go to another
point on the timeline, it cannot be used to dynamically create a timeline or add content at
specific frames; this is only possible using the Flash authoring tool.
When a MovieClip is playing, it progresses along its timeline at a speed dictated by the frame
rate of the SWF file. Alternatively, you can override this setting by setting the
Stage.frameRate property in ActionScript.
Controlling movie clip playback 495
Playing movie clips and stopping playback
The play() and stop() methods allow basic control of a movie clip across its timeline. For
example, suppose you have a movie clip symbol on the Stage which contains an animation of
a bicycle moving across the screen, with its instance name set to bicycle. If the following
code is attached to a keyframe on the main timeline,
bicycle.stop();
the bicycle will not move (its animation will not play). The bicycle’s movement could start
through some other user interaction. For example, if you had a button named startButton,
the following code on a keyframe on the main timeline would make it so that clicking the
button causes the animation to play:
// This function will be called when the button is clicked. It causes the
// bicycle animation to play.
function playAnimation(event:MouseEvent):void
{
bicycle.play();
}
// Register the function as a listener with the button.
startButton.addEventListener(MouseEvent.CLICK, playAnimation);
Fast-forwarding and rewinding
The play() and stop() methods are not the only way of controlling playback in a movie
clip. You can also move the playhead forward or backward along the timeline manually by
using the nextFrame() and prevFrame() methods. Calling either of these methods stops
playback and moves the playhead one frame forward or backward, respectively.
Using the play() method is analogous to calling nextFrame() every time the movie clip
object’s enterFrame event is triggered. Along these lines, you could make the bicycle movie
clip play backwards by creating an event listener for the enterFrame event and telling
bicycle to go to its previous frame in the listener function, as follows:
// This function is called when the enterFrame event is triggered, meaning
// it’s called once per frame.
function everyFrame(event:Event):void
{
if (bicycle.currentFrame == 1)
{
bicycle.gotoAndStop(bicycle.totalFrames);
}
else
496 Working with movie clips
{
bicycle.prevFrame();
}
}
bicycle.addEventListener(Event.ENTER_FRAME, everyFrame);
In normal playback, if a movie clip contains more than a single frame, it will loop indefinitely
when playing; that is, it will return to Frame 1 if it progresses past its final frame. When you
use prevFrame() or nextFrame(), this behavior does not happen automatically (calling
prevFrame() when the playhead is on Frame 1 doesn’t move the playhead to the last frame).
The if condition in the example above checks to see if the playhead has progressed backwards
to the first frame, and sets the playhead ahead to its final frame, effectively creating a
continuous loop of the movie clip playing backwards.
Jumping to a different frame and using frame labels
Sending a movie clip to a new frame is a simple affair. Calling either gotoAndPlay() or
gotoAndStop() will jump the movie clip to the frame number specified as a parameter.
Alternatively, you can pass a string that matches the name of a frame label. Any frame on the
timeline can be assigned a label. To do this, select a frame on the timeline and then enter a
name in the Frame Label field on the Property inspector.
The advantages of using frame labels instead of numbers are particularly evident when
creating a complex movie clip. When the number of frames, layers, and tweens in an
animation becomes large, consider labeling important frames with explanatory descriptions
that represent shifts in the behavior of the movie clip (for example, “off,” “walking,” or
“running”). This improves code readability and also provides flexibility, since ActionScript
calls that go to a labeled frame are pointers to a single reference—the label—rather than a
specific frame number. If later on you decide to move a particular segment of the animation to
a different frame, you will not need to change your ActionScript code as long as you keep the
same label for the frames in the new location.
To represent frame labels in code, ActionScript 3.0 includes the FrameLabel class. Each
instance of this class represents a single frame label, and has a name property representing the
name of the frame label as specified in the Property inspector, and a frame property
representing the frame number of the frame where the label is placed on the timeline.
In order to get access to the FrameLabel instances associated with a movie clip instance, the
MovieClip class includes two properties that directly return FrameLabel objects. The
currentLabels property returns an array that consists of all FrameLabel objects across the
entire timeline of a movie clip. The currentLabel property returns a single FrameLabel
object representing the frame label encountered most recently along the timeline.
Controlling movie clip playback 497
Suppose you were creating a movie clip named robot and had labeled the various states of its
animation. You could set up a condition that checks the currentLabel property to access the
current state of robot, as in the following code:
if (robot.currentLabel.name == "walking"
{
// do something
}
Working with scenes
In the Flash authoring environment, you can use scenes to demarcate a series of timelines that
a SWF file will progress through. Using the second parameter of the gotoAndPlay() or
gotoAndStop() methods, you can specify a scene to send the playhead to. All FLA files start
with only the initial scene, but you can create new scenes.
Using scenes is not always the best approach because scenes have a number of drawbacks. A
Flash document that contains multiple scenes can be difficult to maintain, particularly in
multiauthor environments. Multiple scenes can also be inefficient in bandwidth, because the
publishing process merges all scenes into a single timeline. This causes a progressive download
of all scenes, even if they are never played. For these reasons, use of multiple scenes is often
discouraged except for organizing lengthy multiple timeline-based animations.
The scenes property of the MovieClip class returns an array of Scene objects representing all
the scenes in the SWF file. The currentScene property returns a Scene object that represents
the scene that is currently playing.
The Scene class has several properties that give information about a scene. The labels
property returns an array of FrameLabel objects representing the frame labels in that scene.
The name property returns the scene’s name as a string. The numFrames property returns an
int representing the total number of frames in the scene.
498 Working with movie clips
Creating MovieClip objects with
ActionScript
One way of adding content to the screen in Flash is by dragging assets from the library onto
the Stage, but that is not the only workflow. For complex projects, experienced developers
commonly prefer to create movie clips programatically. This approach brings several
advantages: easier re-use of code, faster compile-time speed, and more sophisticated
modifications that are available only to ActionScript.
The display list API of ActionScript 3.0 streamlines the process of dynamically creating
MovieClip objects. The ability to instantiate a MovieClip instance directly, separate from the
process of adding it to the display list, provides flexibility and simplicity without sacrificing
control.
In ActionScript 3.0, when you create a movie clip (or any other display object) instance
programatically, it is not visible on the screen until it is added to the display list by calling the
addChild() or the addChildAt() method on a display object container. This allows you to
create a movie clip, set its properties, and even call methods before it is rendered to the screen.
For more information on working with the display list, see “Working with display object
containers” on page 383.
Exporting library symbols for ActionScript
By default, instances of movie clip symbols in a Flash document’s library cannot be
dynamically created (that is, created using only ActionScript). This is because each symbol
that is exported for use in ActionScript adds to the size of your SWF file, and it’s recognized
that some symbols might not be intended for use on the stage. For this reason, in order to
make a symbol available in ActionScript, you must specify that the symbol should be exported
for ActionScript.
To export a symbol for ActionScript:
1. Select the symbol in the Library panel and open its Symbol Properties dialog box.
2. If necessary, activate the Advanced settings.
3. In the Linkage section, activate the Export for ActionScript checkbox.
This will activate the Class and Base Class fields.
Creating MovieClip objects with ActionScript 499
By default, the Class field is populated with the symbol name, with spaces removed (for
example, a symbol named “Tree House” would become “TreeHouse”). To specify that the
symbol should use a custom class for its behavior, enter the full name of the class including
its package in this field. If you want to be able to create instances of the symbol in
ActionScript, but don’t need to add any additional behavior, you can leave the class name
as-is.
The Base Class field’s value defaults to flash.display.MovieClip. If you want your
symbol to extend the functionality of another customer class, you can specify that class’s
name instead, as long as that class extends the Sprite (or MovieClip) class.
4. Press the OK button to save the changes.
At this point, if Flash can’t find an external ActionScript file with a definition for the
specified class (for instance, if you didn’t need to add additional behavior for the symbol),
a warning is displayed:
A definition for this class could not be found in the classpath, so one will be automatically
generated in the SWF file upon export.
You can disregard this warning if your library symbol does not require unique
functionality beyond the functionality of the MovieClip class.
If you do not provide a class for your symbol, Flash will create a class for your symbol
equivalent to this one:
package
{
import flash.display.MovieClip;
public class ExampleMovieClip extends MovieClip
{
public function ExampleMovieClip()
{
}
}
}
If you do want to add extra ActionScript functionality to your symbol, add the appropriate
properties and methods to this structure. For example, suppose you have a movie clip symbol
containing a circle of 50 pixels width and 50 pixels height, and the symbol is specified to be
exported for ActionScript with a class named Circle. The following code, when placed in a
Circle.as file, extends the MovieClip class and provides the symbol with the additional
methods getArea() and getCircumference():
package
{
import flash.display.MovieClip;
500 Working with movie clips
public class Circle extends MovieClip
{
public function Circle()
{
}
public function getArea():Number
{
// The formula is Pi times the radius squared.
return Math.PI * Math.pow((width / 2), 2);
}
public function getCircumference():Number
{
// The formula is Pi times the diameter.
return Math.PI * width;
}
}
}
The following code, placed on a keyframe on Frame 1 of the Flash document, will create an
instance of the symbol and display it on the screen:
var c:Circle = new Circle();
addChild(c);
trace(c.width);
trace(c.height);
trace(c.getArea());
trace(c.getCircumference());
This code demonstrates ActionScript-based instantiation as an alternative to dragging
individual assets onto the Stage. It creates a circle that has all of the properties of a movie clip
and also has the custom methods defined in the Circle class. This is a very basic example—
your library symbol can specify any number of properties and methods in its class.
ActionScript-based instantiation is powerful, because it allows you to dynamically create large
quantities of instances that would be tedious to arrange manually. It is also flexible, because
you can customize each instance’s properties as it is created. You can get a sense of both of
these benefits by using a loop to dynamically create several Circle instances. With the Circle
symbol and class described previously in your Flash document’s library, place the following
code on a keyframe on Frame 1:
import flash.geom.ColorTransform;
var totalCircles:uint = 10;
var i:uint;
for (i = 0; i < totalCircles; i++)
{
Loading an external SWF file 501
// Create a new Circle instance.
var c:Circle = new Circle();
// Place the new Circle at an x coordinate that will space the circles
// evenly across the Stage.
c.x = (stage.stageWidth / totalCircles) * i;
// Place the Circle instance at the vertical center of the Stage.
c.y = stage.stageHeight / 2;
// Change the Circle instance to a random color
c.transform.colorTransform = getRandomColor();
// Add the Circle instance to the current timeline.
addChild(c);
}
function getRandomColor():ColorTransform
{
// Generate random values for the red, green, and blue color channels.
var red:Number = (Math.random() * 512) - 255;
var green:Number = (Math.random() * 512) - 255;
var blue:Number = (Math.random() * 512) - 255;
// Create and return a ColorTransform object with the random colors.
return new ColorTransform(1, 1, 1, 1, red, green, blue, 0);
}
This demonstrates how you can create and customize multiple instances of a symbol quickly
using code. Each instance is positioned based on the current count within the loop, and each
instance is given a random color by setting its transform property (which Circle inherits by
extending the MovieClip class).
Loading an external SWF file
In ActionScript 3.0, SWF files are loaded using the Loader class. To load an external SWF file,
your ActionScript needs to do four things:
1. Create a new URLRequest object with the url of the file.
2. Create a new Loader object.
3. Call the Loader object’s load() method, passing the URLRequest instance as a parameter.
4. Call the addChild() method on a display object container (such as the main timeline of a
Flash document) to add the Loader instance to the display list.
Ultimately, the code looks like this:
var request:URLRequest = new URLRequest(“http://www.[yourdomain].com/
externalSwf.swf”);
var loader:Loader = new Loader()
loader.load(request);
addChild(loader);
502 Working with movie clips
This same code can be used to load an external image file such as a JPEG, GIF, or PNG
image, by specifying the image file’s url rather than a SWF file’s url. A SWF file, unlike an
image file, may contain ActionScript. Thus, although the process of loading a SWF file may
be identical to loading an image, when loading an external SWF file both the SWF file doing
the loading and the SWF file being loaded must reside in the same security sandbox if you
plan to use ActionScript to communicate in any way to the external SWF file. Additionally, if
the external SWF file contains classes that share the same namespace as classes in the loading
SWF file, you may need to create a new application domain for the loaded SWF file in order
to avoid namespace conflicts. For more information on security and application domain
considerations, see “Using the ApplicationDomain class” on page 700 and “Loading SWF
files and images” on page 776.
When the external SWF file is successfully loaded, it can be accessed through the
Loader.content property. If the external SWF file is published for ActionScript 3.0, this will
be either a movie clip or a sprite, depending on which class it extends.
Considerations for loading an older SWF file
If the external SWF file has been published with an older version of ActionScript, there are
important limitations to consider. Unlike an ActionScript 3.0 SWF file that runs in AVM2
(ActionScript Virtual Machine 2), a SWF file published for ActionScript 1.0 or 2.0 runs in
AVM1 (ActionScript Virtual Machine 1).
When an AVM1 SWF file is successfully loaded, the loaded object (the Loader.content
property) will be an AVM1Movie object. An AVM1Movie instance is not the same as a
MovieClip instance. It is a display object, but unlike a movie clip, it does not include
timeline-related methods or properties. The parent AVM2 SWF file will not have access to
the properties, methods, or objects of the loaded AVM1Movie object.
There are additional restrictions on an AVM1 SWF file loaded by an AVM2 SWF file. For
details, see the AVM1Movie class listing in the ActionScript 3.0 Language and Components
Reference.
Example: RuntimeAssetsExplorer 503
Example: RuntimeAssetsExplorer
The Export for ActionScript functionality can be especially advantageous for libraries that
may be useful across more than one project. Symbols that have been exported to ActionScript
are available not only to that SWF file alone but to any SWF file within the same security
sandbox that loads it. In this way, a single Flash document can generate a SWF file that is
designated for the sole purpose of holding graphical assets. This technique is particularly
useful for larger projects where designers working on visual assets can work in parallel with
developers who create a “wrapper” SWF file that then loads the graphical assets SWF file at
run time. You can use this method to maintain a series of versioned files where graphical assets
are not dependent upon the progress of programming development.
The RuntimeAssetsExplorer application loads any SWF file that is a subclass of RuntimeAsset
and allows you to browse the available assets of that SWF file. The example illustrates the
following:
■ Loading an external SWF file using Loader.load()
■ Dynamic creation of a library symbol exported for ActionScript
■ ActionScript control of MovieClip playback
Before beginning, note that each of the SWF files must be located in the same security
sandbox. For more information, see “Security sandboxes” on page 770.
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The RuntimeAssetsExplorer application files can be
found in the folder Samples/RuntimeAssetsExplorer. The application consists of the following
files:
File Description
RuntimeAssetsExample.mxml
or
RuntimeAssetsExample.fla
The user interface for the application for Flex
(MXML) or Flash (FLA).
GeometricAssets.as An example class that implements the
RuntimeAsset interface.
GeometricAssets.fla A FLA file linked to the GeometricAssets class
(the document class of the FLA) containing
symbols that are exported for ActionScript.
com/example/programmingas3/
runtimeassetsexplorer/RuntimeLibrary.as
An interface that defines the required methods
expected of all run-time asset SWF files that
will be loaded into the explorer container.
504 Working with movie clips
Establishing a run-time library interface
In order for the explorer to properly interact with a SWF library, the structure of the run-time
asset libraries must be formalized. We will accomplish this by creating an interface, which is
similar to a class in that it’s a blueprint of methods that demarcate an expected structure, but
unlike a class it includes no method bodies. The interface provides a way for both the run-
time library and the explorer to communicate to one another. Each SWF of run-time assets
that is loaded in our browser will implement this interface. For more information about
interfaces and how they can be useful, see “Interfaces” on page 158.
The RuntimeLibrary interface will be very simple—we merely require a function that can
provide the explorer with an array of classpaths for the symbols to be exported and available in
the run-time library. To this end, the interface has a single method: getAssets().
package com.example.programmingas3.runtimeassetexplorer
{
public interface RuntimeLibrary
{
function getAssets():Array;
}
}
Creating the asset library SWF file
By defining the RuntimeLibrary interface, it’s possible to create multiple asset library SWF
files that can be loaded into another SWF file. Making an individual SWF library of assets
involves four tasks:
■ Creating a class for the asset library SWF file
■ Creating classes for individual assets contained in the library
■ Creating the actual graphic assets
■ Associating graphic elements with classes and publishing the library SWF
com/example/programmingas3/
runtimeassetsexplorer/AnimatingBox.as
The class of the library symbol in the shape of a
rotating box.
com/example/programmingas3/
runtimeassetsexplorer/AnimatingStar.as
The class of the library symbol in the shape of a
rotating star.
File Description
Example: RuntimeAssetsExplorer 505
Creating a class to implement the RuntimeLibrary interface
Next, we’ll create the GeometricAssets class that will implement the RuntimeLibrary
interface. This will be the document class of the FLA. The code for this class is very similar to
the RuntimeLibrary interface—the difference between them is that in the class definition the
getAssets() method has a method body.
package
{
import flash.display.Sprite;
import com.example.programmingas3.runtimeassetexplorer.RuntimeLibrary;
public class GeometricAssets extends Sprite implements RuntimeLibrary
{
public function GeometricAssets() {
}
public function getAssets():Array {
return [
"com.example.programmingas3.runtimeassetexplorer.AnimatingBox",
"com.example.programmingas3.runtimeassetexplorer.AnimatingStar" ];
}
}
}
If we were to create a second run-time library, we could create another FLA based upon
another class (for example, AnimationAssets) that provides its own getAssets()
implementation.
Creating classes for each MovieClip asset
For this example, we’ll merely extend the MovieClip class without adding any functionality to
the custom assets. The following code for AnimatingStar is analogous to that of
AnimatingBox:
package com.example.programmingas3.runtimeassetexplorer
{
import flash.display.MovieClip;
public class AnimatingStar extends MovieClip
{
public function AnimatingStar() {
}
}
}
506 Working with movie clips
Publishing the library
We’ll now connect the MovieClip-based assets to the new class by creating a new FLA and
entering GeometricAssets into the Document Class field of the Property inspector. For the
purposes of this example, we’ll create two very basic shapes that use a timeline tween to make
one clockwise rotation over 360 frames. Both the animatingBox and animatingStar
symbols are set to Export for ActionScript and have the Class field set to the respective
classpaths specified in the getAssets() implementation. The default base class of
flash.display.MovieClip remains, as we want to subclass the standard MovieClip
methods.
After setting up your symbol’s export settings, publish the FLA. You now have your first run-
time library. This SWF file could be loaded into another AVM2 SWF file and the
AnimatingBox and AnimatingStar symbols would be available to the new SWF file.
Loading the library into another SWF file
The last functional piece to deal with is the user interface for the asset explorer. In this
example, the path to the run-time library is hard-coded as a variable named ASSETS_PATH.
Alternatively, you could use the FileReference class—for example, to create an interface that
browses for a particular SWF file on your hard drive.
When the run-time library is successfully loaded, Flash Player calls the
runtimeAssetsLoadComplete() method:
private function runtimeAssetsLoadComplete(event:Event):void
{
var rl:* = event.target.content;
var assetList:Array = rl.getAssets();
populateDropdown(assetList);
stage.frameRate = 60;
}
In this method, the variable rl represents the loaded SWF file. The code calls the
getAssets() method of the loaded SWF file, obtaining the list of assets that are available,
and uses them to populate a ComboBox component with a list of available assets by calling
the populateDropDown() method. That method in turn stores the full classpath of each asset.
Clicking the Add button on the user interface triggers the addAsset() method:
private function addAsset():void
{
var className:String = assetNameCbo.selectedItem.data;
var AssetClass:Class = getDefinitionByName(className) as Class;
var mc:MovieClip = new AssetClass();
...
}
Example: RuntimeAssetsExplorer 507
which gets the classpath of whichever asset is currently selected in the ComboBox
(assetNameCbo.selectedItem.data), and uses the getDefinitionByName() function
(from the flash.utils package) to obtain an actual reference to the asset’s class in order to create
a new instance of that asset.
508 Working with movie clips
509
17
CHAPTER 17
Working with text
In ActionScript 3.0, text is usually displayed within a text field, but can occasionally appear as
a property of an item on the display list (for example, as the label on a UI component). This
chapter explains how to work with the script-defined contents of a text field and with user
input, dynamic text from a remote file, or static text defined in Adobe Flash CS3 Professional.
As an ActionScript 3.0 programmer, you can establish specific content for text fields, or
designate the source for the text, and then set the appearance of that text using styles and
formats. You can also respond to user events as the user inputs text or clicks a hyperlink.
Contents
Basics of working with text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510
Displaying text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
Selecting and manipulating text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
Capturing text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
Restricting text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Formatting text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Advanced text rendering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .525
Working with static text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .528
Example: Newspaper-style text formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
510 Working with text
Basics of working with text
Introduction to working with text
To display any text on the screen in Adobe Flash Player, you use an instance of the TextField
class. The TextField class is the basis for other text-based components, like the TextArea
components or the TextInput components, that are provided in the Adobe Flex framework
and in the Flash authoring environment. For more information about using text components
in the Flash authoring environment, please see “About text controls” in Using Flash.
Text field content can be pre-specified in the SWF file, loaded from an external source like a
text file or database, or entered by users interacting with your application. Within a text field,
the text can appear as rendered HTML content, with images embedded in the rendered
HTML. Once you establish an instance of a text field, you can use flash.text package classes,
like the TextFormat class and the StyleSheet class, to control the text’s appearance. The
flash.text package contains nearly all the classes related to creating, managing, and formatting
text in ActionScript.
You can format text by defining the formatting with a TextFormat object and assigning that
object to the text field. If your text field contains HTML text, you can apply a StyleSheet
object to the text field to assign styles to specific pieces of the text field content. The
TextFormat object or StyleSheet object contains properties defining the appearance of the
text, such as color, size, and weight. The TextFormat object assigns the properties to all the
content within a text field or to a range of text. For example, within the same text field, one
sentence can be bold red text and the next sentence can be blue italic text.
For more information on text formats, see “Assigning text formats” on page 521.
For more information on HTML text in text fields, see “Displaying HTML text”
on page 514.
For more information on style sheets, see “Applying cascading style sheets” on page 521.
In addition to the classes in the flash.text package, you can use the flash.events.TextEvent class
to respond to user actions related to text.
Basics of working with text 511
Common tasks for working with text
The following common text-related tasks are described in this chapter:
■ Modifying text field contents
■ Using HTML in text fields
■ Using images in text fields
■ Selecting text and working with user-selected text
■ Capturing text input
■ Restricting text input
■ Applying formatting and CSS styles to text
■ Fine-tuning text display with sharpness, thickness, and anti-aliasing
■ Accessing and working with static text fields from ActionScript
Important concepts and terms
The following reference list contains important terms that you will encounter in this chapter:
■ Cascading style sheets: A standard syntax for specifying styles and formatting for content
that’s structured in XML (or HTML) format.
■ Device font: A font that is installed on the user’s machine.
■ Dynamic text field: A text field whose contents can be changed by ActionScript but not by
user input.
■ Embedded font: A font that has data its character outline data stored in the application’s
SWF file.
■ HTML text: Text content entered into a text field using ActionScript that includes
HTML formatting tags along with actual text content.
■ Input text field: A text field whose contents can be changed either by user input or by
ActionScript.
■ Static text field: A text field created in the Flash authoring tool, whose content cannot
change when the SWF file is running.
■ Text line metrics: Measurements of the size of various parts of the text content in a text
field, such as the baseline of the text, the height of the top of the characters, size of
descenders (the part of some lowercase letters that extends below the baseline), and so on.
512 Working with text
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because this chapter is about working with text fields in ActionScript,
essentially all the code listings in this chapter involve manipulating a TextField object, either
one that has been created and placed on the Stage in the Flash authoring tool, or one that’s
created using ActionScript. Testing a sample involves viewing the result in Flash Player to see
the effects of the code on the text field.
The examples in this chapter fall into two groups. One type of example manipulates a
TextField object without creating the object explicitly. To test these code listings in this
chapter:
1. Create an empty Flash document.
2. Select a keyframe in the timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. Using the Text tool, create a dynamic text field on the Stage.
5. With the text field selected, in the Property inspector, give it an instance name. The name
should match the name used for the text field in the example code listing—for example, if
the code listing manipulates a text field named myTextField, you should name your text
field myTextField as well.
6. Run the program using Control > Test Movie.
On the screen you will see the results of the code manipulating the text field as specified in
the code listing.
The other type of example code listing in this chapter consists of a class definition that is
intended to be used as the document class for the SWF. In those listings, a TextField instance
is created by the example code, so you will not need to create one separately. To test this type
of code listing:
1. Create an empty Flash document and save it to your computer.
2. Create a new ActionScript file and save it in the same directory as the Flash document. The
file’s name should match the name of the class in the code listing. For instance, if the code
listing defines a class named TextFieldTest, use the name TextFieldTest.as to save the
ActionScript file.
3. Copy the code listing into the ActionScript file and save the file.
4. In the Flash document, click a blank part of the Stage or work space to activate the
document Property inspector.
Displaying text 513
5. In the Property inspector, in the Document Class field, enter the name of the ActionScript
class you copied from the text.
6. Run the program using Control > Test Movie.
You will see the results of the example displayed on the screen.
Other techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
Displaying text
Although authoring tools like Adobe Flex Builder and the Flash authoring tool provide several
options for displaying text, including text-related components or text tools, the primary way
to display text programmatically is through a text field.
Types of text
The type of text within a text field is characterized by its source:
■ Dynamic text
Dynamic text includes content that is loaded from an external source, such as a text file,
an XML file, or even a remote web service. For more information, see “Types of text”
on page 513.
■ Input text
Input text is any text entered by a user or dynamic text that a user can edit. You can set up
a style sheet to format input text, or use the flash.text.TextFormat class to assign properties
to the text field for the input content. For more information, see “Capturing text input”
on page 518.
■ Static text
Static text is created through the Flash authoring tool only. You cannot create a static text
instance using ActionScript 3.0. However, you can use ActionScript classes like StaticText
and TextSnapshot to manipulate an existing static text instance. For more information, see
“Working with static text” on page 528.
514 Working with text
Modifying the text field contents
You can define dynamic text by assigning a string to the flash.text.TextField.text
property. You assign a string directly to the property, as follows:
myTextField.text = “Hello World”;
You can also assign the text property a value from a variable defined in your script, as in the
following example:
package
{
import flash.display.Sprite;
import flash.text.*;
public class TextWithImage extends Sprite
{
private var myTextBox:TextField = new TextField();
private var myText:String = "Hello World";
public function TextWithImage()
{
addChild(myTextBox);
myTextBox.text = myText;
}
}
}
Alternatively, you can assign the text property a value from a remote variable. You have three
options for loading text values from remote sources:
■ The flash.net.URLLoader and flash.net.URLRequest classes load variables for the text
from a local or remote location.
■ The FlashVars attribute is embedded in the HTML page hosting the SWF file and can
contain values for text variables.
■ The flash.net.SharedObject class manages persistent storage of values. For more
information, see “Storing local data” on page 668.
Displaying HTML text
The flash.text.TextField class has an htmlText property that you can use to identify your text
string as one containing HTML tags for formatting the content. As in the following example,
you must assign your string value to the htmlText property (not the text property) for Flash
Player to render the text as HTML:
var myText:String = "<p>This is <b>some</b> content to <i>render</i> as
<u>HTML</u> text.</p>";
myTextBox.htmlText = myText;
Displaying text 515
Flash Player supports a subset of HTML tags and entities for the htmlText property. The
flash.text.TextField.htmlText property description in the ActionScript 3.0 Language
and Components Reference provides detailed information about the supported HTML tags and
entities.
Once you designate your content using the htmlText property, you can use style sheets or the
textformat tag to manage the formatting of your content. For more information, see
“Formatting text” on page 520.
Using images in text fields
Another advantage to displaying your content as HTML text is that you can include images in
the text field. You can reference an image, local or remote, using the img tag and have it
appear within the associated text field.
The following example creates a text field named myTextBox and includes a JPG image of an
eye, stored in the same directory as the SWF file, within the displayed text:
package
{
import flash.display.Sprite;
import flash.text.*;
public class TextWithImage extends Sprite
{
private var myTextBox:TextField;
private var myText:String = "<p>This is <b>some</b> content to
<i>test</i> and <i>see</i></p><p><img src='eye.jpg' width='20'
height='20'></p><p>what can be rendered.</p><p>You should see an eye
image and some <u>HTML</u> text.</p>";
public function TextWithImage()
{
myTextBox.width = 200;
myTextBox.height = 200;
myTextBox.multiline = true;
myTextBox.wordWrap = true;
myTextBox.border = true;
addChild(myTextBox);
myTextBox.htmlText = myText;
}
}
}
The img tag supports JPEG, GIF, PNG, and SWF files.
516 Working with text
Scrolling text in a text field
In many cases, your text will be longer than the text field displaying the text. Or you may have
an input field that allows a user to input more text than can be displayed at one time. You can
use the scroll-related properties of the flash.text.TextField class to manage lengthy content,
either vertically or horizontally.
The scroll-related properties include TextField.scrollV, TextField.scrollH and
maxScrollV and maxScrollH. Use these properties to respond to events, like a mouse click or
a keypress.
The following example creates a text field that is a set size and contains more text than the
field can display at one time. As the user clicks on the text field, the text scrolls vertically.
package
{
import flash.display.Sprite;
import flash.text.*;
import flash.events.MouseEvent;
public class TextScrollExample extends Sprite
{
private var myTextBox:TextField = new TextField();
private var myText:String = "Hello world and welcome to the show. It's
really nice to meet you. Take your coat off and stay a while. OK, show is
over. Hope you had fun. You can go home now. Don't forget to tip your
waiter. There are mints in the bowl by the door. Thank you. Please come
again.";
public function TextScrollExample()
{
myTextBox.text = myText;
myTextBox.width = 200;
myTextBox.height = 50;
myTextBox.multiline = true;
myTextBox.wordWrap = true;
myTextBox.background = true;
myTextBox.border = true;
var format:TextFormat = new TextFormat();
format.font = "Verdana";
format.color = 0xFF0000;
format.size = 10;
myTextBox.defaultTextFormat = format;
addChild(myTextBox);
myTextBox.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownScroll);
}
Selecting and manipulating text 517
public function mouseDownScroll(event:MouseEvent):void
{
myTextBox.scrollV++;
}
}
}
Selecting and manipulating text
You can select dynamic or input text. Since the text selection properties and methods of the
TextField class use index positions to set the range of text to manipulate, you can
programmatically select dynamic or input text even if you don’t know the content.
Selecting text
The flash.text.TextField.selectable property is true by default, and you can
programmatically select text using the setSelection() method.
For example, you can set specific text within a text field to be selected when the user clicks on
the text field:
var myTextField:TextField = new TextField();
myTextField.text = "No matter where you click on this text field the TEXT IN
ALL CAPS is selected.";
myTextField.autoSize = TextFieldAutoSize.LEFT;
addChild(myTextField);
addEventListener(MouseEvent.CLICK, selectText);
function selectText(event:MouseEvent):void
{
myTextField.setSelection(49, 65);
}
Similarly, if you want text within a text field to be selected as the text is initially displayed,
create an event handler function that is called as the text field is added to the display list.
NOTE
In the Flash authoring tool, if you choose the selectable option on a static text field, the text
field that is exported and placed on the display list is a regular, dynamic text field.
518 Working with text
Capturing user-selected text
The TextField class’s selectionBeginIndex and selectionEndIndex properties, which are
“read-only” so they can’t be set to programmatically select text, can be used to capture
whatever the user has currently selected. Additionally, input text fields can use the
caretIndex property.
For example, the following code traces the index values of user-selected text:
var myTextField:TextField = new TextField();
myTextField.text = "Please select the TEXT IN ALL CAPS to see the index
values for the first and last letters.";
myTextField.autoSize = TextFieldAutoSize.LEFT;
addChild(myTextField);
addEventListener(MouseEvent.MOUSE_UP, selectText);
function selectText(event:MouseEvent):void
{
trace("First letter index position: " + myTextField.selectionBeginIndex);
trace("Last letter index position: " + myTextField.selectionEndIndex);
}
You can apply a collection of TextFormat object properties to the selection to change the text’s
appearance. For more information about applying a collection of TextFormat properties to
selected text, see “Formatting ranges of text within a text field” on page 524.
Capturing text input
By default, a text field’s type property is set to dynamic. If you set the type property to input
using the TextFieldType class, you can collect user input and save the value for use in other
parts of your application. Input text fields are useful for forms and any application that wants
the user to define a text value for use elsewhere in the program.
For example, the following code creates an input text field called myTextBox. As the user
enters text in the field, the textInput event is triggered. An event handler called
textInputCapture captures the string of text entered and assigns it a variable. Flash Player
displays the new text in another text field, called myOutputBox.
package
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.text.*;
import flash.events.*;
public class CaptureUserInput extends Sprite
{
Capturing text input 519
private var myTextBox:TextField = new TextField();
private var myOutputBox:TextField = new TextField();
private var myText:String = "Type your text here.";
public function CaptureUserInput()
{
captureText();
}
public function captureText():void
{
myTextBox.type = TextFieldType.INPUT;
myTextBox.background = true;
addChild(myTextBox);
myTextBox.text = myText;
myTextBox.addEventListener(TextEvent.TEXT_INPUT, textInputCapture);
}
public function textInputCapture(event:TextEvent):void
{
var str:String = myTextBox.text;
createOutputBox(str);
}
public function createOutputBox(str:String):void
{
myOutputBox.background = true;
myOutputBox.x = 200;
addChild(myOutputBox);
myOutputBox.text = str;
}
}
}
520 Working with text
Restricting text input
Since input text fields are often used for forms or dialog boxes in applications, you may want
to limit the types of characters a user can enter in a text field, or even keep the text hidden —
for example, for a password. The flash.text.TextField class has a displayAsPassword property
and a restrict property that you can set to control user input.
The displayAsPassword property simply hides the text (displaying it as a series of asterisks)
as the user types it. When displayAsPassword is set to true, the Cut and Copy commands
and their corresponding keyboard shortcuts will not function. As the following example
shows, you assign the displayAsPassword property just as you would other properties, such
as background and color:
myTextBox.type = TextFieldType.INPUT;
myTextBox.background = true;
myTextBox.displayAsPassword = true;
addChild(myTextBox);
The restrict property is a little more complicated since you need to specify what characters
the user is allowed to type in an input text field. You can allow specific letters, numbers, or
ranges of letters, numbers, and characters. The following code allows the user to enter only
uppercase letters (and not numbers or special characters) in the text field:
myTextBox.restrict = “A-Z”;
ActionScript 3.0 uses hyphens to define ranges, and carets to define excluded characters. For
more information about defining what is restricted in an input text field, see the
flash.text.TextField.restrict property entry in the ActionScript 3.0 Language and
Components Reference.
Formatting text
You have several options for programmatically formatting the display of text. You can set
properties directly on the TextField instance—for example, the TextFIeld.thickness,
TextField.textColor, and TextField.textHeight properties. Or you can designate the
content of the text field using the htmlText property and use the supported HTML tags,
such as b, i, and u. But you can also apply TextFormat objects to text fields containing plain
text, or StyleSheet objects to text fields containing the htmlText property. Using TextFormat
and StyleSheet objects provides the most control and consistency over the appearance of text
throughout your application. You can define a TextFormat or StyleSheet object and apply it to
many or all text fields in your application.
Formatting text 521
Assigning text formats
You can use the TextFormat class to set a number of different text display properties and to
apply them to the entire contents of a TextField object, or to a range of text.
The following example applies one TextFormat object to an entire TextField object and
applies a second TextFormat object to a range of text within that TextField object:
var tf:TextField = new TextField();
tf.text = "Hello Hello";
var format1:TextFormat = new TextFormat();
format1.color = 0xFF0000;
var format2:TextFormat = new TextFormat();
format2.font = "Courier";
tf.setTextFormat(format1);
var startRange:uint = 6;
tf.setTextFormat(format2, startRange);
addChild(tf);
The TextField.setTextFormat() method only affects text that is already displayed in the
text field. If the content in the TextField changes, your application might need to call the
TextField.setTextFormat() method again to reapply the formatting. You can also set the
TextField object’s defaultTextFormat property to specify the format to be used for user-
entered text.
Applying cascading style sheets
Text fields can contain either plain text or HTML-formatted text. Plain text is stored in the
text property of the instance, and HTML text is stored in the htmlText property.
You can use CSS style declarations to define text styles that you can apply to many different
text fields. CSS style declarations can be created in your application code or loaded in at run
time from an external CSS file.
The flash.text.StyleSheet class handles CSS styles. The StyleSheet class recognizes a limited set
of CSS properties. For a detailed list of the style properties that the StyleSheet class supports,
see the flash.textStylesheet entry in the ActionScript 3.0 Language and Components Reference.
522 Working with text
As the following example shows, you can create CSS in your code and apply those styles to
HTML text by using a StyleSheet object:
var style:StyleSheet = new StyleSheet();
var styleObj:Object = new Object();
styleObj.fontSize = "bold";
styleObj.color = "#FF0000";
style.setStyle(".darkRed", styleObj);
var tf:TextField = new TextField();
tf.styleSheet = style;
tf.htmlText = "<span class = 'darkRed'>Red</span> apple";
addChild(tf);
After creating a StyleSheet object, the example code creates a simple object to hold a set of
style declaration properties. Then it calls the StyleSheet.setStyle() method, which adds
the new style to the stylesheet with the name “.darkred”. Next, it applies the stylesheet
formatting by assigning the StyleSheet object to the TextField object’s styleSheet property.
For CSS styles to take effect, the stylesheet should be applied to the a TextField object before
the htmlText property is set.
By design, a text field with a style sheet is not editable. If you have an input text field and
assign a style sheet to it, the text field shows the style sheet’s properties, but the text field will
not allow users to enter new text into it. Also, you cannot use the following ActionScript APIs
on a text field with an assigned style sheet:
■ The TextField.replaceText() method
■ The TextField.replaceSelectedText() method
■ The TextField.defaultTextFormat property
■ The TextField.setTextFormat() method
If a text field has a style sheet assigned to it, but later the TextField.styleSheet property is
set to null, the contents of both TextField.text and TextField.htmlText properties will
add tags and attributes to their content to incorporate the formatting from the previously
assigned style sheet. To preserve the original htmlText property, save it in a variable before
setting the style sheet to null.
Formatting text 523
Loading an external CSS file
The CSS approach to formatting is more powerful when you can load CSS information from
an external file at run time. When the CSS data is external to the application itself, you can
change the visual style of text in your application without having to change your ActionScript
3.0 source code. After your application has been deployed, you can change an external CSS
file to change the look of the application, without having to redeploy the application’s SWF
file.
The StyleSheet.parseCSS() method converts a string that contains CSS data into style
declarations in the StyleSheet object. The following example shows how to read an external
CSS file and apply its style declarations to a TextField object.
First, here is the content of the CSS file to be loaded, which will be named example.css:
p {
font-family: Times New Roman, Times, _serif;
font-size: 14;
}
h1 {
font-family: Arial, Helvetica, _sans;
font-size: 20;
font-weight: bold;
}
.bluetext {
color: #0000CC;
}
Next is the ActionScript code for a class that loads the example.css file and applies the styles to
TextField content:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
524 Working with text
public class CSSFormattingExample extends Sprite
{
var loader:URLLoader;
var field:TextField;
var exampleText:String = "<h1>This is a headline</h1>” +
“<p>This is a line of text. <span class='bluetext'>” +
“This line of text is colored blue.</span></p>";
public function CSSFormattingExample():void
{
field = new TextField();
field.width = 300;
field.autoSize = TextFieldAutoSize.LEFT;
field.wordWrap = true;
addChild(field);
var req:URLRequest = new URLRequest("example.css");
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onCSSFileLoaded);
loader.load(req);
}
public function onCSSFileLoaded(event:Event):void
{
var sheet:StyleSheet = new StyleSheet();
sheet.parseCSS(loader.data);
field.styleSheet = sheet;
field.htmlText = exampleText;
}
}
}
When the CSS data is loaded, the onCSSFileLoaded() method executes and calls the
StyleSheet.parseCSS() method to transfer the style declarations to the StyleSheet object.
Formatting ranges of text within a text field
A particularly useful method of the flash.text.TextField class is the setTextFormat()
method. Using setTextFormat(), you can assign specific properties to part of a text field’s
contents to respond to user input, such as forms that need to remind users that certain entries
are required or to change the emphasis of a subsection of a passage of text within a text field as
a user selects parts of the text.
Advanced text rendering 525
The following example uses TextField.setTextFormat() on a range of characters to change
the appearance of part of the content of myTextField when the user clicks on the text field:
var myTextField:TextField = new TextField();
myTextField.text = "No matter where you click on this text field the TEXT IN
ALL CAPS changes format.";
myTextField.autoSize = TextFieldAutoSize.LEFT;
addChild(myTextField);
addEventListener(MouseEvent.CLICK, changeText);
var myformat:TextFormat = new TextFormat();
myformat.color = 0xFF0000;
myformat.size = 18;
myformat.underline = true;
function changeText(event:MouseEvent):void
{
myTextField.setTextFormat(myformat, 49, 65);
}
Advanced text rendering
ActionScript 3.0 provides a variety of classes in the flash.text package to control the properties
of displayed text, including embedded fonts, anti-aliasing settings, alpha channel control, and
other specific settings. The ActionScript 3.0 Language and Components Reference provides
detailed descriptions of these classes and properties, including the CSMSettings, Font, and
TextRenderer classes.
Using embedded fonts
When you specify a specific font for a TextField in your application, Flash Player will look for
a device font (a font that resides on the user’s computer) with the same name. If it doesn’t find
that font on the user’s system, or if the user has a slightly different version of a font with that
name, the text display could look very different from what you intend.
To make sure the user sees exactly the right font, you can embed that font in your application’s
SWF file. Embedded fonts have a number of benefits:
■ Embedded font characters are anti-aliased, making their edges appear smoother, especially
for larger text.
■ You can rotate text that uses embedded fonts.
■ Embedded font text can be made transparent or semitransparent.
■ You can use the kerning CSS style with embedded fonts.
526 Working with text
The biggest limitation to using embedded fonts is that they increase the file size or download
size of your application.
The exact method of embedding a sound file into your application’s SWF file varies according
to your development environment.
Once you have embedded a font you can make sure a TextField uses the correct embedded
font:
■ Set the embedFonts property of the TextField to true.
■ Create a TextFormat object, set its fontFamily property to the name of the embedded
font, and apply the TextFormat object to the TextField. When specifying an embedded
font, the fontFamily property should only contain a single name; it cannot use a comma-
delimited list of multiple font names.
■ If using CSS styles to set fonts for TextFields or components, set the font-family CSS
property to the name of the embedded font. The font-family property must contain a
single name and not a list of names if you want to specify an embedded font.
Embedding a font in Flash
The Flash authoring tool lets you embed almost any font you have installed on your system,
including TrueType fonts and Type 1 Postscript fonts.
There are many ways to embed fonts in a Flash application, including:
■ Setting the font and style properties of a TextField on the Stage and clicking the Embed
Fonts checkbox
■ Creating and referencing a font symbol
■ Creating and using a run-time shared library containing embedded font symbols
For more details about how to embed fonts in Flash applications, see “Embedded fonts for
dynamic or input text fields” in Using Flash.
Advanced text rendering 527
Controlling sharpness, thickness, and anti-aliasing
By default, Flash Player determines the settings for text display controls like sharpness,
thickness, and anti-aliasing as text resizes, changes color, or is displayed on various
backgrounds. In some cases, like when you have very small or very large text, or text on a
variety of unique backgrounds, you may want to maintain your own control over these
settings. You can override the Flash Player settings using the flash.text.TextRenderer class
and its associated classes, like the CSMSettings class. These classes give you precise control
over the rendering quality of embedded text. For more information about embedded fonts,
see “Using embedded fonts” on page 525.
The following example applies custom continuous stroke modulation (CSM) properties and
formatting to displayed text using an embedded font called myFont. When the user clicks on
the displayed text, Flash Player applies the custom settings:
var format:TextFormat = new TextFormat();
format.color = 0x336699;
format.size = 48;
format.font = "myFont";
var myText:TextField = new TextField();
myText.embedFonts = true;
myText.autoSize = TextFieldAutoSize.LEFT;
myText.antiAliasType = AntiAliasType.ADVANCED;
myText.defaultTextFormat = format;
myText.selectable = false;
myText.mouseEnabled = true;
myText.text = "Hello World";
addChild(myText);
myText.addEventListener(MouseEvent.CLICK, clickHandler);
function clickHandler(event:Event):void
{
var myAntiAliasSettings = new CSMSettings(48, 0.8, -0.8);
var myAliasTable:Array = new Array(myAntiAliasSettings);
TextRenderer.setAdvancedAntiAliasingTable("myFont", FontStyle.ITALIC,
TextColorType.DARK_COLOR, myAliasTable);
}
NOTE
The flash.text.TextField.antiAliasType property must have the value
AntiAliasType.ADVANCED (which is the default value) in order for you to set the
sharpness, thickness, or the gridFitType property, or to use the
TextRenderer.setAdvancedAntiAliasingTable() method.
528 Working with text
Working with static text
Static text is created only within the Flash authoring tool. You cannot programmatically
instantiate static text using ActionScript. Static text is useful if the text is very short and is not
intended to change (as dynamic text can). Think of static text as a sort of graphic element like
a circle or square drawn on the Stage in the Flash authoring tool. While static text is more
limited than dynamic text, ActionScript 3.0 does support the ability to read the property
values of static text using the flash.text.StaticText class. In addition, you can use the
flash.text.TextSnapshot class to read values out of the static text.
Accessing static text fields with the StaticText class
Typically, you use the flash.text.StaticText class in the Actions panel of the Flash authoring
tool to interact with a static text instance placed on the Stage. You may also work in
ActionScript files that interact with a SWF file containing static text. In either case, you can’t
instantiate a static text instance programmatically. Static text is created in the Flash CS3
authoring tool.
To create a reference to an existing static text field in ActionScript 3.0, you can iterate over the
items in the display list and assign a variable. For example:
for (var i = 0; i < this.numChildren; i++) {
var displayitem:DisplayObject = this.getChildAt(i);
if (displayitem instanceof StaticText) {
trace("a static text field is item " + i + " on the display list");
var myFieldLabel:StaticText = StaticText(displayitem);
trace("and contains the text: " + myFieldLabel.text);
}
}
Once you have a reference to a static text field, you can use the properties of that field in
ActionScript 3.0. The following code is attached to a frame in the Timeline, and assumes a
variable named myFieldLabel assigned to a static text reference. In the example, a dynamic
text field named myField is positioned relative to the x and y values of myFieldLabel and
displays the value of myFieldLabel again.
var myField:TextField = new TextField();
addChild(myField);
myField.x = myFieldLabel.x;
myField.y = myFieldLabel.y + 20;
myField.autoSize = TextFieldAutoSize.LEFT;
myField.text = "and " + myFieldLabel.text
Example: Newspaper-style text formatting 529
Using the TextSnapshot class
If you want to programmatically work with an existing static text instance, you can use the
flash.text.TextSnapshot class to work with the textSnapshot property of a
flash.display.DisplayObjectContainer. In other words, you create a TextSnapshot instance
from the DisplayObjectContainer.textSnapshot property. You can then apply methods
to that instance to retrieve values or select parts of the static text.
For example, place a static text field that contains the text "TextSnapshot Example" on the
Stage. Add the following ActionScript to Frame 1 of the Timeline:
var mySnap:TextSnapshot = this.getTextSnapshot();
var count:Number = mySnap.getCount();
mySnap.setSelected(0, 4, true);
mySnap.setSelected(1, 2, false);
var myText:String = mySnap.getSelectedText(false);
trace(myText);
The TextSnapshot class is useful for getting the text out of static text fields in a loaded SWF, in
case you want to use the text as a value in another part of an application.
Example: Newspaper-style text
formatting
The News Layout example formats text to look something like a story in a printed newspaper.
The input text can contain a headline, a subtitle, and the body of the story. Given a display
width and height, this News Layout example will format the headline and the subtitle to take
the full width of the display area. The story text will be distributed across two or more
columns.
This example illustrates the following ActionScript programming techniques:
■ Extending the TextField class
■ Loading and applying an external CSS file
■ Converting CSS styles into TextFormat objects
■ Using the TextLineMetrics class to get information about text display size
530 Working with text
To get the application files for this sample, see www.adobe.com/go/
learn_programmingAS3samples_flash. The News Layout application files can be found in the
folder Samples/NewsLayout. The application consists of the following files:
Reading the external CSS file
The News Layout application starts by reading story text from a local XML file. Then it reads
an external CSS file that provides the formatting information for the headline, subtitle, and
main text.
The CSS file defines three styles, a standard paragraph style for the story, and the h1 and h2
styles for the headline and subtitle respectively.
p {
font-family: Georgia, Times New Roman, Times, _serif;
font-size: 12;
leading: 2;
text-align: justify;
}
h1 {
font-family: Verdana, Arial, Helvetica, _sans;
font-size: 20;
font-weight: bold;
color: #000099;
text-align: left;
}
File Description
NewsLayout.mxml
or
NewsLayout.fla
The user interface for the application for Flex
(MXML) or Flash (FLA).
StoryLayout.as The main ActionScript class that arranges all
the components of a news story for display.
FormattedTextField.as A subclass of the TextField class that manages
its own TextFormat object.
HeadlineTextField.as A subclass of the FormattedTextField class that
adjusts font sizes to fit a desired width.
MultiColumnTextField.as An ActionScript class that splits text across two
or more columns.
story.css A CSS file that defines text styles for the layout.
newsconfig.xml An XML file containing story content.
Example: Newspaper-style text formatting 531
h2 {
font-family: Verdana, Arial, Helvetica, _sans;
font-size: 16;
font-weight: normal;
text-align: left;
}
The technique used to read the external CSS file is that same as the technique described in
“Loading an external CSS file” on page 523. When the CSS file has been loaded the
application executes the onCSSFileLoaded() method, shown below.
public function onCSSFileLoaded(event:Event):void
{
this.sheet = new StyleSheet();
this.sheet.parseCSS(loader.data);
h1Format = getTextStyle("h1", this.sheet);
if (h1Format == null)
{
h1Format = getDefaultHeadFormat();
}
h2Format = getTextStyle("h2", this.sheet);
if (h2Format == null)
{
h2Format = getDefaultHeadFormat();
h2Format.size = 16;
}
displayStory();
}
The onCSSFileLoaded() method creates a new StyleSheet object and has it parse the input
CSS data. The main text for the story will be displayed in a MultiColumnTextField object,
which can use a StyleSheet object directly. However, the headline fields use the
HeadlineTextField class, which uses a TextFormat object for its formatting.
The onCSSFileLoaded() method calls the getTextStyle() method twice to convert a CSS
style declaration into a TextFormat object for use with each of the two HeadlineTextField
objects. The getTextStyle() method is shown below:
public function getTextStyle(styleName:String, ss:StyleSheet):TextFormat
{
var format:TextFormat = null;
var style:Object = ss.getStyle(styleName);
if (style != null)
{
532 Working with text
var colorStr:String = style.color;
if (colorStr != null && colorStr.indexOf("#") == 0)
{
style.color = colorStr.substr(1);
}
format = new TextFormat(style.fontFamily,
style.fontSize,
style.color,
(style.fontWeight == "bold"),
(style.fontStyle == "italic"),
(style.textDecoration == "underline"),
style.url,
style.target,
style.textAlign,
style.marginLeft,
style.marginRight,
style.textIndent,
style.leading);
if (style.hasOwnProperty("letterSpacing"))
{
format.letterSpacing = style.letterSpacing;
}
}
return format;
}
The property names and the meaning of the property values differ between CSS style
declarations and TextFormat objects. The getTextStyle() method translates CSS property
values into the values expected by the TextFormat object.
Arranging story elements on the page
The StoryLayout class formats and lays out the headline, subtitle, and main text fields into a
newspaper-style arrangement. The displayText() method initially creates and places the
various fields.
public function displayText():void
{
headlineTxt = new HeadlineTextField(h1Format);
headlineTxt.wordWrap = true;
this.addChild(headlineTxt);
headlineTxt.width = 600;
headlineTxt.height = 100;
headlineTxt.fitText(this.headline, 1, true);
Example: Newspaper-style text formatting 533
subtitleTxt = new HeadlineTextField(h2Format);
subtitleTxt.wordWrap = true;
subtitleTxt.y = headlineTxt.y + headlineTxt.height;
this.addChild(subtitleTxt);
subtitleTxt.width = 600;
subtitleTxt.height = 100;
subtitleTxt.fitText(this.subtitle, 1, false);
storyTxt = new MultiColumnTextField(2, 10, 600, 200);
storyTxt.y = subtitleTxt.y + subtitleTxt.height + 4;
this.addChild(storyTxt);
storyTxt.styleSheet = this.sheet;
storyTxt.htmlText = loremIpsum;
}
Each field is simply placed below the previous field by setting its y property equal to the y
property of the previous field plus the height of the previous field. This dynamic placement
calculation is needed because HeadlineTextField objects and MultiColumnTextField objects
can change their height to fit their contents.
Altering font size to fit the field size
Given a width in pixels and a maximum number of lines to display, the HeadlineTextField
alters the font size to make the text fit the field. If the text is short the font size will be very
large, creating a tabloid-style headline. If the text is long the font size will of course be smaller.
The HeadlineTextField.fitText() method shown below does the font sizing work:
public function fitText(msg:String, maxLines:uint = 1, toUpper:Boolean =
false, targetWidth:Number = -1):uint
{
this.text = toUpper ? msg.toUpperCase() : msg;
if (targetWidth == -1)
{
targetWidth = this.width;
}
var pixelsPerChar:Number = targetWidth / msg.length;
var pointSize:Number = Math.min(MAX_POINT_SIZE, Math.round(pixelsPerChar
* 1.8 * maxLines));
if (pointSize < 6)
{
534 Working with text
// the point size is too small
return pointSize;
}
this.changeSize(pointSize);
if (this.numLines > maxLines)
{
return shrinkText(--pointSize, maxLines);
}
else
{
return growText(pointSize, maxLines);
}
}
public function growText(pointSize:Number, maxLines:uint = 1):Number
{
if (pointSize >= MAX_POINT_SIZE)
{
return pointSize;
}
this.changeSize(pointSize + 1);
if (this.numLines > maxLines)
{
// set it back to the last size
this.changeSize(pointSize);
return pointSize;
}
else
{
return growText(pointSize + 1, maxLines);
}
}
public function shrinkText(pointSize:Number, maxLines:uint=1):Number
{
if (pointSize <= MIN_POINT_SIZE)
{
return pointSize;
}
Example: Newspaper-style text formatting 535
this.changeSize(pointSize);
if (this.numLines > maxLines)
{
return shrinkText(pointSize - 1, maxLines);
}
else
{
return pointSize;
}
}
The HeadlineTextField.fitText() method uses a simple recursive technique to size the
font. First it guesses an average number of pixels per character in the text and from there
calculates a starting point size. Then it changes the text field’s font size and checks whether the
text has word wrapped to create more than the maximum number of text lines. If there are too
many lines it calls the shrinkText() method to decrease the font size and try again. If there
are not too many lines it calls the growText() method to increase the font size and try again.
The process stops at the point where incrementing the font size by one more point would
create too many lines.
Splitting text across multiple columns
The MultiColumnTextField class spreads text among multiple TextField objects which are
then arranged like newspaper columns.
The MultiColumnTextField() constructor first creates an array of TextField objects, one for
each column, as shown here:
for (var i:int = 0; i < cols; i++)
{
var field:TextField = new TextField();
field.autoSize = TextFieldAutoSize.NONE;
field.wordWrap = true;
field.styleSheet = this.styleSheet;
this.fieldArray.push(field);
this.addChild(field);
}
Each TextField object is added to the array and added to the display list with the addChild()
method.
Whenever the StoryLayout object’s text property or styleSheet property changes, it calls
the layoutColumns() method to redisplay the text. The layoutColumns() method calls the
getOptimalHeight() method, shown below, to figure out the correct pixel height needed to
fit all of the text within the given layout width.
536 Working with text
public function getOptimalHeight(str:String):int
{
if (fieldArray.length == 0 || str == "" || str == null)
{
return this.preferredHeight;
}
else
{
var colWidth:int = Math.floor( (this.preferredWidth -
((this.numColumns - 1) * gutter)) / this.numColumns);
var field:TextField = fieldArray[0] as TextField;
field.width = colWidth;
field.htmlText = str;
var linesPerCol:int = Math.ceil(field.numLines / this.numColumns);
var metrics:TextLineMetrics = field.getLineMetrics(0);
var prefHeight:int = linesPerCol * metrics.height;
return prefHeight + 4;
}
}
First the getOptimalHeight() method calculates the width of each column. Then it sets the
width and htmlText property of the first TextField object in the array. The
getOptimalHeight() method uses that first TextField object to discover the total number of
word-wrapped lines in the text, and from that it identifies how many lines should be in each
column. Next it calls the TextField.getLineMetrics() method to retrieve a
TextLineMetrics object that contains details about size of the text in the first line. The
TextLineMetrics.height property represents the full height of a line of text, in pixels,
including the ascent, descent, and leading. The optimal height for the MultiColumnTextField
object is then the line height multiplied by the number of lines per column, plus 4 to account
for the 2 pixel border at the top and the bottom of a TextField object.
Here is the code for the full layoutColumns() method:
public function layoutColumns():void
{
if (this._text == "" || this._text == null)
{
return;
}
if (this.fitToText)
{
this.preferredHeight = this.getOptimalHeight(this._text);
}
var colWidth:int = Math.floor( (this.preferredWidth -
((numColumns - 1) * gutter)) / numColumns);
Example: Newspaper-style text formatting 537
var field:TextField;
var remainder:String = this._text;
var fieldText:String = "";
for (var i:int = 0; i < fieldArray.length; i++)
{
field = this.fieldArray[i] as TextField;
field.width = colWidth;
field.height = this.preferredHeight;
field.x = i * (colWidth + gutter);
field.y = 0;
field.htmlText = "<p>" + remainder + "</p>";
remainder = "";
fieldText = "";
var linesRemaining:int = field.numLines;
var linesVisible:int = field.numLines - field.maxScrollV + 1;
for (var j:int = 0; j < linesRemaining; j++)
{
if (j < linesVisible)
{
fieldText += field.getLineText(j);
}
else
{
remainder += field.getLineText(j);
}
}
field.htmlText = "<p>" + fieldText + "</p>";
}
}
After the preferredHeight property has been set by calling the getOptimalHeight()
method, the layoutColumns() method iterates through the TextField objects, setting the
height of each to the preferredHeight value. The layoutColumns() method then
distributes just enough lines of text to each field so that no scrolling occurs in any individual
field, and the text in each successive field begins where the text in the previous field ended.
538 Working with text
539
18
CHAPTER 18
Working with bitmaps
In addition to its vector drawing capabilities, ActionScript 3.0 includes the ability to create
bitmap images or manipulate the pixel data of external bitmap images that are loaded into a
SWF. With the ability to access and change individual pixel values, you can create your own
filter-like image effects and use the built-in noise functions to create textures and random
noise. All of these techniques are described in this chapter.
Contents
Basics of working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
The Bitmap and BitmapData classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .543
Manipulating pixels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
Copying bitmap data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
Making textures with noise functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
Scrolling bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .552
Example: Animating sprites using an offscreen bitmap . . . . . . . . . . . . . . . . . . . . . . .553
540 Working with bitmaps
Basics of working with bitmaps
Introduction to working with bitmaps
When you work with digital images, you’re likely to encounter two main types of graphics:
bitmap and vector. Bitmap graphics, also known as raster graphics, are composed of tiny
squares (pixels) that are arranged in a rectangular grid formation. Vector graphics are
composed of mathematically generated geometric shapes such as lines, curves, and polygons.
Bitmap images are defined by the width and height of the image, measured in pixels, and the
number of bits contained in each pixel, which represents the number of colors a pixel can
contain. In the case of a bitmap image that utilizes the RGB color model, the pixels are made
up of three bytes: red, green, and blue. Each of these bytes contains a value ranging from 0 to
255. When the bytes are combined within the pixel, they produce a color similar to an artist
mixing paint colors. For example, a pixel containing byte values of red-255, green-102 and
blue-0 would produce a vibrant orange color.
The quality of a bitmap image is determined by combining the resolution of the image with
its color depth bit value. Resolution relates to the number of pixels contained within an image.
The greater the number of pixels, the higher the resolution and the finer the image appears.
Color depth relates to the amount of information a pixel can contain. For example, an image
that has a color depth value of 16 bits per pixel cannot represent the same number of colors as
an image that has a color depth of 48 bits. As a result, the 48-bit image will have smoother
degrees of shading than its 16-bit counterpart.
Because bitmap graphics are resolution-dependent, they don’t scale very well. This is most
noticeable when bitmap images are scaled up in size. Scaling up a bitmap usually results in a
loss of detail and quality.
Bitmap file formats
Bitmap images are grouped into a number of common file formats. These formats use
different types of compression algorithms to reduce file size, as well as optimize image quality
based on the end purpose of the image. The bitmap image formats supported by Adobe Flash
Player are GIF, JPG, and PNG.
Basics of working with bitmaps 541
GIF
The Graphics Interchange Format (GIF) was originally developed by CompuServe in 1987 as
a means to transmit images with 256 colors (8-bit color). The format provides small file sizes
and is ideal for web-based images. Because of this format’s limited color palette, GIF images
are generally not suitable for photographs, which typically require high degrees of shading and
color gradients. GIF images permit single-bit transparency, which allows colors to be mapped
as clear (or transparent). This results in the background color of a web page showing through
the image where the transparency has been mapped.
JPEG
Developed by the Joint Photographic Experts Group (JPEG), the JPEG (often written JPG)
image format uses a lossy compression algorithm to allow 24-bit color depth with a small file
size. Lossy compression means that each time the image is saved, the image loses quality and
data but results in a smaller file size. The JPEG format is ideal for photographs because it is
capable of displaying millions of colors. The ability to control the degree of compression
applied to an image allows you to manipulate image quality and file size.
PNG
The Portable Network Graphics (PNG) format was produced as an open-source alternative to
the patented GIF file format. PNGs support up to 64-bit color depth, allowing for up to 16
million colors. Because PNG is a relatively new format, some older browsers don’t support
PNG files. Unlike JPGs, PNGs use lossless compression, which means that none of the image
data is lost when the image is saved. PNG files also support alpha transparency, which allows
for up to 256 levels of transparency.
Transparent bitmaps and opaque bitmaps
Bitmap images that use either the GIF or PNG formats can have an extra byte (alpha channel)
added to each pixel. This extra pixel byte represents the transparency value of the pixel.
GIF images allow single-bit transparency, which means that you can specify a single color,
from a 256-color palette, to be transparent. PNG images, on the other hand, can have up to
256 levels of transparency. This function is especially beneficial when images or text are
required to blend into backgrounds.
ActionScript 3.0 replicates this extra transparency pixel byte within the BitmapData class.
Similar to the PNG transparency model, the BitmapDataChannel.ALPHA constant offers up
to 256 levels of transparency.
542 Working with bitmaps
Common tasks for working with bitmaps
The following list describes several tasks you may want to perform when working with bitmap
images in ActionScript:
■ Displaying bitmaps on the screen
■ Retrieving and setting pixel color values
■ Copying bitmap data:
■ Creating an exact copy of a bitmap
■ Copying data from one color channel of a bitmap into one color channel of another
bitmap
■ Copying a snapshot of a screen display object into a bitmap
■ Creating noise and textures in bitmap images
■ Scrolling bitmaps
Important concepts and terms
The following list contains important terms that you will encounter in this chapter:
■ Alpha: The level of transparency (or more accurately, opacity) in a color or an image. The
amount of alpha is often described as the alpha channel value.
■ ARGB color: A color scheme where each pixel’s color is a mixture of red, green, and blue
color values, and its transparency is specified as an alpha value.
■ Color channel: Commonly, colors are represented as a mixture of a few basic colors—
usually (for computer graphics) red, green, and blue. Each basic color is considered a color
channel; the amount of color in each color channel, mixed together, determines the final
color.
■ Color depth: Also known as bit depth, this refers to the amount of computer memory that
is devoted to each pixel, which in turn determines the number of possible colors that can
be represented in the image.
■ Pixel: The smallest unit of information in a bitmap image—essentially a dot of color.
■ Resolution: The pixel dimensions of an image, which determines the level of fine-grained
detail contained in the image. Resolution is often expressed in terms of width and height
in number of pixels.
■ RGB color: A color scheme where each pixel’s color is represented as a mixture of red,
green, and blue color values.
The Bitmap and BitmapData classes 543
Working through in-chapter examples
While you’re working through the chapter, you may want to test the example code. Because
this chapter deals with creating and manipulating visual content, testing the code involves
running the code and viewing the results in the SWF that’s created.
To test the code examples in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code into the Script pane.
4. Run the program using Control > Test Movie.
You see the results of the code in the SWF file that’s created.
Nearly all the example code includes code that creates a bitmap image, so you can just test the
code directly without needing to provide any bitmap content. Alternatively, if you want to test
the code on your own image, you can import that image into Adobe Flash CS3 Professional or
load the external image into the test SWF and use its bitmap data with the example code. For
instructions on loading external images, see “Loading display content dynamically”
on page 418.
The Bitmap and BitmapData classes
The main ActionScript 3.0 classes for working with bitmap images are the Bitmap class,
which is used to display bitmap images on the screen, and the BitmapData class, which is used
to access and manipulate the raw image data of a bitmap.
Understanding the Bitmap class
As a subclass of the DisplayObject class, the Bitmap class is the main ActionScript 3.0 class
used for displaying bitmap images. These images may have been loaded into Flash via the
flash.display.Loader class or created dynamically using the Bitmap() constructor. When
loading an image from an external source, a Bitmap object can only use GIF, JPEG, or PNG
format images. Once instantiated, the Bitmap instance can be considered a wrapper for a
BitmapData object that needs to be rendered to the Stage. Because a Bitmap instance is a
display object, all the characteristics and functionality of display objects can be used to
manipulate a Bitmap instance as well. For more information about working with display
objects, see Chapter 12, “Display programming,” on page 371.
544 Working with bitmaps
Pixel snapping and smoothing
In addition to the functionality common to all display objects, the Bitmap class provides some
additional features that are specific to bitmap images.
Similar to the snap-to-pixel feature found in the Flash authoring tool, the pixelSnapping
property of the Bitmap class determines whether or not a Bitmap object snaps to its nearest
pixel. This property accepts one of three constants defined in the PixelSnapping class: ALWAYS,
AUTO, and NEVER.
The syntax for applying pixel snapping is as follows:
myBitmap.pixelSnapping = PixelSnapping.ALWAYS;
Often, when bitmap images are scaled, they become blurred and distorted. To help reduce this
distortion, use the smoothing property of the BitmapData class. This Boolean property, when
set to true, smooths, or anti-aliases, the pixels within the image when it is scaled. This gives
the image a clearer and more natural appearance.
Understanding the BitmapData class
The BitmapData class, which is in the flash.display package, can be likened to a photographic
snapshot of the pixels contained within a loaded or dynamically created bitmap image. This
snapshot is represented by an array of pixel data within the object. The BitmapData class also
contains a series of built-in methods that are useful for creation and manipulation of pixel
data.
To instantiate a BitmapData object, use the following code:
var myBitmap:BitmapData = new BitmapData(width:Number, height:Number,
transparent:Boolean, fillColor:uinit);
The width and height parameters specify the size of the bitmap; the maximum value of both
is 2880 pixels. The transparent parameter specifies whether the bitmap data includes an
alpha channel (true) or not (false). The fillColor parameter is a 32-bit color value that
specifies the background color, as well as the transparency value (if it has been set to true).
The following example creates a BitmapData object with an orange background that is 50
percent transparent:
var myBitmap:BitmapData = new BitmapData(150, 150, true, 0x80FF3300);
Manipulating pixels 545
To render a newly created BitmapData object to the screen, assign it to or wrap it in a Bitmap
instance. To do this, you can either pass the BitmapData object as a parameter of the Bitmap
object’s constructor, or you can assign it to the bitmapData property of an existing Bitmap
instance. You must also add the Bitmap instance to the display list by calling the addChild()
or addChildAt() methods of the display object container that will contain the Bitmap
instance. For more information on working with the display list, see “Adding display objects
to the display list” on page 383.
The following example creates a BitmapData object with a red fill, and displays it in a Bitmap
instance:
var myBitmapDataObject:BitmapData = new BitmapData(150, 150, false,
0xFF0000);
var myImage:Bitmap = new Bitmap(myBitmapDataObject);
addChild(myImage);
Manipulating pixels
The BitmapData class contains a set of methods that allow you to manipulate pixel data
values.
Manipulating individual pixels
When changing the appearance of a bitmap image at a pixel level, you first need to get the
color values of the pixels contained within the area you wish to manipulate. You use the
getPixel() method to read these pixel values.
The getPixel() method retrieves an RGB value from a set of x, y (pixel) coordinates that are
passed as a parameter. If any of the pixels that you want to manipulate include transparency
(alpha channel) information, you need to use the getPixel32() method. This method also
retrieves an RGB value, but unlike with getPixel(), the value returned by getPixel32()
contains additional data that represents the alpha channel (transparency) value of the selected
pixel.
Alternatively, if you simply want to change the color or transparency of a pixel contained
within a bitmap, you can use the setPixel() or setPixel32() method. To set a pixel’s
color, simply pass in the x, y coordinates and the color value to one of these methods.
The following example uses setPixel() to draw a cross on a green BitmapData background.
It then uses getPixel() to retrieve the color value from the pixel at the coordinate 50, 50 and
traces the returned value.
546 Working with bitmaps
import flash.display.Bitmap;
import flash.display.BitmapData;
var myBitmapData:BitmapData = new BitmapData(100, 100, false, 0x009900);
for (var i:uint = 0; i < 100; i++)
{
var red:uint = 0xFF0000;
myBitmapData.setPixel(50, i, red);
myBitmapData.setPixel(i, 50, red);
}
var myBitmapImage:Bitmap = new Bitmap(myBitmapData);
addChild(myBitmapImage);
var pixelValue:uint = myBitmapData.getPixel(50, 50);
trace(pixelValue.toString(16));
If you want to read the value of a group of pixels, as opposed to a single pixel, use the
getPixels() method. This method generates a byte array from a rectangular region of pixel
data that is passed as a parameter. Each of the elements of the byte array (in other words, the
pixel values) are unsigned integers—32-bit, unmultiplied pixel values.
Conversely, in order to change (or set) the value of a group of pixels, use the setPixels()
method. This method expects two parameters (rect and inputByteArray), which are
combined to output a rectangular region (rect) of pixel data (inputByteArray).
As data is read (and written) out of the inputByteArray, the
ByteArray.readUnsignedInt() method is called for each of the pixels in the array. If, for
some reason, the inputByteArray doesn't contain a full rectangle worth of pixel data, the
method stops processing the image data at that point.
It's important to remember that, for both getting and setting pixel data, the byte array expects
32-bit alpha, red, green, blue (ARGB) pixel values.
The following example uses the getPixels() and setPixels() methods to copy a group of
pixels from one BitmapData object to another:
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import flash.geom.Rectangle;
var bitmapDataObject1:BitmapData = new BitmapData(100, 100, false,
0x006666FF);
var bitmapDataObject2:BitmapData = new BitmapData(100, 100, false,
0x00FF0000);
var rect:Rectangle = new Rectangle(0, 0, 100, 100);
var bytes:ByteArray = bitmapDataObject1.getPixels(rect);
Manipulating pixels 547
bytes.position = 0;
bitmapDataObject2.setPixels(rect, bytes);
var bitmapImage1:Bitmap = new Bitmap(bitmapDataObject1);
addChild(bitmapImage1);
var bitmapImage2:Bitmap = new Bitmap(bitmapDataObject2);
addChild(bitmapImage2);
bitmapImage2.x = 110;
Pixel-level collision detection
The BitmapData.hitTest() method performs pixel-level collision detection between the
bitmap data and another object or point.
The BitmapData.hitTest() method accepts five parameters:
■ firstPoint (Point): This parameter refers to the pixel position of the upper-left corner of
the first BitmapData upon which the hit test is being performed.
■ firstAlphaThreshold (uint): This parameter specifies the highest alpha channel value
that is considered opaque for this hit test.
■ secondObject (Object): This parameter represents the area of impact. The
secondObject object can be a Rectangle, Point, Bitmap, or BitmapData object. This
object represents the hit area on which the collision detection is being performed.
■ secondBitmapDataPoint (Point): This optional parameter is used to define a pixel
location in the second BitmapData object. This parameter is used only when the value of
secondObject is a BitmapData object. The default is null.
■ secondAlphaThreshold (uint): This optional parameter represents the highest alpha
channel value that is considered opaque in the second BitmapData object. The default
value is 1. This parameter is only used when the value of secondObject is a BitmapData
object and both BitmapData objects are transparent.
When performing collision detection on opaque images, keep in mind that ActionScript
treats the image as though it were a fully opaque rectangle (or bounding box). Alternatively,
when performing pixel-level hit testing on images that are transparent, both of the images are
required to be transparent. In addition to this, ActionScript uses the alpha threshold
parameters to determine at what point the pixels change from being transparent to opaque.
548 Working with bitmaps
The following example creates three bitmap images and checks for pixel collision using two
different collision points (one returns false, the other true):
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
var bmd1:BitmapData = new BitmapData(100, 100, false, 0x000000FF);
var bmd2:BitmapData = new BitmapData(20, 20, false, 0x00FF3300);
var bm1:Bitmap = new Bitmap(bmd1);
this.addChild(bm1);
// Create a red square.
var redSquare1:Bitmap = new Bitmap(bmd2);
this.addChild(redSquare1);
redSquare1.x = 0;
// Create a second red square.
var redSquare2:Bitmap = new Bitmap(bmd2);
this.addChild(redSquare2);
redSquare2.x = 150;
redSquare2.y = 150;
// Define the point at the top-left corner of the bitmap.
var pt1:Point = new Point(0, 0);
// Define the point at the center of redSquare1.
var pt2:Point = new Point(20, 20);
// Define the point at the center of redSquare2.
var pt3:Point = new Point(160, 160);
trace(bmd1.hitTest(pt1, 0xFF, pt2)); // true
trace(bmd1.hitTest(pt1, 0xFF, pt3)); // false
Copying bitmap data 549
Copying bitmap data
To copy bitmap data from one image to another, you can use several methods: clone(),
copyPixels(), copyChannel(), and draw().
As its name suggests, the clone() method lets you clone, or sample, bitmap data from one
BitmapData object to another. When called, the method returns a new BitmapData object
that is an exact clone of the original instance it was copied from.
The following example clones a copy of an orange (parent) square and places the clone beside
the original parent square:
import flash.display.Bitmap;
import flash.display.BitmapData;
var myParentSquareBitmap:BitmapData = new BitmapData(100, 100, false,
0x00ff3300);
var myClonedChild:BitmapData = myParentSquareBitmap.clone();
var myParentSquareContainer:Bitmap = new Bitmap(myParentSquareBitmap);
this.addChild(myParentSquareContainer);
var myClonedChildContainer:Bitmap = new Bitmap(myClonedChild);
this.addChild(myClonedChildContainer);
myClonedChildContainer.x = 110;
The copyPixels() method is a quick and easy way of copying pixels from one BitmapData
object to another. The method takes a rectangular snapshot (defined by the sourceRect
parameter) of the source image and copies it to another rectangular area (of equal size). The
location of the newly “pasted” rectangle is defined within the destPoint parameter.
The copyChannel() method samples a predefined color channel value (alpha, red, green, or
blue) from a source BitmapData object and copies it into a channel of a destination
BitmapData object. Calling this method does not affect the other channels in the destination
BitmapData object.
The draw() method draws, or renders, the graphical content from a source sprite, movie clip,
or other display object on to a new bitmap. Using the matrix, colorTransform, blendMode,
and destination clipRect parameters, you can modify the way in which the new bitmap is
rendered. This method uses the Flash Player vector renderer to generate the data.
550 Working with bitmaps
When you call draw(), you pass the source object (sprite, movie clip, or other display object)
as the first parameter, as demonstrated here:
myBitmap.draw(movieClip);
If the source object has had any transformations (color, matrix, and so forth) applied to it after
it was originally loaded, these transformations are not copied across to the new object. If you
want to copy the transformations to the new bitmap, then you need to copy the value of the
transform property from the original object to the transform property of the Bitmap object
that uses the new BitmapData object.
Making textures with noise functions
To modify the appearance of a bitmap, you can apply a noise effect to it, using either the
noise() method or the perlinNoise() methods. A noise effect can be likened to the static
that appears on an untuned television screen.
To apply a noise effect to a a bitmap, use the noise() method. This method applies a random
color value to pixels within a specified area of a bitmap image.
This method accepts five parameters:
■ randomSeed (int): The random seed number that determines the pattern. Despite its
name, this number actually creates the same results if the same number is passed. In order
to get a true random result, use the Math.random() method to pass a random number for
this parameter.
■ low (uint): This parameter refers to the lowest value to be generated for each pixel (0 to
255). The default value is 0. Setting this value lower results in a darker noise pattern,
while setting it to a higher value results in a brighter pattern.
■ high (uint): This parameter refers to the highest value to be generated for each pixel (0 to
255). The default value is 255. Setting this value lower results in a darker noise pattern,
while setting it to a higher value results in a brighter pattern.
■ channelOptions (uint): This parameter specifies to which color channel of the bitmap
object the noise pattern will be applied. The number can be a combination of any of the
four color channel ARGB values. The default value is 7.
■ grayScale (Boolean): When set to true, this parameter applies the randomSeed value to
the bitmap pixels, effectively washing all color out of the image. The alpha channel is not
affected by this parameter. The default value is false.
Making textures with noise functions 551
The following example creates a bitmap image and applies a blue noise pattern to it:
import flash.display.Bitmap;
import flash.display.BitmapData;
var myBitmap:BitmapData = new BitmapData(250, 250,false, 0xff000000);
myBitmap.noise(500, 0, 255, BitmapDataChannel.BLUE,false);
var image:Bitmap = new Bitmap(myBitmap);
addChild(image);
If you want to create a more organic-looking texture, use the perlinNoise() method. The
perlinNoise() method produces realistic, organic textures that are ideal for smoke, clouds,
water, fire, or even explosions.
Because it is generated by an algorithm, the perlinNoise() method uses less memory than
bitmap-based textures. However, it can still have an impact on processor usage, slowing down
your Flash content and causing the screen to be redrawn more slowly than the frame rate,
especially on older computers. This is mainly due to the floating-point calculations that need
to occur in order to process the perlin noise algorithms.
The method accepts nine parameters (the first six are required):
■ baseX (Number): Determines the x (size) value of patterns created.
■ baseY (Number): Determines the y (size) value of the patterns created.
■ numOctaves (uint): Number of octaves or individual noise functions to combine to create
this noise. Larger numbers of octaves create images with greater detail but also require
more processing time.
■ randomSeed (int): The random seed number works exactly the same way as it does in the
noise() function. To get a true random result, use the Math.random() method to pass a
random number for this parameter.
■ stitch (Boolean): If set to true, this method attempts to stitch (or smooth) the
transition edges of the image to create seamless textures for tiling as a bitmap fill.
■ fractalNoise (Boolean): This parameter relates to the edges of the gradients being
generated by the method. If set to true, the method generates fractal noise that smooths
the edges of the effect. If set to false, it generates turbulence. An image with turbulence
has visible discontinuities in the gradient that can make it better approximate sharper
visual effects, like flames and ocean waves.
■ channelOptions (uint): The channelOptions parameter works exactly the same way as
it does in the noise() method. It specifies to which color channel (of the bitmap) the
noise pattern is applied. The number can be a combination of any of the four color
channel ARGB values. The default value is 7.
552 Working with bitmaps
■ grayScale (Boolean): The grayScale parameter works exactly the same way as it does in
the noise() method. If set to true, it applies the randomSeed value to the bitmap pixels,
effectively washing all color out of the image. The default value is false.
■ offsets (Array): An array of points that correspond to x and y offsets for each octave. By
manipulating the offset values, you can smoothly scroll the layers of the image. Each point
in the offset array affects a specific octave noise function. The default value is null.
The following example creates a 150 x 150 pixel BitmapData object that calls the
perlinNoise() method to generate a green and blue cloud effect:
import flash.display.Bitmap;
import flash.display.BitmapData;
var myBitmapDataObject:BitmapData = new BitmapData(150, 150, false,
0x00FF0000);
var seed:Number = Math.floor(Math.random() * 100);
var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE
myBitmapDataObject.perlinNoise(100, 80, 6, seed, false, true, channels,
false, null);
var myBitmap:Bitmap = new Bitmap(myBitmapDataObject);
addChild(myBitmap);
Scrolling bitmaps
Imagine you have created a street mapping application where each time the user moves the
map you are required to update the view (even if the map has been moved by just a few
pixels).
One way to create this functionality would be to re-render a new image containing the
updated map view each time the user moves the map. Alternatively, you could create a large
single image and the scroll() method.
The scroll() method copies an on-screen bitmap and then pastes it to a new offset
location—specified by (x, y) parameters. If a portion of the bitmap happens to reside off-
stage, this gives the effect that the image has shifted. When combined with a timer function
(or an enterFrame event), you can make the image appear to be animating or scrolling.
Example: Animating sprites using an offscreen bitmap 553
The following example takes the previous perlin noise example and generates a larger bitmap
image (three-fourths of which is rendered off-stage). The scroll() method is then applied,
along with an enterFrame event listener that offsets the image by one pixel in a diagonally
downward direction. This method is called each time the frame is entered and as a result, the
offscreen portions of the image are rendered to the Stage as the image scrolls down.
import flash.display.Bitmap;
import flash.display.BitmapData;
var myBitmapDataObject:BitmapData = new BitmapData(1000, 1000, false,
0x00FF0000);
var seed:Number = Math.floor(Math.random() * 100);
var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE;
myBitmapDataObject.perlinNoise(100, 80, 6, seed, false, true, channels,
false, null);
var myBitmap:Bitmap = new Bitmap(myBitmapDataObject);
myBitmap.x = -750;
myBitmap.y = -750;
addChild(myBitmap);
addEventListener(Event.ENTER_FRAME, scrollBitmap);
function scrollBitmap(event:Event):void
{
myBitmapDataObject.scroll(1, 1);
}
Example: Animating sprites using an
offscreen bitmap
Many Flash games involve hundreds of images animating on the screen at once. This bitmap
animation example draws several hundred small bitmaps or sprites on to a big offscreen
bitmap, then writes that one bitmap to the screen, which greatly speeds up the animation. For
a description of this example and to download the source code, go to www.adobe.com/go/
learn_fl_bitmaps.
554 Working with bitmaps
555
19
CHAPTER 19
Working with video
Flash video is one of the standout technologies on the Internet. However, the traditional
presentation of video—in a rectangular screen with a progress bar and some control buttons
underneath—is only one possible use of video in a Flash application. Through ActionScript,
you have fine-tuned access to and control over video loading, presentation, and playback.
Contents
Basics of video . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .556
Understanding the Flash Video (FLV) format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
Understanding the Video class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
Loading video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
Controlling video playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .562
Streaming video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .564
Understanding cue points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .565
Writing callback methods for onCuePoint and onMetaData . . . . . . . . . . . . . . . . . .566
Using cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .572
Using video metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .573
Capturing camera input. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .585
Example: Video Jukebox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .587
556 Working with video
Basics of video
Introduction to working with video
One important capability of Adobe Flash Player is the ability to display and manipulate video
information with ActionScript in the same way that you can manipulate other visual content
such as images, animation, text, and so on.
When you create a Flash Video (FLV) file in Adobe Flash CS3 Professional, you have the
option to select a skin for your video including common playback controls. However, there is
no reason you need to limit yourself to the options available. Using ActionScript, you have
fine-tuned control over loading, displaying, and controlling playback of video—meaning you
could create your own video player skin, or use your video in any less traditional way that you
want.
Working with video in ActionScript involves working with a combination of several classes:
■ Video class: The actual video content box on the Stage is an instance of the Video class.
The Video class is a display object, so it can be manipulated using the same techniques
that can be applied to other display objects, such as positioning, applying transformations,
applying filters and blending modes, and so forth.
■ NetStream class: When you’re loading a video file to be controlled by ActionScript, a
NetStream instance is used to represent the source of the video content—in this case, a
stream of video data. Using a NetStream instance also involves using a NetConnection
object, which is the connection to the video file—like the tunnel that the video data is fed
through.
■ Camera class: When you’re working with video data from a camera connected to the user’s
computer, a Camera instance represents the source of the video content—the user’s
camera and the video data it makes available.
When you’re loading external video, you can load the file from a standard web server for
progressive download playback, or you can work with streaming video delivered by a
specialized server such as Adobe’s Macromedia® Flash® Media Server.
Basics of video 557
Common video tasks
This chapter describes the following video-related tasks that you will likely want to perform:
■ Displaying and controlling video on the screen
■ Loading external FLV files
■ Handling metadata and cue point information in a video file
■ Capturing and displaying video input from a user’s camera
Important concepts and terms
■ Cue point: A marker that can be placed at a specific moment in time in a video file, for
example to act like a bookmark for locating that point in time, or to provide additional
data that is associated with that moment in time.
■ Encoding: The process of taking video data in one format and converting it to another
video data format; for example, taking a high resolution source video and converting it to
a format that’s suitable for Internet delivery.
■ Frame: A single segment of video information; each frame is like a still image representing
a snapshot of a moment in time. By playing frames in sequence at high speed, the illusion
of motion is created.
■ Keyframe: A video frame which contains the full information for the frame. Other frames
that follow a keyframe only contain information about how they differ from the keyframe,
rather than containing the full frame’s worth of information.
■ Metadata: Information about a video file that can be embedded within the video file and
retrieved when the video has loaded.
■ Progressive download: When a video file is delivered from a standard web server, the video
data is loaded using progressive download, meaning the video information loads in
sequence. This has the benefit that the video can begin playing before the entire file is
downloaded; however, it prevents you from jumping ahead to a part of the video that
hasn’t loaded.
■ Streaming: As an alternative to progressive download, a special video server can be used to
deliver video over the Internet using a technique known as streaming (sometimes called
“true streaming”). With streaming, the viewer’s computer never downloads the entire
video at one time. To speed up download times, at any moment the computer only needs
a portion of the total video information. Because a special server controls the delivery of
the video content, any part of the video can be accessed at any time, rather than needing
to wait for it to download before accessing it.
558 Working with video
Working through in-chapter examples
As you’re working through the chapter, you may want to test some of the example code
listings for yourself. Because this chapter is about working with video in ActionScript, many
of the code listings in this chapter involve working with a video object, which could be one
that has been created and placed on the Stage in Flash authoring, or one that’s created using
ActionScript. Testing a sample will involve viewing the result in Flash Player to see the effects
of the code on the video.
Most of the example code listings manipulate a Video object without creating the object
explicitly. To test these code listings in this chapter:
1. Create an empty Flash document.
2. Select a keyframe in the Timeline.
3. Open the Actions panel and copy the code listing into the Script pane.
4. If necessary, open the Library panel.
5. From the Library panel menu, choose New Video.
6. In the Video Properties dialog box, enter a name for the new video symbol, and choose
Video (ActionScript-controlled) in the Type field. Click OK to create the Video symbol.
7. Drag an instance of your video symbol from the Library panel onto the Stage.
8. With the video instance selected, in the Property inspector, give it an instance name. The
name should match the name used for the Video instance in the example code listing—for
example, if the code listing manipulates a Video object named vid, you should name your
Stage instance vid as well.
9. Run the program using Control > Test Movie.
On the screen you will see the results of the code manipulating the video as specified in
the code listing.
Some example code listings in this chapter include a class definition in addition to the
example code. In those listings, in addition to the previous steps, and before testing the SWF,
you will need to create the class that’s used in the example. To create a class defined in an
example code listing:
1. Make sure you have saved the FLA file that will be used for testing.
2. From the main menu choose File > New.
3. In the New Document dialog, in the Type section, choose ActionScript File. Click OK to
create the new ActionScript file.
4. Copy the class definition code from the example into the ActionScript document.
Understanding the Flash Video (FLV) format 559
5. From the main menu, choose File > Save. Save the file in the same directory as the Flash
document. The file’s name should match the name of the class in the code listing. For
instance, if the code listing defines a class named “VideoTest,” save the ActionScript file as
“VideoTest.as”.
6. Return to the Flash document.
7. Run the program using Control > Test Movie.
You will see the results of the example displayed on the screen.
Other techniques for testing example code listings are explained in more detail in “Testing in-
chapter example code listings” on page 63.
Understanding the Flash Video (FLV)
format
The FLV file format contains encoded audio and video data for delivery by using Flash Player.
For example, if you have a QuickTime or Windows Media video file, you use an encoder
(such as Flash Video Encoder, or Sorenson™ Squeeze) to convert that file to an FLV file.
You can create FLV files by importing video into the Flash authoring tool and exporting it as
an FLV file. You can use the FLV Export plug-in to export FLV files from supported video-
editing applications.
Using external FLV files provides certain capabilities that are not available when you use
imported video:
■ Longer video clips can be used in your Flash documents without slowing down playback.
External FLV files play using cached memory, which means that large files are stored in
small pieces and accessed dynamically, requiring less memory than embedded video files.
■ An external FLV file can have a different frame rate than the Flash document in which it
plays. For example, you can set the Flash document frame rate to 30 frames per second
(fps) and the video frame rate to 21 fps. This setting gives you better control of the video
than embedded video, to ensure smooth video playback. It also allows you to play FLV
files at different frame rates without the need to alter existing Flash content.
560 Working with video
■ With external FLV files, Flash document playback does not have to be interrupted while
the video file is loading. Imported video files can sometimes interrupt document playback
to perform certain functions, such as accessing a CD-ROM drive. FLV files can perform
functions independently of the Flash document, which does not interrupt playback.
■ Captioning video content is easier with external FLV files because you can access the
video’s metadata using event handlers.
Understanding the Video class
The Video class enables you to display live streaming video in an application without
embedding it in your SWF file. You can capture and play live video using the
Camera.getCamera() method. You can also use the Video class to play back FLV files over
HTTP or from the local file system. There are several different ways to use Video in your
projects:
■ Load an FLV dynamically using the NetConnection and NetStream classes and display
the video in a Video object.
■ Capture input from the user’s camera.
■ Use the FLVPlayback component.
Even though the Video class is in the flash.media package, it inherits from the
flash.display.DisplayObject class, therefore all display object functionality (such as matrix
transformations and filters) also applies to Video instances.
For more information see “Manipulating display objects” on page 395, “Working with
geometry” on page 431, and “Filtering display objects” on page 465.
TIP
To load FLV files from a web server, you might need to register the file extension and
MIME type with your web server; check your web server documentation. The MIME
type for FLV files is video/x-flv. For more information, see “About configuring FLV
files for hosting on a server” on page 585.
NOTE
Instances of a Video object on the Stage are instances of the Video class.
Loading video files 561
Loading video files
Loading videos using the NetStream and NetConnection classes is a multistep process:
1. The first step is to create a NetConnection object. The NetConnection class lets you play
streaming FLV files from either an HTTP address or a local drive by passing the value null
to the connect() method, if you are connecting to a local FLV file that is not using a server
such as Adobe's Flash Media Server 2 or Adobe Flex.
var nc:NetConnection = new NetConnection();
nc.connect(null);
2. The second step is to create a NetStream object which takes a NetConnection object as a
parameter and specify which FLV file you want to load. The following snippet connects a
NetStream object to the specified NetConnection instance and loads an FLV named
video.flv in the same directory as the SWF file:
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
ns.play("video.flv");
function asyncErrorHandler(event:AsyncErrorEvent):void
{
// ignore error
}
3. The third step is to create a new Video object and attach the previously created NetStream
object using the Video class’s attachNetStream() method. Then you can add the video
object to the display list using the addChild() method, as seen in the following snippet:
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
After entering the previous code, Flash Player will attempt to load the video.flv video file in
the same directory as your SWF file.
TIP
To load FLV files from a web server, you might need to register the file extension and
MIME type with your web server; check your web server documentation. The MIME type
for FLV files is video/x-flv. For more information, see “About configuring FLV files for
hosting on a server” on page 585.
562 Working with video
Controlling video playback
The NetStream class offers four main methods for controlling video playback:
pause(): Pauses playback of a video stream. If the video is already paused, calling this
method does nothing.
resume(): Resumes playback of a video stream that is paused. If the video is already
playing, calling this method does nothing.
seek(): Seeks the keyframe closest to the specified location (an offset, in seconds, from
the beginning of the stream).
togglePause(): Pauses or resumes playback of a stream.
The following example demonstrates how to control a video using several different buttons.
To run the following example, create a new document and add four button instances to your
workspace (pauseBtn, playBtn, stopBtn, and togglePauseBtn):
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
ns.play("video.flv");
function asyncErrorHandler(event:AsyncErrorEvent):void
{
// ignore error
}
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
pauseBtn.addEventListener(MouseEvent.CLICK, pauseHandler);
playBtn.addEventListener(MouseEvent.CLICK, playHandler);
stopBtn.addEventListener(MouseEvent.CLICK, stopHandler);
togglePauseBtn.addEventListener(MouseEvent.CLICK, togglePauseHandler);
function pauseHandler(event:MouseEvent):void
{
ns.pause();
}
NOTE
There is no stop() method. In order to stop a stream you must pause playback and seek to the
beginning of the video stream.
NOTE
The play() method does not resume playback, it is used for loading video files.
Controlling video playback 563
function playHandler(event:MouseEvent):void
{
ns.resume();
}
function stopHandler(event:MouseEvent):void
{
// Pause the stream and move the playhead back to
// the beginning of the stream.
ns.pause();
ns.seek(0);
}
function togglePauseHandler(event:MouseEvent):void
{
ns.togglePause();
}
Clicking on the pauseBtn button instance while the video is playing causes the video file to
pause. If the video is already paused, clicking this button has no effect. Clicking on the
playBtn button instance resumes video playback if playback was previously paused, otherwise
the button has no effect if the video was already playing.
Detecting the end of a video stream
In order to listen for the beginning and end of a video stream, you need to add an event
listener to the NetStream instance to listen for the netStatus event. The following code
demonstrates how to listen for the various codes throughout the video’s playback:
ns.addEventListener(NetStatusEvent.NET_STATUS, statusHandler);
function statusHandler(event:NetStatusEvent):void
{
trace(event.info.code)
}
The previous code generates the following output:
NetStream.Play.Start
NetStream.Buffer.Empty
NetStream.Buffer.Full
NetStream.Buffer.Empty
NetStream.Buffer.Full
NetStream.Buffer.Empty
NetStream.Buffer.Full
NetStream.Buffer.Flush
NetStream.Play.Stop
NetStream.Buffer.Empty
NetStream.Buffer.Flush
564 Working with video
The two codes that you want to specifically listen for are “NetStream.Play.Start” and
“NetStream.Play.Stop” which signal the beginning and end of the video’s playback. The
following snippet uses a switch statement to filter these two codes and trace a message:
function statusHandler(event:NetStatusEvent):void
{
switch (event.info.code)
{
case "NetStream.Play.Start":
trace("Start [" + ns.time.toFixed(3) + " seconds]");
break;
case "NetStream.Play.Stop":
trace("Stop [" + ns.time.toFixed(3) + " seconds]");
break;
}
}
By listening for the netStatus event (NetStatusEvent.NET_STATUS), you can build a video
player which loads the next video in a playlist once the current video has finished playing.
Streaming video files
To steam files from Flash Media Server, you can use the NetConnection and NetStream
classes to connect to a remote server instance and play a specified stream. To specify a Real-
Time Messaging Protocal (RTMP) server, you pass the desired RTMP URL, such as “rtmp://
localhost/appName/appInstance”, to the NetConnection.connect() method instead of
passing null. To play a specific live or recorded stream from the specified Flash Media Server,
you pass an identifying name for live data published by NetStream.publish(), or a recorded
filename for playback to the NetStream.play() method. For more information, see the Flash
Media Server documentation.
Understanding cue points 565
Understanding cue points
Not all FLV files contain cue points. Cue points are typically embedded in an FLV file during
FLV encoding, although tools exist for embedding cue points in existing FLV files.
You can use several different kinds of cue points with Flash Video. You can use ActionScript
to interact with cue points that you embed in an FLV file (when you create the FLV file), or
that you create by using ActionScript.
■ Navigation cue points: You embed navigation cue points in the FLV stream and FLV
metadata packet when you encode the FLV file. You use navigation cue points to let users
seek to a specified part of a file.
■ Event cue points: You embed event cue points in the FLV stream and FLV metadata
packet when you encode the FLV file. You can write code to handle the events that are
triggered at specified points during FLV playback.
■ ActionScript cue points: External cue points that you create by using ActionScript code.
You can write code to trigger these cue points in relation to the video's playback. These
cue points are less accurate than embedded cue points (up to a tenth of a second), because
the video player tracks them separately.
Navigation cue points create a keyframe at the specified cue point location, so you can use
code to move a video player's playhead to that location. You can set particular points in an
FLV file where you might want users to seek. For example, your video might have multiple
chapters or segments, and you can control the video by embedding navigation cue points in
the video file.
If you plan to create an application in which you want users to navigate to a cue point, you
should create and embed cue points when you encode the file instead of using ActionScript
cue points. You should embed the cue points in the FLV file, because they are more accurate
to work with. For more information on encoding FLV files with cue points, see “Embed cue
points” in Using Flash.
You can access cue point parameters by writing ActionScript. Cue point parameters are a part
of the event object received from the onCuePoint callback handler.
To trigger certain actions in your code when a video reaches a specific cue point, you use the
NetStream.onCuePoint event handler. For more information, see “Writing callback methods
for onCuePoint and onMetaData” on page 566.
566 Working with video
Writing callback methods for onCuePoint
and onMetaData
You can trigger actions in your application when specific cue points are reached or specific
metadata is received by the player. To trigger such actions, you use the onCuePoint and
onMetaData event handlers. You must write callback methods for these handlers, or Flash
Player may throw errors. For example, the following code plays an FLV file named video.flv in
the same folder as your SWF document:
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
ns.play("video.flv");
function asyncErrorHandler(event:AsyncErrorEvent):void
{
trace(event.text);
}
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
The previous code loads a local FLV file named video.flv and listens for the asyncError
(AsyncErrorEvent.ASYNC_ERROR) to be dispatched. This event is dispatched when an
exception is thrown from native asynchronous code. In this case, it is dispatched when an FLV
contains metadata or cue point information, and the appropriate listeners have not been
defined. The previous code handles the asyncError event and ignores the error if you are not
interested in the video file’s metadata or cue point information. If you had an FLV with
metadata and several cue points, the following information would be traced:
Error #2095: flash.net.NetStream was unable to invoke callback onMetaData.
Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint.
Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint.
Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint.
Writing callback methods for onCuePoint and onMetaData 567
The error occurs because the NetStream object was unable to find an onMetaData or
onCuePoint callback method. There are several ways to define these callback methods within
your applications:
■ Set the NetStream object’s client property to an Object
■ Create a custom class and define methods to handle the callback methods
■ Extend the NetStream class and add methods to handle the callback methods
■ Extend the NetStream class and make it dynamic
■ Set the NetStream object’s client property to this
Set the NetStream object’s client property to an
Object
By setting the client property to either an Object or a subclass of NetStream, you can
reroute the onMetaData and onCuePoint callback methods or ignore them completely. The
following example demonstrates how you can use an empty Object to ignore the callback
methods without listening for the asyncError event:
var nc:NetConnection = new NetConnection();
nc.connect(null);
var customClient:Object = new Object();
var ns:NetStream = new NetStream(nc);
ns.client = customClient;
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
If you wanted to listen for either the onMetaData or onCuePoint callback methods, you
would need to define methods to handle those callback methods, as shown in the following
snippet:
var customClient:Object = new Object();
customClient.onMetaData = metaDataHandler;
function metaDataHandler(infoObject:Object):void
{
trace("metadata");
}
568 Working with video
The previous code listens for the onMetaData callback method and calls the
metaDataHandler() method, which traces a string. If Flash Player encountered a cue point,
no errors would be generated even though no onCuePoint callback method is defined.
Create a custom class and define methods to handle
the callback methods
The following code sets the NetStream object’s client property to a custom class,
CustomClient, which defines handlers for the callback methods:
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.client = new CustomClient();
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
The CustomClient class is as follows:
package
{
public class CustomClient
{
public function onMetaData(infoObject:Object):void
{
trace("metadata");
}
}
}
The CustomClient class defines a handler for the onMetaData callback handler. If a cue point
was encountered and the onCuePoint callback handler was called, an asyncError event
(AsyncErrorEvent.ASYNC_ERROR) would be dispatched saying “flash.net.NetStream was
unable to invoke callback onCuePoint.” To prevent this error, you would either need to define
an onCuePoint callback method in your CustomClient class, or define an event handler for
the asyncError event.
Writing callback methods for onCuePoint and onMetaData 569
Extend the NetStream class and add methods to
handle the callback methods
The following code creates an instance of the CustomNetStream class, which is defined in a
later code listing:
var ns:CustomNetStream = new CustomNetStream();
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
The following code listing defines the CustomNetStream class that extends the NetStream
class, handles the creation of the necessary NetConnection object, and handles the
onMetaData and onCuePoint callback handler methods:
package
{
import flash.net.NetConnection;
import flash.net.NetStream;
public class CustomNetStream extends NetStream
{
private var nc:NetConnection;
public function CustomNetStream()
{
nc = new NetConnection();
nc.connect(null);
super(nc);
}
public function onMetaData(infoObject:Object):void
{
trace("metadata");
}
public function onCuePoint(infoObject:Object):void
{
trace("cue point");
}
}
}
570 Working with video
If you want to rename the onMetaData() and onCuePoint() methods in the
CustomNetStream class, you could use the following code:
package
{
import flash.net.NetConnection;
import flash.net.NetStream;
public class CustomNetStream extends NetStream
{
private var nc:NetConnection;
public var onMetaData:Function;
public var onCuePoint:Function;
public function CustomNetStream()
{
onMetaData = metaDataHandler;
onCuePoint = cuePointHandler;
nc = new NetConnection();
nc.connect(null);
super(nc);
}
private function metaDataHandler(infoObject:Object):void
{
trace("metadata");
}
private function cuePointHandler(infoObject:Object):void
{
trace("cue point");
}
}
}
Extend the NetStream class and make it dynamic
You can extend the NetStream class and make the subclass dynamic so that onCuePoint and
onMetaData callback handlers can be added dynamically. This is demonstrated in the
following listing:
var ns:DynamicCustomNetStream = new DynamicCustomNetStream();
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
Writing callback methods for onCuePoint and onMetaData 571
The DynamicCustomNetStream class is as follows:
package
{
import flash.net.NetConnection;
import flash.net.NetStream;
public dynamic class DynamicCustomNetStream extends NetStream
{
private var nc:NetConnection;
public function DynamicCustomNetStream()
{
nc = new NetConnection();
nc.connect(null);
super(nc);
}
}
}
Even with no handlers for the onMetaData and onCuePoint callback handlers, no errors are
thrown since the DynamicCustomNetStream class is dynamic. If you want to define methods
for the onMetaData and onCuePoint callback handlers, you could use the following code:
var ns:DynamicCustomNetStream = new DynamicCustomNetStream();
ns.onMetaData = metaDataHandler;
ns.onCuePoint = cuePointHandler;
ns.play("http://www.helpexamples.com/flash/video/cuepoints.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
function metaDataHandler(infoObject:Object):void
{
trace("metadata");
}
function cuePointHandler(infoObject:Object):void
{
trace("cue point");
}
572 Working with video
Set the NetStream object’s client property to this
By setting the client property to this, Flash Player looks in the current scope for
onMetaData() and onCuePoint() methods. You can see this in the following example:
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.client = this;
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
If the onMetaData or onCuePoint callback handlers are called and no methods exist to handle
the callback, no errors are generated. To handle these callback handlers, create an
onMetaData() and onCuePoint() method in your code, as seen in the following snippet:
function onMetaData(infoObject:Object):void
{
trace("metadata");
}
function onCuePoint(infoObject:Object):void
{
trace("cue point");
}
Using cue points
The following example uses a simple for..in loop to iterate over each of the properties in the
onCuePoint callback handler’s infoObject parameter and trace a message when cue point
data is received:
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.client = this;
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
Using video metadata 573
function onCuePoint(infoObject:Object):void
{
var key:String;
for (key in infoObject)
{
trace(key + ": " + infoObject[key]);
}
}
The following output appears:
parameters:
name: point1
time: 0.418
type: navigation
This code used one of several techniques to set the object on which the callback method was
invoked. You can use other techniques; for more information, see Writing callback methods
for onCuePoint and onMetaData.
Using video metadata
You can use the onMetaData callback handler to view the metadata information in your FLV
file. Metadata includes information about your FLV file, such as duration, width, height, and
frame rate. The metadata information that is added to your FLV file depends on the software
you use to encode your FLV file or the software you use to add metadata information.
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
ns.client = this;
ns.play("video.flv");
var vid:Video = new Video();
vid.attachNetStream(ns);
addChild(vid);
function onMetaData(infoObject:Object):void
{
var key:String;
for (key in infoObject)
{
trace(key + ": " + infoObject[key]);
}
}
574 Working with video
The previous code generates code similar to the following, assuming your FLV file contains
cue points and audio:
width: 320
audiodelay: 0.038
canSeekToEnd: true
height: 213
cuePoints: ,,
audiodatarate: 96
duration: 16.334
videodatarate: 400
framerate: 15
videocodecid: 4
audiocodecid: 2
In the previous code, the cue point information was not displaying. In order to display the cue
point metadata, you can use the following function which recursively displays the items in an
Object:
function traceObject(obj:Object, indent:uint = 0):void
{
var indentString:String = "";
var i:uint;
var prop:String;
var val:*;
for (i = 0; i < indent; i++)
{
indentString += "t";
}
for (prop in obj)
{
val = obj[prop];
if (typeof(val) == "object")
{
trace(indentString + " " + j + ": [Object]");
traceObject(val, indent + 1);
}
else
{
trace(indentString + " " + prop + ": " + val);
}
}
}
TIP
If your video does not have audio, the audio-related metadata information (such as
audiodat
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming
Adobe   action script 3.0 programming

More Related Content

What's hot (18)

PDF
Acro js guide
TAHIRNASIR12
 
PDF
Photoshop 6 classroom in a book
tamuna01
 
PDF
Photoshop cs5 help
Agung Yuwono
 
PDF
Corel vs pro_x4_ug modul corel draw
DIANTO IRAWAN
 
PDF
Maya 2010 Getting Started
codewarrior congrejo
 
PDF
Gettingstartedmaya2010 A1pdf
guest43cd94a
 
PDF
Ppm7.5 cmd tokval
indiatechnopark
 
PDF
Xi3 voyager userguide_en
Anil Damara
 
PDF
Learn python the right way
DianaLaCruz2
 
PDF
Ppm7.5 demand cg
indiatechnopark
 
PDF
W java81
rasikow
 
PDF
Artromick Ac Hostguide304 for Hospital Computing Solutions
Artromick
 
PDF
tutorial poser
massuroto
 
PDF
10. cutipa portillo, edy dany
IESTPTECNOTRONIC
 
PDF
Excel vba visual basic (korol) (1)
Liceth777
 
PDF
Zambak it excel2010
Nazrin Afandiyeva
 
PDF
Hp man ppm9.20_whats_new_pdf
ugunal
 
PDF
Corel draw learning file
Baig Group of Industries
 
Acro js guide
TAHIRNASIR12
 
Photoshop 6 classroom in a book
tamuna01
 
Photoshop cs5 help
Agung Yuwono
 
Corel vs pro_x4_ug modul corel draw
DIANTO IRAWAN
 
Maya 2010 Getting Started
codewarrior congrejo
 
Gettingstartedmaya2010 A1pdf
guest43cd94a
 
Ppm7.5 cmd tokval
indiatechnopark
 
Xi3 voyager userguide_en
Anil Damara
 
Learn python the right way
DianaLaCruz2
 
Ppm7.5 demand cg
indiatechnopark
 
W java81
rasikow
 
Artromick Ac Hostguide304 for Hospital Computing Solutions
Artromick
 
tutorial poser
massuroto
 
10. cutipa portillo, edy dany
IESTPTECNOTRONIC
 
Excel vba visual basic (korol) (1)
Liceth777
 
Zambak it excel2010
Nazrin Afandiyeva
 
Hp man ppm9.20_whats_new_pdf
ugunal
 
Corel draw learning file
Baig Group of Industries
 

Similar to Adobe action script 3.0 programming (20)

PDF
C sharp programming
singhadarsh
 
PDF
C sharp programming[1]
Abdul Rasheed Feroz Khan
 
PDF
Flash File Format Specification
guest0ebe1e
 
PDF
Ebay News 1998 10 27 Earnings
QuarterlyEarningsReports
 
PDF
In designcs5 scripting tutorial
Mustfeez Rasul
 
PDF
Java script tools guide cs6
Sadiq Momin
 
PDF
Programming Flex 2 Chafic Kazoun Joey Lott
mogilciprawq
 
PDF
Ibm enterprise it and asset management 7.1 java for customization training
Hal Izi
 
PPT
ActionScript 3.0 Fundamentals
Saurabh Narula
 
PDF
File Format Specification V9
guest0ebe1e
 
PDF
PythonIntro
webuploader
 
PDF
Introduction to Computer Science Using Python and Pygame
Daniel Francisco Tamayo
 
PDF
Adobe Flex 3 - Compiler API
eugeneyh
 
PDF
Code Conventions
provadaordem
 
PDF
Perltut
Ashoka Vanjare
 
PDF
EloquenFundamentalsof Web Developmentt_JavaScript.pdf
kasperkey106
 
PDF
Applications Developer 11.5.10
Hossam El-Faxe
 
PDF
Learn c++
aashay1996
 
PDF
Flash coding convention for action script 3
Tan Tran
 
PDF
AcroJS
tutorialsruby
 
C sharp programming
singhadarsh
 
C sharp programming[1]
Abdul Rasheed Feroz Khan
 
Flash File Format Specification
guest0ebe1e
 
Ebay News 1998 10 27 Earnings
QuarterlyEarningsReports
 
In designcs5 scripting tutorial
Mustfeez Rasul
 
Java script tools guide cs6
Sadiq Momin
 
Programming Flex 2 Chafic Kazoun Joey Lott
mogilciprawq
 
Ibm enterprise it and asset management 7.1 java for customization training
Hal Izi
 
ActionScript 3.0 Fundamentals
Saurabh Narula
 
File Format Specification V9
guest0ebe1e
 
PythonIntro
webuploader
 
Introduction to Computer Science Using Python and Pygame
Daniel Francisco Tamayo
 
Adobe Flex 3 - Compiler API
eugeneyh
 
Code Conventions
provadaordem
 
EloquenFundamentalsof Web Developmentt_JavaScript.pdf
kasperkey106
 
Applications Developer 11.5.10
Hossam El-Faxe
 
Learn c++
aashay1996
 
Flash coding convention for action script 3
Tan Tran
 
Ad

Recently uploaded (20)

PPTX
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
PDF
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PDF
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PPTX
Designing_the_Future_AI_Driven_Product_Experiences_Across_Devices.pptx
presentifyai
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PDF
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
PDF
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
PDF
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
PPTX
Digital Circuits, important subject in CS
contactparinay1
 
PDF
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PPTX
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
PPT
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
PDF
ICONIQ State of AI Report 2025 - The Builder's Playbook
Razin Mustafiz
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Designing_the_Future_AI_Driven_Product_Experiences_Across_Devices.pptx
presentifyai
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
Digital Circuits, important subject in CS
contactparinay1
 
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Agentforce World Tour Toronto '25 - Supercharge MuleSoft Development with Mod...
Alexandra N. Martinez
 
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
ICONIQ State of AI Report 2025 - The Builder's Playbook
Razin Mustafiz
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
Ad

Adobe action script 3.0 programming

  • 2. © 2007 Adobe Systems Incorporated. All rights reserved. Programming ActionScript™ 3.0 If this guide is distributed with software that includes an end-user agreement, this guide, as well as the software described in it, is furnished under license and may be used or copied only in accordance with the terms of such license. Except as permitted by any such license, no part of this guide may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, recording, or otherwise, without the prior written permission of Adobe Systems Incorporated. Please note that the content in this guide is protected under copyright law even if it is not distributed with software that includes an end-user license agreement. The content of this guide is furnished for informational use only, is subject to change without notice, and should not be construed as a commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsibility or liability for any errors or inaccuracies that may appear in the informational content contained in this guide. Please remember that existing artwork or images that you may want to include in your project may be protected under copyright law. The unauthorized incorporation of such material into your new work could be a violation of the rights of the copyright owner. Please be sure to obtain any permission required from the copyright owner. Any references to company names in sample templates are for demonstration purposes only and are not intended to refer to any actual organization. Adobe, the Adobe logo, Flex, Flex Builder and Flash Player are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries. ActiveX and Windows are either registered trademarks or trademarks of Microsoft Corporation in the United States and other countries. Macintosh is a trademark of Apple Inc., registered in the United States and other countries. All other trademarks are the property of their respective owners. Speech compression and decompression technology licensed from Nellymoser, Inc. (www.nellymoser.com). Sorenson™ Spark™ video compression and decompression technology licensed from Sorenson Media, Inc. Opera ® browser Copyright © 1995-2002 Opera Software ASA and its suppliers. All rights reserved. Adobe Systems Incorporated, 345 Park Avenue, San Jose, California 95110, USA Notice to U.S. government end users. The software and documentation are “Commercial Items,” as that term is defined at 48 C.F.R. §2.101, consisting of “Commercial Computer Software” and “Commercial Computer Software Documentation,” as such terms are used in 48 C.F.R. §12.212 or 48 C.F.R. §227.7202, as applicable. Consistent with 48 C.F.R. §12.212 or 48 C.F.R. §§227.7202-1 through 227.7202-4, as applicable, the Commercial Computer Software and Commercial Computer Software Documentation are being licensed to U.S. Government end users (a) only as Commercial items and (b) with only those rights as are granted to all other end users pursuant to the terms and conditions herein. Unpublished-rights reserved under the copyright laws of the United States. Adobe Systems Incorporated, 345 Park Avenue, San Jose, CA 95110-2704, USA. For U.S. Government End Users, Adobe agrees to comply with all applicable equal opportunity laws including, if appropriate, the provisions of Executive Order 11246, as amended, Section 402 of the Vietnam Era Veterans Readjustment Assistance Act of 1974 (38 USC 4212), and Section 503 of the Rehabilitation Act of 1973, as amended, and the regulations at 41 CFR Parts 60-1 through 60-60, 60-250 ,and 60-741. The affirmative action clause and regulations contained in the preceding sentence shall be incorporated by reference.
  • 3. 3 Contents About this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Using this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Accessing ActionScript documentation . . . . . . . . . . . . . . . . . . . . . . . . . . 15 ActionScript learning resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Chapter 1: Introduction to ActionScript 3.0 . . . . . . . . . . . . . . . . . . 19 About ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Advantages of ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 What’s new in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Core language features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Flash Player API features. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Compatibility with previous versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Chapter 2: Getting started with ActionScript. . . . . . . . . . . . . . . . .27 Programming fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27 What computer programs do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27 Variables and constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Working with objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Basic event handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Examining the event-handling process . . . . . . . . . . . . . . . . . . . . . 35 Event-handling examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Creating object instances. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Common program elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Example: Animation portfolio piece . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Building applications with ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . .47 Options for organizing your code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Choosing the right tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 The ActionScript development process . . . . . . . . . . . . . . . . . . . . . . . . 51 Creating your own classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Strategies for designing a class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Writing the code for a class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
  • 4. 4 Suggestions for organizing your classes . . . . . . . . . . . . . . . . . . . . . . . 56 Example: Creating a basic application . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Running subsequent examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Chapter 3: ActionScript language and syntax . . . . . . . . . . . . . . . . 67 Language overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Objects and classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Packages and namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Dynamic classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Data type descriptions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Type conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .119 Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Basic function concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Function parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Functions as objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Function scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Chapter 4: Object-oriented programming in ActionScript. . . . . 137 Basics of object-oriented programming. . . . . . . . . . . . . . . . . . . . . . . . . 138 Classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Class definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .141 Class property attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Enumerations with classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 Embedded asset classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Inheritance. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Advanced topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Example: GeometricShapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
  • 5. 5 Chapter 5: Working with dates and times. . . . . . . . . . . . . . . . . . . 191 Basics of dates and times. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Managing calendar dates and times . . . . . . . . . . . . . . . . . . . . . . . . . . . .193 Controlling time intervals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .196 Example: Simple analog clock. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .199 Chapter 6: Working with strings . . . . . . . . . . . . . . . . . . . . . . . . . 203 Basics of strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Creating strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 The length property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Working with characters in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Comparing strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Obtaining string representations of other objects. . . . . . . . . . . . . . . . 209 Concatenating strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Finding substrings and patterns in strings . . . . . . . . . . . . . . . . . . . . . . .210 Converting strings between uppercase and lowercase . . . . . . . . . . . .215 Example: ASCII art. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .216 Chapter 7: Working with arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Basics of arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Indexed arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 Associative arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Multidimensional arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 Cloning arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241 Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241 Example: PlayList. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247 Chapter 8: Handling errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 Basics of error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Types of errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 Error handling in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 ActionScript 3.0 error-handling elements . . . . . . . . . . . . . . . . . . . . 260 Error-handling strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .261 Working with the debugger version of Flash Player . . . . . . . . . . . . . . .261 Handling synchronous errors in an application . . . . . . . . . . . . . . . . . . 262 Creating custom error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 Responding to error events and status . . . . . . . . . . . . . . . . . . . . . . . . . 268
  • 6. 6 Comparing the Error classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272 ECMAScript core Error classes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272 ActionScript core Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .275 flash.error package Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . .276 Example: CustomErrors application . . . . . . . . . . . . . . . . . . . . . . . . . . . .278 Chapter 9: Using regular expressions. . . . . . . . . . . . . . . . . . . . . 285 Basics of regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .286 Regular expression syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .288 Creating an instance of a regular expression . . . . . . . . . . . . . . . . . .289 Characters, metacharacters, and metasequences . . . . . . . . . . . . 290 Character classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .293 Quantifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 Alternation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297 Groups. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .297 Flags and properties. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Methods for using regular expressions with strings. . . . . . . . . . . . . . 305 Example: A Wiki parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 Chapter 10: Handling events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 Basics of handling events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 How ActionScript 3.0 event handling differs from earlier versions . 317 The event flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Event objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322 Event listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327 Example: Alarm Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .335 Chapter 11: Working with XML. . . . . . . . . . . . . . . . . . . . . . . . . . . 343 Basics of XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 The E4X approach to XML processing . . . . . . . . . . . . . . . . . . . . . . . . .348 XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 XMLList objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .353 Initializing XML variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .354 Assembling and transforming XML objects. . . . . . . . . . . . . . . . . . . . . .356 Traversing XML structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358 Using XML namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .363 XML type conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364 Reading external XML documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . .366 Example: Loading RSS data from the Internet. . . . . . . . . . . . . . . . . . .367
  • 7. 7 Chapter 12: Display programming . . . . . . . . . . . . . . . . . . . . . . . . . 371 Basics of display programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 Core display classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 Advantages of the display list approach . . . . . . . . . . . . . . . . . . . . . . . . 379 Working with display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382 Properties and methods of the DisplayObject class . . . . . . . . . . . 382 Adding display objects to the display list . . . . . . . . . . . . . . . . . . . . . 383 Working with display object containers . . . . . . . . . . . . . . . . . . . . . . 383 Traversing the display list. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 Setting Stage properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 Handling events for display objects . . . . . . . . . . . . . . . . . . . . . . . . . . 392 Choosing a DisplayObject subclass. . . . . . . . . . . . . . . . . . . . . . . . . . 393 Manipulating display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Changing position . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Panning and scrolling display objects . . . . . . . . . . . . . . . . . . . . . . . . 400 Manipulating size and scaling objects. . . . . . . . . . . . . . . . . . . . . . . . 402 Controlling distortion when scaling . . . . . . . . . . . . . . . . . . . . . . . . 403 Caching display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405 When to enable caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406 Enabling bitmap caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 Setting an opaque background color . . . . . . . . . . . . . . . . . . . . . . 409 Applying blending modes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409 Adjusting DisplayObject colors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .410 Setting color values with code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 Altering color and brightness effects with code . . . . . . . . . . . . . .412 Rotating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413 Fading objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .413 Masking display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .414 Animating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .416 Loading display content dynamically. . . . . . . . . . . . . . . . . . . . . . . . . . . .418 Loading display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .419 Monitoring loading progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420 Specifying loading context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .421 Example: SpriteArranger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423 Chapter 13: Working with geometry . . . . . . . . . . . . . . . . . . . . . . .431 Basics of geometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .431 Using Point objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 Using Rectangle objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Using Matrix objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 Example: Applying a matrix transformation to a display object . . . . 442
  • 8. 8 Chapter 14: Using the drawing API . . . . . . . . . . . . . . . . . . . . . . . 447 Basics of using the drawing API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448 Understanding the Graphics class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Drawing lines and curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Drawing shapes using built-in methods . . . . . . . . . . . . . . . . . . . . . . . . .453 Creating gradient lines and fills. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454 Using the Math class with drawing methods . . . . . . . . . . . . . . . . . . . . 460 Animating with the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 Example: Algorithmic Visual Generator . . . . . . . . . . . . . . . . . . . . . . . . .462 Chapter 15: Filtering display objects . . . . . . . . . . . . . . . . . . . . . . 465 Basics of filtering display objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465 Creating and applying filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467 Creating a new filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467 Applying a filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467 How filters work. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .470 Potential issues for working with filters . . . . . . . . . . . . . . . . . . . . . . .470 Available display filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472 Bevel filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .473 Blur filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .474 Drop shadow filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .474 Glow filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .475 Gradient bevel filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .476 Gradient glow filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477 Example: Combining basic filters . . . . . . . . . . . . . . . . . . . . . . . . . . . .478 Color matrix filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480 Convolution filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481 Displacement map filter. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .484 Example: Filter Workbench. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490 Chapter 16: Working with movie clips. . . . . . . . . . . . . . . . . . . . . . 491 Basics of movie clips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 Working with MovieClip objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 Controlling movie clip playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 Working with scenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .497 Creating MovieClip objects with ActionScript . . . . . . . . . . . . . . . . . . 498 Exporting library symbols for ActionScript . . . . . . . . . . . . . . . . . . . 498 Loading an external SWF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501 Example: RuntimeAssetsExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
  • 9. 9 Chapter 17: Working with text . . . . . . . . . . . . . . . . . . . . . . . . . . . 509 Basics of working with text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .510 Displaying text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513 Types of text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .513 Modifying the text field contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . .514 Displaying HTML text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .514 Using images in text fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .515 Scrolling text in a text field. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .516 Selecting and manipulating text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 Capturing text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .518 Restricting text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Formatting text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Assigning text formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521 Applying cascading style sheets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .521 Loading an external CSS file. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523 Formatting ranges of text within a text field. . . . . . . . . . . . . . . . . . . 524 Advanced text rendering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 Working with static text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528 Example: Newspaper-style text formatting . . . . . . . . . . . . . . . . . . . . . 529 Reading the external CSS file. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 Arranging story elements on the page . . . . . . . . . . . . . . . . . . . . . . . 532 Altering font size to fit the field size . . . . . . . . . . . . . . . . . . . . . . . . . . 533 Splitting text across multiple columns. . . . . . . . . . . . . . . . . . . . . . . . 535 Chapter 18: Working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . 539 Basics of working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540 The Bitmap and BitmapData classes . . . . . . . . . . . . . . . . . . . . . . . . . . 543 Manipulating pixels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545 Manipulating individual pixels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545 Pixel-level collision detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547 Copying bitmap data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549 Making textures with noise functions. . . . . . . . . . . . . . . . . . . . . . . . . . . 550 Scrolling bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 Example: Animating sprites using an offscreen bitmap . . . . . . . . . . . 553 Chapter 19: Working with video . . . . . . . . . . . . . . . . . . . . . . . . . . 555 Basics of video . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 Understanding the Flash Video (FLV) format . . . . . . . . . . . . . . . . . . . 559 Understanding the Video class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 Loading video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .561 Controlling video playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562 Detecting the end of a video stream . . . . . . . . . . . . . . . . . . . . . . . . . 563
  • 10. 10 Streaming video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .564 Understanding cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .565 Writing callback methods for onCuePoint and onMetaData . . . . . . .566 Set the NetStream object’s client property to an Object . . . . . . . .567 Create a custom class and define methods to handle the callback methods. . . . . . . . . . . . . . . . . . . . . . . . . . . .568 Extend the NetStream class and add methods to handle the callback methods. . . . . . . . . . . . . . . . . . . . . . . . . . . 569 Extend the NetStream class and make it dynamic . . . . . . . . . . . . .570 Set the NetStream object’s client property to this. . . . . . . . . . . . . .572 Using cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .572 Using video metadata. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .573 Capturing camera input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 Understanding the Camera class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 Displaying camera content on-screen . . . . . . . . . . . . . . . . . . . . . . . .578 Designing your camera application. . . . . . . . . . . . . . . . . . . . . . . . . . .578 Connecting to a user’s camera. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .578 Verifying that cameras are installed . . . . . . . . . . . . . . . . . . . . . . . . . .579 Detecting permissions for camera access . . . . . . . . . . . . . . . . . . . 580 Maximizing video quality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .582 Monitoring playback conditions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .583 Sending video to a server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .584 Advanced topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .585 Flash Player compatibility with encoded FLV files . . . . . . . . . . . . .585 About configuring FLV files for hosting on a server . . . . . . . . . . . .585 About targeting local FLV files on the Macintosh . . . . . . . . . . . . . .586 Example: Video Jukebox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .587 Chapter 20: Working with sound . . . . . . . . . . . . . . . . . . . . . . . . 595 Basics of working with sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 Understanding the sound architecture . . . . . . . . . . . . . . . . . . . . . . . . . 599 Loading external sound files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600 Working with embedded sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603 Working with streaming sound files . . . . . . . . . . . . . . . . . . . . . . . . . . . 604 Playing sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605 Pausing and resuming a sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 Monitoring playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .607 Stopping streaming sounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 Security considerations when loading and playing sounds . . . . . . . 609 Controlling sound volume and panning . . . . . . . . . . . . . . . . . . . . . . . . . .611 Working with sound metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 Accessing raw sound data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614 Capturing sound input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
  • 11. 11 Accessing a microphone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 Routing microphone audio to local speakers. . . . . . . . . . . . . . . . . . .619 Altering microphone audio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .619 Detecting microphone activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620 Sending audio to and from a media server. . . . . . . . . . . . . . . . . . . . .621 Example: Podcast Player . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622 Reading RSS data for a podcast channel . . . . . . . . . . . . . . . . . . . . 623 Simplifying sound loading and playback using the SoundFacade class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623 Displaying playback progress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627 Pausing and resuming playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628 Extending the Podcast Player example . . . . . . . . . . . . . . . . . . . . . . 629 Chapter 21: Capturing user input . . . . . . . . . . . . . . . . . . . . . . . . . .631 Basics of user input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .631 Capturing keyboard input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633 Capturing mouse input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636 Example: WordSearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .641 Chapter 22: Networking and communication . . . . . . . . . . . . . . 645 Basics of networking and communication . . . . . . . . . . . . . . . . . . . . . . 646 Working with external data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649 Connecting to other Flash Player instances. . . . . . . . . . . . . . . . . . . . . 656 Socket connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 Storing local data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668 Working with file upload and download . . . . . . . . . . . . . . . . . . . . . . . . . 671 Example: Building a Telnet client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 Example: Uploading and downloading files . . . . . . . . . . . . . . . . . . . . . 685 Chapter 23: Client system environment . . . . . . . . . . . . . . . . . . . 695 Basics of the client system environment. . . . . . . . . . . . . . . . . . . . . . . . 695 Using the System class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698 Using the Capabilities class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699 Using the ApplicationDomain class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700 Using the IME class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 Example: Detecting system capabilities . . . . . . . . . . . . . . . . . . . . . . . . 709
  • 12. 12 Chapter 24: Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 Basics of printing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 Printing a page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718 Flash Player tasks and system printing. . . . . . . . . . . . . . . . . . . . . . . . . . 719 Setting size, scale, and orientation . . . . . . . . . . . . . . . . . . . . . . . . . . . . .722 Example: Multiple-page printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .725 Example: Scaling, cropping, and responding . . . . . . . . . . . . . . . . . . . . 727 Chapter 25: Using the external API . . . . . . . . . . . . . . . . . . . . . . . 731 Basics of using the external API. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .732 External API requirements and advantages . . . . . . . . . . . . . . . . . . . . .735 Using the ExternalInterface class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .736 Getting information about the external container . . . . . . . . . . . . . . 737 Calling external code from ActionScript . . . . . . . . . . . . . . . . . . . . . . 737 Calling ActionScript code from the container. . . . . . . . . . . . . . . . . .739 The external API’s XML format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .740 Example: Using the external API with a web page container. . . . . . .742 Example: Using the external API with an ActiveX container . . . . . . .749 Chapter 26: Flash Player security. . . . . . . . . . . . . . . . . . . . . . . . .757 Flash Player security overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .758 Overview of permission controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .760 Security sandboxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770 Restricting networking APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 Full-screen mode security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775 Loading content. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776 Cross-scripting. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779 Accessing loaded media as data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .783 Loading data. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .786 Loading embedded content from SWF files imported into a security domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .788 Working with legacy content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .789 Setting LocalConnection permissions . . . . . . . . . . . . . . . . . . . . . . . . . .790 Controlling access to scripts in a host web page . . . . . . . . . . . . . . . . .790 Shared objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .792 Camera, microphone, clipboard, mouse, and keyboard access . . . .794 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
  • 13. 13 About this manual This manual provides a foundation for developing applications in ActionScript™ 3.0. To best understand the ideas and techniques described, you should already be familiar with general programming concepts such as data types, variables, loops, and functions. You should also understand basic object-oriented programming concepts such as classes and inheritance. Prior knowledge of ActionScript 1.0 or ActionScript 2.0 is helpful but not necessary. Contents Using this manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Accessing ActionScript documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 ActionScript learning resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .17
  • 14. 14 About this manual Using this manual The chapters in this manual are organized into the following logical groups to help you better find related areas of ActionScript documentation: This manual also contains numerous sample files that demonstrate application programming concepts for important or commonly used classes. Sample files are packaged in ways to make them easier to load and use with Adobe® Flash® CS3 Professional and may include wrapper files. However, the core sample code is pure ActionScript 3.0 that you can use in whichever development environment you prefer. ActionScript 3.0 can be written and compiled a number of ways, including: ■ Using the Adobe Flex Builder 2 development environment ■ Using any text editor and a command-line compiler, such as the one provided with Flex Builder 2 ■ Using the Adobe® Flash® CS3 Professional authoring tool For more information about ActionScript development environments, see Chapter 1, “Introduction to ActionScript 3.0.” Chapters Description Chapters 1 through 4, overview of ActionScript programming Discusses core ActionScript 3.0 concepts, including language syntax, statements and operators, the ECMAScript edition 4 draft language specification, object-oriented ActionScript programming, and the new approach to managing display objects on the Adobe® Flash® Player 9 display list. Chapters 5 through 10, core ActionScript 3.0 data types and classes Describes top-level data types in ActionScript 3.0 that are also part of the ECMAScript draft specification. Chapters 11 through 26, Flash Player APIs Describes important features that are implemented in packages and classes specific to Adobe Flash Player 9, including event handling, networking and communications, file input and output, the external interface, the application security model, and more.
  • 15. Accessing ActionScript documentation 15 To understand the code samples in this manual, you don’t need to have prior experience using integrated development environments for ActionScript, such as Flex Builder or the Flash authoring tool. You will, however, want to refer to the documentation for those tools to learn how to use them to write and compile ActionScript 3.0 code. For more information, see “Accessing ActionScript documentation” on page 15. Accessing ActionScript documentation Because this manual focuses on describing ActionScript 3.0, which is a rich and powerful object-oriented programming language, it does not extensively cover the application development process or workflow within a particular tool or server architecture. So in addition to Programming ActionScript 3.0, you’ll want to consult other sources of documentation as you design, develop, test, and deploy ActionScript 3.0 applications. ActionScript 3.0 documentation This manual familiarizes you with the concepts behind the ActionScript 3.0 programming language and gives you implementation details and samples illustrating important language features. However, this manual is not a complete language reference. For that, see the ActionScript 3.0 Language and Components Reference, which describes every class, method, property, and event in the language. The ActionScript 3.0 Language and Components Reference provides detailed reference information about the core language, Flash components (in the fl packages), and Flash Player APIs (in the flash packages). Flash documentation If you use the Flash development environment, you may want to consult these manuals: Book Description Using Flash Describes how to develop your dynamic web applications in the Flash authoring environment Programming ActionScript 3.0 Describes specific usage of the ActionScript 3.0 language and core Flash Player API ActionScript 3.0 Language and Components Reference Provides syntax, usage, and code examples for the Flash components and ActionScript 3.0 API Using ActionScript 3.0 Components Explains the details of using components to develop Flash applications
  • 16. 16 About this manual Learning ActionScript 2.0 in Adobe Flash Provides an overview of ActionScript 2.0 syntax and explains how to use ActionScript 2.0 when working with different types of objects ActionScript 2.0 Language Reference Provides syntax, usage, and code examples for the Flash components and ActionScript 2.0 API Using ActionScript 2.0 Components Explains in detail how to use ActionScript 2.0 components to develop Flash applications ActionScript 2.0 Components Language Reference Describes each component available in the Version 2 Adobe Component Architecture, along with its API Extending Flash Describes the objects, methods, and properties available in the JavaScript API Getting Started with Flash Lite 2.x Explains how to use Adobe® Flash® Lite™ 2.x to develop applications and provides syntax, usage, and code examples for the ActionScript features that are available with Flash Lite 2.x Developing Flash Lite 2.x Applications Explains how to develop Flash Lite 2.x applications Introduction to Flash Lite 2.x ActionScript Introduces how to develop applications with Flash Lite 2.x and describes all the ActionScript features available to Flash Lite 2.x developers Flash Lite 2.x ActionScript Language Reference Provides syntax, usage, and code examples for the ActionScript 2.0 API that is available in Flash Lite 2.x Getting Started with Flash Lite 1.x Provides an introduction to Flash Lite 1.x and describes how to test your content using the Adobe® Device Central CS3 emulator Developing Flash Lite 1.x Applications Describes how to develop applications for mobile devices using Flash Lite 1.x Learning Flash Lite 1.x ActionScript Explains how to use ActionScript in Flash Lite 1.x applications and describes all the ActionScript features available with Flash Lite 1.x Flash Lite 1.x ActionScript Language Reference Provides the syntax and usage of ActionScript elements that are available with Flash Lite 1.x Book Description
  • 17. ActionScript learning resources 17 ActionScript learning resources In addition to the content in these manuals, Adobe provides regularly updated articles, design ideas, and examples at the Adobe Developer Center and the Adobe Design Center. Adobe Developer Center The Adobe Developer Center is your resource for up-to-the-minute information on ActionScript, articles about real-world application development, and information about important emerging issues. View the Developer Center at www.adobe.com/devnet/. Adobe Design Center Learn the latest in digital design and motion graphics. Browse work by leading artists, discover new design trends, and hone your skills with tutorials, key workflows, and advanced techniques. Check back twice a month for fresh tutorials and articles, and inspirational gallery pieces. View the Design Center at www.adobe.com/designcenter/.
  • 18. 18 About this manual
  • 19. 19 1 CHAPTER 1 Introduction to ActionScript 3.0 This chapter provides an overview of ActionScript 3.0, the newest and most revolutionary version of ActionScript. Contents About ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Advantages of ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20 What’s new in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Compatibility with previous versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 About ActionScript ActionScript is the programming language for the Adobe Flash Player run-time environment. It enables interactivity, data handling, and much more in Flash content and applications. ActionScript is executed by the ActionScript Virtual Machine (AVM), which is part of Flash Player. ActionScript code is typically compiled into bytecode format (a sort of programming language that’s written and understood by computers) by a compiler, such as the one built into Adobe Flash CS3 Professional or Adobe® Flex™ Builder™, or that is available in the Adobe® Flex™ SDK and the Flex™ Data Services. The bytecode is embedded in SWF files, which are executed by the Flash Player, the run-time environment. ActionScript 3.0 offers a robust programming model that will be familiar to developers with a basic knowledge of object-oriented programming. Some of the key features of ActionScript 3.0 include the following: ■ A new ActionScript Virtual Machine, called AVM2, that uses a new bytecode instruction set and provides significant performance improvements ■ A more modern compiler code base that adheres much more closely to the ECMAScript (ECMA 262) standard and that performs deeper optimizations than previous versions of the compiler
  • 20. 20 Introduction to ActionScript 3.0 ■ An expanded and improved application programming interface (API), with low-level control of objects and a true object-oriented model ■ A core language based on the upcoming ECMAScript (ECMA-262) edition 4 draft language specification ■ An XML API based on the ECMAScript for XML (E4X) specification (ECMA-357 edition 2). E4X is a language extension to ECMAScript that adds XML as a native data type of the language. ■ An event model based on the Document Object Model (DOM) Level 3 Events Specification Advantages of ActionScript 3.0 ActionScript 3.0 goes beyond the scripting capabilities of previous versions of ActionScript. It is designed to facilitate the creation of highly complex applications with large data sets and object-oriented, reusable code bases. While ActionScript 3.0 is not required for content that runs in Adobe Flash Player 9, it opens the door to performance improvements that are only available with the AVM2, the new virtual machine. ActionScript 3.0 code can execute up to ten times faster than legacy ActionScript code. The older version of ActionScript Virtual Machine, AVM1, executes ActionScript 1.0 and ActionScript 2.0 code. AVM1 is supported by Flash Player 9 for backward compatibility with existing and legacy content. For more information, see “Compatibility with previous versions” on page 24.
  • 21. What’s new in ActionScript 3.0 21 What’s new in ActionScript 3.0 Although ActionScript 3.0 contains many classes and features that will be familiar to ActionScript programmers, ActionScript 3.0 is architecturally and conceptually different from previous versions of ActionScript. The enhancements in ActionScript 3.0 include new features of the core language and an improved Flash Player API that provides increased control of low-level objects. Core language features The core language defines the basic building blocks of the programming language, such as statements, expressions, conditions, loops, and types. ActionScript 3.0 contains many new features that speed up the development process. Run-time exceptions ActionScript 3.0 reports more error conditions than previous versions of ActionScript. Run- time exceptions are used for common error conditions, improving the debugging experience and enabling you to develop applications that handle errors robustly. Run-time errors can provide stack traces annotated with source file and line number information, helping you quickly pinpoint errors. Run-time types In ActionScript 2.0, type annotations were primarily a developer aid; at run time, all values were dynamically typed. In ActionScript 3.0, type information is preserved at run time, and used for a number of purposes. Flash Player 9 performs run-time type checking, improving the system’s type safety. Type information is also used to represent variables in native machine representations, improving performance and reducing memory usage. Sealed classes ActionScript 3.0 introduces the concept of sealed classes. A sealed class possesses only the fixed set of properties and methods that were defined at compile time; additional properties and methods cannot be added. This enables stricter compile-time checking, resulting in more robust programs. It also improves memory usage by not requiring an internal hash table for each object instance. Dynamic classes are also possible using the dynamic keyword. All classes in ActionScript 3.0 are sealed by default, but can be declared to be dynamic with the dynamic keyword.
  • 22. 22 Introduction to ActionScript 3.0 Method closures ActionScript 3.0 enables a method closure to automatically remember its original object instance. This feature is useful for event handling. In ActionScript 2.0, method closures would not remember what object instance they were extracted from, leading to unexpected behavior when the method closure was invoked. The mx.utils.Delegate class was a popular workaround, but it is no longer needed. ECMAScript for XML (E4X) ActionScript 3.0 implements ECMAScript for XML (E4X), recently standardized as ECMA- 357. E4X offers a natural, fluent set of language constructs for manipulating XML. In contrast to traditional XML-parsing APIs, XML with E4X performs like a native data type of the language. E4X streamlines the development of applications that manipulate XML by drastically reducing the amount of code needed. For more information about the ActionScript 3.0 implementation of E4X, see Chapter 11, “Working with XML,” on page 343. To view ECMA’s E4X specification, go to www.ecma-international.org. Regular expressions ActionScript 3.0 includes native support for regular expressions so that you can quickly search for and manipulate strings. ActionScript 3.0 implements support for regular expressions as they are defined in the ECMAScript (ECMA-262) edition 3 language specification. Namespaces Namespaces are similar to the traditional access specifiers used to control visibility of declarations (public, private, protected). They work as custom access specifiers, which can have names of your choice. Namespaces are outfitted with a Universal Resource Identifier (URI) to avoid collisions, and are also used to represent XML namespaces when you work with E4X. New primitive types ActionScript 2.0 has a single numeric type, Number, a double-precision, floating-point number. ActionScript 3.0 contains the int and uint types. The int type is a 32-bit signed integer that lets ActionScript code take advantage of the fast integer math capabilities of the CPU. The int type is useful for loop counters and variables where integers are used. The uint type is an unsigned, 32-bit integer type that is useful for RGB color values, byte counts, and more.
  • 23. What’s new in ActionScript 3.0 23 Flash Player API features The Flash Player API in ActionScript 3.0 contains many new classes that allow you to control objects at a low level. The architecture of the language is completely new and more intuitive. While there are too many new classes to cover in detail here, the following sections highlight some significant changes. DOM3 event model Document Object Model Level 3 event model (DOM3) provides a standard way of generating and handling event messages so that objects within applications can interact and communicate, maintaining their state and responding to change. Patterned after the World Wide Web Consortium DOM Level 3 Events Specification, this model provides a clearer and more efficient mechanism than the event systems available in previous versions of ActionScript. Events and error events are located in the flash.events package. The Flash components framework uses the same event model as the Flash Player API, so the event system is unified across the Flash platform. Display list API The API for accessing the Flash Player display list—the tree that contains any visual elements in a Flash application—consists of classes for working with visual primitives in Flash. The new Sprite class is a lightweight building block, similar to the MovieClip class but more appropriate as a base class for UI components. The new Shape class represents raw vector shapes. These classes can be instantiated naturally with the new operator and can be dynamically re-parented at any time. Depth management is now automatic and built into Flash Player, rendering assignment of depth numbers unnecessary. New methods are provided for specifying and managing the z- order of objects. Handling dynamic data and content ActionScript 3.0 contains mechanisms for loading and handling assets and data in your Flash application that are intuitive and consistent across the API. The new Loader class provides a single mechanism for loading SWF files and image assets and provides a way to access detailed information about loaded content. The URLLoader class provides a separate mechanism for loading text and binary data in data-driven applications. The Socket class provides a means to read and write binary data to server sockets in any format.
  • 24. 24 Introduction to ActionScript 3.0 Low-level data access Various APIs provide low-level access to data that was never before available in ActionScript. For data that is being downloaded, the URLStream class, which is implemented by URLLoader, provides access to data as raw binary data while it is being downloaded. The ByteArray class lets you optimize reading, writing, and working with binary data. The new Sound API provides detailed control of sound through the SoundChannel and SoundMixer classes. New APIs dealing with security provide information about the security privileges of a SWF file or loaded content, enabling you to better handle security errors. Working with text ActionScript 3.0 contains a flash.text package for all text-related APIs. The TextLineMetrics class provides detailed metrics for a line of text within a text field; it replaces the TextField.getLineMetrics() method in ActionScript 2.0. The TextField class contains a number of interesting new low-level methods that can provide specific information about a line of text or a single character in a text field. These methods include getCharBoundaries(), which returns a rectangle representing the bounding box of a character, getCharIndexAtPoint(), which returns the index of the character at a specified point, and getFirstCharInParagraph(), which returns the index of the first character in a paragraph. Line-level methods include getLineLength(), which returns the number of characters in a specified line of text, and getLineText(), which returns the text of the specified line. A new Font class provides a means to manage embedded fonts in SWF files. Compatibility with previous versions As always, Flash Player provides full backward compatibility with previously published content. Any content that ran in previous versions of Flash Player runs in Flash Player 9. The introduction of ActionScript 3.0 in Flash Player 9, however, does present some challenges for interoperability between old and new content running in Flash Player 9. The compatibility issues include the following: ■ A single SWF file cannot combine ActionScript 1.0 or 2.0 code with ActionScript 3.0 code. ■ ActionScript 3.0 code can load a SWF file written in ActionScript 1.0 or 2.0, but it cannot access the SWF file’s variables and functions.
  • 25. Compatibility with previous versions 25 ■ SWF files written in ActionScript 1.0 or 2.0 cannot load SWF files written in ActionScript 3.0. This means that SWF files authored in Flash 8 or Flex Builder 1.5 or earlier versions cannot load ActionScript 3.0 SWF files. The only exception to this rule is that an ActionScript 2.0 SWF file can replace itself with an ActionScript 3.0 SWF file, as long as the ActionScript 2.0 SWF file hasn't previously loaded anything into any of its levels. An ActionScript 2.0 SWF file can do this through a call to loadMovieNum(), passing a value of 0 to the level parameter. ■ In general, SWF files written in ActionScript 1.0 or 2.0 must be migrated if they are to work together with SWF files written in ActionScript 3.0. For example, say you created a media player using ActionScript 2.0. The media player loads various content that was also created using ActionScript 2.0. You cannot create new content in ActionScript 3.0 and load it in the media player. You must migrate the video player to ActionScript 3.0. If, however, you create a media player in ActionScript 3.0, that media player can perform simple loads of your ActionScript 2.0 content.
  • 26. 26 Introduction to ActionScript 3.0 The following table summarizes the limitations of previous versions of Flash Player in relation to loading new content and executing code, as well as the limitations for cross-scripting between SWF files written in different versions of ActionScript. Supported functionality Run-time environment Flash Player 7 Flash Player 8 Flash Player 9 Can load SWFs published for 7 and earlier 8 and earlier 9 and earlier Contains this AVM AVM1 AVM1 AVM1 and AVM2 Runs SWFs written in ActionScript 1.0 and 2.0 1.0 and 2.0 1.0 and 2.0, and 3.0 Supported functionality* * Content running in Flash Player 9 or later. Content running in Flash Player 8 or earlier can load, display, execute, and cross-script only ActionScript 1.0 and 2.0. Content created in ActionScript 1.0 and 2.0 ActionScript 3.0 Can load content and execute code in content created in ActionScript 1.0 and 2.0 only ActionScript 1.0 and 2.0, and ActionScript 3.0 Can cross script content created in ActionScript 1.0 and 2.0 only† † ActionScript 3.0 through Local Connection. ActionScript 3.0‡ ‡ ActionScript 1.0 and 2.0 through LocalConnection.
  • 27. 27 2 CHAPTER 2 Getting started with ActionScript This chapter is designed to get you started with ActionScript programming and give you the background you’ll need to understand the concepts and examples in the rest of this manual. We’ll begin with a discussion of basic programming concepts, described in the context of how to apply them in ActionScript. We’ll also cover the essentials of how to organize and build an ActionScript application. Contents Programming fundamentals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Working with objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Common program elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Example: Animation portfolio piece . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Building applications with ActionScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Creating your own classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Example: Creating a basic application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Running subsequent examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Programming fundamentals Since ActionScript is a programming language, it will help you learn ActionScript if you first understand a few general computer programming concepts. What computer programs do First of all, it’s useful to have a conceptual idea of what a computer program is and what it does. There are two main aspects to a computer program: ■ A program is a series of instructions or steps for the computer to carry out. ■ Each step ultimately involves manipulating some piece of information or data.
  • 28. 28 Getting started with ActionScript In a general sense, a computer program is just a list of step-by-step instructions that you give to the computer, which it performs one by one. Each individual instruction is known as a statement. As you’ll see throughout this manual, in ActionScript, each statement is written with a semicolon at the end. In essence, all that a given instruction in a program does is manipulate some bit of data that’s stored in the computer’s memory. In a simple case, you might instruct the computer to add two numbers and store the result in its memory. In a more complex case, imagine there is a rectangle drawn on the screen, and you want to write a program to move it somewhere else on the screen. The computer is keeping track of certain information about the rectangle—the x, y coordinates where it’s located, how wide and tall it is, what color it is, and so forth. Each of those bits of information is stored somewhere in the computer’s memory. A program to move the rectangle to a different location would have steps like “change the x coordinate to 200; change the y coordinate to 150” (in other words, specifying new values to be used for the x and y coordinates). Of course, the computer does something with this data to actually turn those numbers into the image that appears on the computer screen; but for the level of detail we’re interested in, it’s enough to know that the process of “moving a rectangle on the screen” really just involves changing bits of data in the computer’s memory. Variables and constants Since programming mainly involves changing pieces of information in the computer’s memory, there needs to be a way to represent a single piece of information in the program. A variable is a name that represents a value in the computer’s memory. As you write statements to manipulate values, you write the variable’s name in place of the value; any time the computer sees the variable name in your program, it looks in its memory and uses the value it finds there. For example, if you have two variables named value1 and value2, each containing a number, to add those two numbers you could write the statement: value1 + value2 When it’s actually carrying out the steps, the computer will look to see the values in each variable, and add them together. In ActionScript 3.0, a variable actually consists of three different parts: ■ The variable’s name ■ The type of data that can be stored in the variable ■ The actual value stored in the computer’s memory
  • 29. Programming fundamentals 29 We’ve just discussed how the computer uses the name as a placeholder for the value. The data type is also important. When you create a variable in ActionScript, you specify the specific type of data that it will hold; from that point on, your program’s instructions can store only that type of data in the variable, and you can manipulate the value using the particular characteristics associated with its data type. In ActionScript, to create a variable (known as declaring the variable), you use the var statement: var value1:Number; In this case, we’ve told the computer to create a variable named value1, which will hold only Number data (“Number” is a specific data type defined in ActionScript). You can also store a value in the variable right away: var value2:Number = 17; In the Adobe Flash CS3 Professional, there is another way to declare a variable. When you place a movie clip symbol, button symbol, or text field on the Stage, you can give it an instance name in the Property inspector. Behind the scenes, Flash creates a variable with the same name as the instance name, which you can use in your ActionScript code to refer to that Stage item. So, for example, if you have a movie clip symbol on the Stage and you give it the instance name rocketShip, whenever you use the variable rocketShip in your ActionScript code, you will in fact be manipulating that movie clip. Data types In ActionScript, there are many data types that you can use as the data type of the variables you create. Some of these can be thought of as “simple” or “fundamental” data types: ■ String: a textual value, like a name or the text of a book chapter ■ Numeric: ActionScript 3.0 includes three specific data types for numeric data: ■ Number: any numeric value, including values with or without a fraction ■ int: an integer (a whole number without a fraction) ■ uint: an “unsigned” integer, meaning a whole number that can’t be negative ■ Boolean: a true-or-false value, such as whether a switch is on or whether two values are equal
  • 30. 30 Getting started with ActionScript The simple data types represent a single piece of information: for example, a single number or a single sequence of text. However, the majority of the data types defined in ActionScript could be described as complex data types, because they represent a set of values grouped together. For example, a variable with the data type Date represents a single value—a moment in time. Nevertheless, that date value is actually represented as several values: the day, month, year, hours, minutes, seconds, and so on, all of which are individual numbers. So while we think of a date as a single value (and we can treat it as a single value by creating a Date variable), internally the computer thinks of it as a group of several values that, put together, define a single date. Most of the built-in data types, as well as data types defined by programmers, are complex data types. Some of the complex data types you might recognize are: ■ MovieClip: a movie clip symbol ■ TextField: a dynamic or input text field ■ SimpleButton: a button symbol ■ Date: information about a single moment in time (a date and time) Two words that are often used as synonyms for data type are class and object. A class is simply the definition of a data type—it’s like a template for all objects of the data type, like saying “all variables of the Example data type have these characteristics: A, B, and C.” An object, on the other hand, is just an actual instance of a class; a variable whose data type is MovieClip could be described as a MovieClip object. The following are different ways of saying the same thing: ■ The data type of the variable myVariable is Number. ■ The variable myVariable is a Number instance. ■ The variable myVariable is a Number object. ■ The variable myVariable is an instance of the Number class.
  • 31. Working with objects 31 Working with objects ActionScript is what’s known as an object-oriented programming language. Object-oriented programming is simply an approach to programming—really nothing more than a way to organize the code in a program, using objects. Earlier we defined a computer program as a series of steps or instructions that the computer performs. Conceptually, then, we might imagine a computer program as just a single long list of instructions. However, in object-oriented programming, the program instructions are divided among different objects—the code is grouped into chunks of functionality, so related types of functionality or related pieces of information are grouped together in one container. In fact, if you’ve worked with symbols in Flash, you’re already used to working with objects. Imagine you’ve defined a movie clip symbol—let’s say it’s a drawing of a rectangle—and you’ve placed a copy of it on the Stage. That movie clip symbol is also (literally) an object in ActionScript; it’s an instance of the MovieClip class. There are various characteristics of the movie clip that you can modify. For example, when it’s selected there are values you can change in the Property inspector, like its x coordinate, or its width, or various color adjustments like changing its alpha (transparency), or applying a drop- shadow filter to it. Other Flash tools let you make more changes, like using the Free Transform tool to rotate the rectangle. All of these things that you can do to modify a movie clip symbol in the Flash authoring environment are also things you can do in ActionScript by changing the pieces of data that are all put together into a single bundle called a MovieClip object. In ActionScript object-oriented programming, there are three types of characteristics that any class can include: ■ Properties ■ Methods ■ Events Together, these elements are used to manage the pieces of data used by the program and to decide what actions are carried out and in what order. Properties A property represents one of the pieces of data that are bundled together in an object. A song object might have properties named artist and title; the MovieClip class has properties like rotation, x, width, and alpha. You work with properties like individual variables—in fact, you might think of properties as simply the “child” variables contained in an object.
  • 32. 32 Getting started with ActionScript Here are some examples of ActionScript code that uses properties. This line of code moves the MovieClip named square to the x coordinate 100 pixels: square.x = 100; This code uses the rotation property to make the square MovieClip rotate to match the rotation of the triangle MovieClip: square.rotation = triangle.rotation; This code alters the horizontal scale of the square MovieClip so that it’s one-and-a-half times wider than it used to be: square.scaleX = 1.5; Notice the common structure: you use a variable (square, triangle) as the name of the object, followed by a period (.) and then the name of the property (x, rotation, scaleX). The period, known as the dot operator, is used to indicate that you’re accessing one of the child elements of an object. The whole structure together, “variable name-dot-property name,” is used like a single variable, as a name for a single value in the computer’s memory. Methods A method is an action that can be performed by an object. For example, if you’ve made a movie clip symbol in Flash with several keyframes and animation on its timeline, that movie clip can play, or stop, or be instructed to move the playhead to a particular frame. This code instructs the MovieClip named shortFilm to start playing: shortFilm.play(); This line makes the MovieClip named shortFilm stop playing (the playhead stops in place, like pausing a video): shortFilm.stop(); This code makes a MovieClip named shortFilm move its playhead to Frame 1 and stop playing (like rewinding a video): shortFilm.gotoAndStop(1);
  • 33. Working with objects 33 As you can see, methods, like properties, are accessed by writing the object’s name (a variable), then a period, and then the name of the method followed by parentheses. The parentheses are the way that you indicate that you’re calling the method—or in other words, instructing the object to perform that action. Sometimes values (or variables) are placed in the parentheses, as a way to pass along additional information that is needed to carry out the action. These values are known as method parameters. For example, the gotoAndStop() method needs to know which frame it should go to, so it requires a single parameter in the parentheses. Other methods, like play() and stop(), are self-explanatory, so they don’t require extra information. Nevertheless, they are still written with parentheses. Unlike properties (and variables), methods aren’t used as value placeholders. However, some methods can perform calculations and return a result that can be used like a variable. For example, the Number class’s toString() method converts the numeric value to its text representation: var numericData:Number = 9; var textData:String = numericData.toString(); For instance, you would use the toString() method if you wanted to display the value of a Number variable in a text field on the screen. The TextField class’s text property (which represents the actual text content displayed on the screen) is defined as a String, so it can contain only text values. This line of code converts the numeric value in the variable numericData to text, and then makes it show up on the screen in the TextField object named calculatorDisplay: calculatorDisplay.text = numericData.toString(); Events We’ve described a computer program as a series of instructions that the computer carries out step-by-step. Some simple computer programs consist of nothing more than that—a few steps which the computer carries out, at which point the program ends. However, ActionScript programs are designed to keep running, waiting for user input or other things to happen. Events are the mechanism that determines which instructions the computer carries out and when. In essence, events are things that happen that ActionScript is aware of and can respond to. Many events are related to user interaction—like a user clicking a button, or pressing a key on the keyboard—but there are also other types of events. For example, if you use ActionScript to load an external image, there is an event that can let you know when the image has finished loading. In essence, when an ActionScript program is running, Adobe Flash Player just sits and waits for certain things to happen, and when those things happen, it runs the specific ActionScript code that you’ve specified for those events.
  • 34. 34 Getting started with ActionScript Basic event handling The technique for specifying certain actions that should be performed in response to particular events is known as event handling. When you are writing ActionScript code to perform event handling, there are three important elements you’ll want to identify: ■ The event source: Which object is the one the event is going to happen to? For instance, which button will be clicked, or which Loader object is loading the image? The event source is also known as the event target, because it’s the object where the event is targeted by Flash Player (where the event actually happens). ■ The event: What is the thing that is going to happen, the thing that you want to respond to? This is important to identify, because many objects trigger several events. ■ The response: What step(s) do you want performed when the event happens? Any time you write ActionScript code to handle events, it will include these three elements, and the code will follow this basic structure (elements in bold are placeholders you’d fill in for your specific case): function eventResponse(eventObject:EventType):void { // Actions performed in response to the event go here. } eventSource.addEventListener(EventType.EVENT_NAME, eventResponse); This code does two things. First, it defines a function, which is the way to specify the actions you want performed in response to the event. Next, it calls the addEventListener() method of the source object, in essence “subscribing” the function to the specified event so that when the event happens, the function’s actions are carried out. We’ll consider each of these parts in more detail. A function provides a way for you to group actions together, with a single name that is like a shortcut name to carry out the actions. A function is identical to a method except that it isn’t necessarily associated with a specific class (in fact, a method could be defined as a function that is associated with a particular class). When you’re creating a function for event handling, you must choose the name for the function (named eventResponse in this case), and you must also specify one parameter (named eventObject in this example). Specifying a function parameter is like declaring a variable, so you also have to indicate the data type of the parameter. There is an ActionScript class defined for each event, and the data type you specify for the function parameter is always the class associated with the particular event you want to respond to. Finally, between the opening and closing curly braces ({ ... }), you write the instructions you want the computer to carry out when the event happens.
  • 35. Working with objects 35 Once you’ve written the event-handling function, you need to tell the event source object (the object that the event happens to—for example, the button) that you want your function to be called when the event happens. You do this by calling the addEventListener() method of that object (all objects that have events also have an addEventListener() method). The addEventListener() method takes two parameters: ■ First, the name of the specific event you want to respond to. Once again, each event is affiliated with a specific class, and that class will have a special value predefined for each event—sort of like the event’s own unique name, which you should use for the first parameter. ■ Second, the name of your event response function. Note that a function name is written without parentheses when it’s passed as a parameter. Examining the event-handling process The following is a step-by-step description of the process that happens when you create an event listener. In this case, it’s an example of creating a listener function that is called when an object named myButton is clicked. The actual code written by the programmer is as follows: function eventResponse(event:MouseEvent):void { // Actions performed in response to the event go here. } myButton.addEventListener(MouseEvent.CLICK, eventResponse);
  • 36. 36 Getting started with ActionScript Here is how this code would actually work when it’s running in Flash Player: 1. When the SWF file loads, Flash Player makes note of the fact that there’s a function named eventResponse(). 2. Flash Player then runs the code (specifically, the lines of code that aren’t in a function). In this case that’s only one line of code: calling the addEventListener() method on the event source object (named myButton) and passing the eventResponse function as a parameter.
  • 37. Working with objects 37 a. Internally, myButton has a list of functions that are listening to each of its events, so when its addEventListener() method is called, myButton stores the eventResponse() function in its list of event listeners. 3. At some point, the user clicks the myButton object, triggering its click event (identified as MouseEvent.CLICK in the code). At that point, the following occurs:
  • 38. 38 Getting started with ActionScript a. Flash Player creates an object, an instance of the class associated with the event in question (MouseEvent in this example). For many events this will be an instance of the Event class; for mouse events it will be a MouseEvent instance; and for other events it will be an instance of the class that’s associated with that event. This object that’s created is known as the event object, and it contains specific information about the event that happened: what type of event it is, where it happened, and other event- specific information if applicable. b. Flash Player then looks at the list of event listeners stored by myButton. It goes through these functions one by one, calling each function and passing the event object to the function as a parameter. Since the eventResponse() function is one of myButton’s listeners, as part of this process Flash Player calls the eventResponse() function.
  • 39. Working with objects 39 c. When the eventResponse() function is called, the code in that function runs, so your specified actions are carried out. Event-handling examples Here are a few more concrete examples of events to give you an idea of some of the common event elements and possible variations available when you write event-handling code: ■ Clicking a button to start the current movie clip playing. In the following example, playButton is the instance name of the button, and this is a special name meaning “the current object”: this.stop(); function playMovie(event:MouseEvent):void { this.play(); } playButton.addEventListener(MouseEvent.CLICK, playMovie); ■ Detecting typing in a text field. In this example, entryText is an input text field, and outputText is a dynamic text field: function updateOutput(event:TextEvent):void { var pressedKey:String = event.text; outputText.text = "You typed: " + pressedKey; } entryText.addEventListener(TextEvent.TEXT_INPUT, updateOutput);
  • 40. 40 Getting started with ActionScript ■ Clicking a button to navigate to a URL. In this case, linkButton is the instance name of the button: function gotoAdobeSite(event:MouseEvent):void { var adobeURL:URLRequest = new URLRequest("http://www.adobe.com/"); navigateToURL(adobeURL); } linkButton.addEventListener(MouseEvent.CLICK, gotoAdobeSite); Creating object instances Of course, before you can use an object in ActionScript, the object has to exist in the first place. One part of creating an object is declaring a variable; however, declaring a variable only creates an empty place in the computer’s memory. You must assign an actual value to the variable—that is, create an object and store it in the variable—before you attempt to use or manipulate it. The process of creating an object is known as instantiating the object—in other words, creating an instance of a particular class. One simple way to create an object instance doesn’t involve ActionScript at all. In Flash, when you place a movie clip symbol, button symbol, or text field on the Stage, and you assign it an instance name in the Property inspector, Flash automatically declares a variable with that instance name, creates an object instance, and stores that object in the variable. Likewise, in Adobe Flex Builder when you create a component in Macromedia® MXML™ from Adobe (either by coding an MXML tag or by placing the component on the editor in Design mode) and assign an ID to that component (in the MXML markup or in the Flex Properties view), that ID becomes the name of an ActionScript variable, and an instance of the component is created and stored in the variable. However, you won’t always want to create an object visually. There are also several ways you can create object instances using only ActionScript. First, with several ActionScript data types, you can create an instance using a literal expression—a value written directly into the ActionScript code. Here are some examples: ■ Literal numeric value (enter the number directly): var someNumber:Number = 17.239; var someNegativeInteger:int = -53; var someUint:uint = 22; ■ Literal String value (surround the text with double quotation marks): var firstName:String = "George"; var soliloquy:String = "To be or not to be, that is the question...";
  • 41. Working with objects 41 ■ Literal Boolean value (use the literal values true or false): var niceWeather:Boolean = true; var playingOutside:Boolean = false; ■ Literal XML value (enter the XML directly): var employee:XML = <employee> <firstName>Harold</firstName> <lastName>Webster</lastName> </employee>; ActionScript also defines literal expressions for the Array, RegExp, Object, and Function data types. For details on these classes, see “Working with arrays” on page 223, “Using regular expressions” on page 285, and “Object data type” on page 97. For any other data type, to create an object instance you use the new operator with the class name, like this: var raceCar:MovieClip = new MovieClip(); var birthday:Date = new Date(2006, 7, 9); Creating an object using the new operator is often referred to as “calling the class’s constructor.” A constructor is a special method that is called as part of the process of creating an instance of a class. Notice that when you create an instance in this way, you put parentheses after the class name, and sometimes you specify parameter values—two things that you also do when calling a method. It’s important to be familiar with the new ClassName() way of creating objects. If you need to create an instance of any ActionScript data type that doesn’t have a visual representation (and hence can’t be created by placing an item on the Flash Stage or the Design mode of Flex Builder’s MXML editor), you can only do so by creating the object directly in ActionScript using the new operator. In Flash specifically, the new operator can also be used to create an instance of a movie clip symbol that is defined in the Library but isn’t placed on the Stage. For more about this, see “Creating MovieClip objects with ActionScript” on page 498. NOTE Even for those data types that let you create instances using a literal expression, you can still use the new operator to create an object instance. For instance, these two lines of code do exactly the same thing: var someNumber:Number = 6.33; var someNumber:Number = new Number(6.33);
  • 42. 42 Getting started with ActionScript Common program elements In addition to declaring variables, creating object instances, and manipulating objects using their properties and methods, there are a few other building blocks that you use to create an ActionScript program. Operators Operators are special symbols (or occasionally words) that are used to perform calculations. They are mostly used for math operations, and also used when comparing values to each other. As a general rule, an operator uses one or more values and “works out” to a single result. For example: ■ The addition operator (+) adds two values together, resulting in a single number: var sum:Number = 23 + 32; ■ The multiplication operator (*) multiplies one value by another, resulting in a single number: var energy:Number = mass * speedOfLight * speedOfLight; ■ The equality operator (==) compares two values to see if they are equal, resulting in a single true-or-false (Boolean) value: if (dayOfWeek == "Wednesday") { takeOutTrash(); } As shown here, the equality operator and the other “comparison” operators are most commonly used with the if statement to determine if certain instructions should be carried out or not. For more details and examples of using operators, see “Operators” on page 109. Comments As you’re writing ActionScript, you’ll often want to leave notes to yourself, perhaps explaining how certain lines of code work or why you made a particular choice. Code comments are a tool you can use to write text that the computer should ignore in your code. ActionScript includes two kinds of comments: ■ Single-line comment: A single-line comment is designated by placing two slashes anywhere on a line. Everything after the slashes up to the end of that line is ignored by the computer:
  • 43. Common program elements 43 // This is a comment; it’s ignored by the computer. var age:Number = 10; // Set the age to 10 by default. ■ Multiline comments: A multiline comment includes a starting comment marker (/*), then the comment content, and an ending comment marker (*/). Everything between the starting and ending markers is ignored by the computer, regardless of how many lines the comment spans: /* This might be a really long description, perhaps describing what a particular function is used for or explaining a section of code. In any case, these lines are all ignored by the computer. */ Another common use of comments is to temporarily “turn off” one or more lines of code— for example, if you’re testing out a different way of doing something, or trying to figure out why certain ActionScript code isn’t working the way you expect. Flow control Many times in a program, you will want to repeat certain actions, perform only certain actions and not others, perform alternative actions depending on certain conditions, and so on. Flow control is the control over which actions are performed. There are several types of flow control elements available in ActionScript. ■ Functions: Functions are like shortcuts—they provide a way to group a series of actions under a single name, and can be used to perform calculations. Functions are particularly important for handling events, but are also used as a general tool for grouping a series of instructions. For more on functions, see “Functions” on page 122. ■ Loops: Loop structures let you designate a set of instructions that the computer will perform a set number of times or until some condition changes. Often loops are used to manipulate several related items, using a variable whose value changes each time the computer works through the loop. For more on loops, see “Looping” on page 119. ■ Conditional statements: Conditional statements provide a way to designate certain instructions that are carried out only under certain circumstances or to provide alternative sets of instructions for different conditions. The most common type of conditional statement is the if statement. The if statement checks a value or expression in its parentheses. If the value is true, the lines of code in curly braces are carried out; otherwise, they are ignored. For example: if (age < 20) { // show special teenager-targeted content }
  • 44. 44 Getting started with ActionScript The if statement’s companion, the else statement, lets you designate alternative instructions to be performed if the condition is not true: if (username == "admin") { // do some administrator-only things, like showing extra options } else { // do some non-administrator things } For more on conditional statements, see “Conditionals” on page 117. Example: Animation portfolio piece This example is designed to give you a first opportunity to see how you can piece together bits of ActionScript into a complete, if not ActionScript-heavy, application. The animation portfolio piece is an example of how you could take an existing linear animation (for example, a piece created for a client) and add some minor interactive elements appropriate for incorporating that animation into an online portfolio. The interactive behavior that we’ll add to the animation will include two buttons the viewer can click: one to start the animation, and one to navigate to a separate URL (such as the portfolio menu or the author’s home page). The process of creating this piece can be divided into these main sections: 1. Prepare the FLA file for adding ActionScript and interactive elements. 2. Create and add the buttons. 3. Write the ActionScript code. 4. Test the application. Preparing to add interactivity Before we can add interactive elements to our animation, it’s helpful to set up the FLA file by creating some places to add our new content. This includes creating actual space on the Stage where buttons can be placed, and also creating “space” in the FLA file for keeping different items separate.
  • 45. Example: Animation portfolio piece 45 To set up your FLA for adding interactive elements: 1. If you don’t already have a linear animation to which you’ll be adding interactivity, create a new FLA file with a simple animation such as a single motion tween or shape tween. Otherwise, open the FLA file containing the animation that you’re showcasing in the project, and save it with a new name to create a new working file. 2. Decide where on the screen you’ll want the two buttons to appear (one to start the animation and one to link to the author portfolio or home page). If necessary, clear or add some space on the Stage for this new content. If the animation doesn’t already have one, you might want to create a splash screen on the first frame (you’ll probably want to shift the animation over so it starts on Frame 2 or later). 3. Add a new layer, above the other layers in the Timeline, and rename it buttons. This will be the layer where you’ll add the buttons. 4. Add a new layer, above the buttons layer, and name it actions. This will be where you’ll add ActionScript code to your application. Creating and adding buttons Next we’ll need to actually create and position the buttons that will form the center of our interactive application. To create and add buttons to the FLA: 1. Using the drawing tools, create the visual appearance of your first button (the “play” button) on the buttons layer. For example, you might draw a horizontal oval with text on top of it. 2. Using the Selection tool, select all the graphic parts of the single button. 3. From the main menu, choose Modify > Convert To Symbol. 4. In the dialog box, choose Button as the symbol type, give the symbol a name, and click OK. 5. With the button selected, in the Property inspector give the button the instance name playButton. 6. Repeat steps 1 through 5 to create the button that will take the viewer to the author’s home page. Name this button homeButton. Writing the code The ActionScript code for this application can be divided into three sets of functionality, although it will all be entered in the same place. The three things the code needs to do are: ■ Stop the playhead as soon as the SWF file loads (when the playhead enters Frame 1).
  • 46. 46 Getting started with ActionScript ■ Listen for an event to start the SWF file playing when the user clicks the play button. ■ Listen for an event to send the browser to the appropriate URL when the user clicks the author home page button. To create code to stop the playhead when it enters Frame 1: 1. Select the keyframe on Frame 1 of the actions layer. 2. To open the Actions panel, from the main menu, choose Window > Actions. 3. In the Script pane, enter the following code: stop(); To write code to start the animation when the play button is clicked: 1. At the end of the code entered in the previous steps, add two empty lines. 2. Enter the following code at the bottom of the script: function startMovie(event:MouseEvent):void { this.play(); } This code defines a function called startMovie(). When startMovie() is called, it causes the main timeline to start playing. 3. On the line following the code added in the previous step, enter this line of code: playButton.addEventListener(MouseEvent.CLICK, startMovie); This line of code registers the startMovie() function as a listener for playButton’s click event. In other words, it makes it so that whenever the button named playButton is clicked, the startMovie() function is called. To write code to send the browser to a URL when the home page button is clicked: 1. At the end of the code entered in the previous steps, add two empty lines. 2. Enter this code at the bottom of the script: function gotoAuthorPage(event:MouseEvent):void { var targetURL:URLRequest = new URLRequest("http://example.com/"); navigateToURL(targetURL); } This code defines a function called gotoAuthorPage(). This function first creates a URLRequest instance representing the URL http://example.com/, and then passes that URL to the navigateToURL() function, causing the user’s browser to open that URL. 3. On the line following the code added in the previous step, enter this line of code: homeButton.addEventListener(MouseEvent.CLICK, gotoAuthorPage);
  • 47. Building applications with ActionScript 47 This line of code registers the gotoAuthorPage() function as a listener for homeButton’s click event. In other words, it makes it so that whenever the button named homeButton is clicked, the gotoAuthorPage() function is called. Testing the application At this point, the application should be completely functional. Let’s test it to make sure that’s the case. To test the application: 1. From the main menu, choose Control > Test Movie. Flash creates the SWF file and opens it in a Flash Player window. 2. Try both the buttons to make sure they do what you expect them to. 3. If the buttons don’t work, here are some things to check for: ■ Do the buttons both have distinct instance names? ■ Do the addEventListener() method calls use the same names as the buttons’ instance names? ■ Are the correct event names used in the addEventListener() method calls? ■ Is the correct parameter specified for each of the functions? (Both should have a single parameter with the data type MouseEvent.) All of these and most other possible mistakes should give an error message either when you choose the Test Movie command or when you click the button. Look in the Compiler Errors panel for compiler errors (the ones that happen when you first choose Test Movie), and check the Output panel for run-time errors (errors which happen while the SWF is playing—such as when you click a button). Building applications with ActionScript The process of writing ActionScript to build an application involves more than just knowing the syntax and the names of the classes you’ll use. While most of the information in this manual is geared towards those two topics (syntax and using ActionScript classes), you’ll also want to know some information such as what programs can be used for writing ActionScript, how ActionScript code can be organized and included in an application, and what steps you should follow in developing an ActionScript application.
  • 48. 48 Getting started with ActionScript Options for organizing your code You can use ActionScript 3.0 code to power everything from simple graphics animations to complex client-server transaction processing systems. Depending on the type of application you’re building, you may prefer to use one or more of these different ways of including ActionScript in your project. Storing code in frames in a Flash timeline In the Flash authoring environment, you can add ActionScript code to any frame in a timeline. This code will be executed while the movie is playing back, when the playhead enters that frame. Placing ActionScript code in frames provides a simple way to add behaviors to applications built in the Flash authoring tool. You can add code to any frame in the main timeline or to any frame in the timeline of any MovieClip symbol. However, this flexibility comes with a cost. When you build larger applications, it becomes easy to lose track of which frames contain which scripts. This can make the application more difficult to maintain over time. Many developers simplify the organization of their ActionScript code in the Flash authoring tool by placing code only in the first frame of a timeline, or on a specific layer in the Flash document. This makes it easier to locate and maintain the code in your Flash FLA files. However, in order to use the same code in another Flash project, you must copy and paste the code into the new file. If you want to be able to use your ActionScript code in other Flash projects in the future, you will want to store your code in external ActionScript files (text files with the .as extension). Storing code in ActionScript files If your project involves significant ActionScript code, the best way to organize your code is in separate ActionScript source files (text files with the .as extension). An ActionScript file can be structured in one of two ways, depending on how you intend to use it in your application. ■ Unstructured ActionScript code: Lines of ActionScript code, including statements or function definitions, written as though they were entered directly in a timeline script, MXML file, and so on.
  • 49. Building applications with ActionScript 49 ActionScript written in this way can be accessed using the include statement in ActionScript, or the <mx:Script> tag in Adobe Flex MXML. The ActionScript include statement causes the contents of an external ActionScript file to be inserted at a specific location and within a given scope in a script, as if it were entered there directly. In the Flex MXML language, the <mx:Script> tag lets you specify a source attribute that identifies an external ActionScript file to be loaded at that point in the application. For example, the following tag will load an external ActionScript file named Box.as: <mx:Script source=“Box.as” /> ■ ActionScript class definition: A definition of an ActionScript class, including its method and property definitions. When you define a class, you can access the ActionScript code in the class by creating an instance of the class and using its properties, methods, and events, just as you would with any of the built-in ActionScript classes. This requires two parts: ■ Use the import statement to specify the full name of the class, so the ActionScript compiler knows where to find it. For example, if you want to use the MovieClip class in ActionScript, you first need to import that class using its full name, including package and class: import flash.display.MovieClip; Alternatively, you can import the package that contains the MovieClip class, which is equivalent to writing separate import statements for each class in the package: import flash.display.*; The only exceptions to the rule that a class must be imported if you refer to that class in your code are the top-level classes, which are not defined in a package. ■ Write code which specifically refers to the class name (usually declaring a variable with that class as its data type, and creating an instance of the class to store in the variable). By referring to another class name in ActionScript code, you tell the compiler to load the definition of that class. For example, given an external class called Box, this statement causes a new instance of the Box class to be created: var smallBox:Box = new Box(10,20); When the compiler comes across the reference to the Box class for the first time, it searches the loaded source code to locate the Box class definition. NOTE In Flash, for scripts attached to frames on the Timeline, the built-in classes (in the flash.* packages) are automatically imported. However, when you write your own classes, or if you’re working with Flash authoring components (the fl.* packages) or if you’re working in Flex, you will need to explicitly import any class in order to write code that creates instances of that class.
  • 50. 50 Getting started with ActionScript Choosing the right tool Depending on the needs of your project and the resources you have available to you, you may want to use one of several tools (or multiple tools in conjunction with each other) for writing and editing your ActionScript code. Flash authoring tool In addition to its graphics and animation creation capabilities, Adobe Flash CS3 Professional includes tools for working with ActionScript code, either attached to elements in a FLA file or in external ActionScript-only files. The Flash authoring tool is ideal for projects that involve significant animation or video or where you want to create most of the graphic assets yourself, particularly projects with minimal user interaction or functionality requiring ActionScript. Another reason you may choose to use the Flash authoring tool to develop your ActionScript project is if you prefer to create visual assets and write code in the same application. You may also want to use Flash authoring if you want to use pre-built user interface components, but smaller SWF size or easier visual skinning are key priorities for your project. Adobe Flash CS3 Professional includes two tools for writing ActionScript code: ■ Actions panel: Available when working in a FLA file, this panel allows you to write ActionScript code attached to frames on a timeline. ■ Script window: The Script window is a dedicated text editor for working with ActionScript (.as) code files. Flex Builder Adobe Flex Builder is the premier tool for creating projects with the Flex framework. In addition to its visual layout and MXML editing tools, Flex Builder also includes a full- featured ActionScript editor, so it can be used to create Flex or ActionScript-only projects. Flex applications have several benefits, including a rich set of pre-built user interface controls, flexible dynamic layout controls, and built-in mechanisms for working with external data sources and linking external data to user interface elements. However, because of the additional code required to provide these features, Flex applications can have a larger SWF file size and can’t be completely re-skinned as easily as their Flash counterparts. Use Flex Builder if you are creating full-featured, data-driven rich Internet applications with Flex, and you want to edit ActionScript code, edit MXML code, and lay out your application visually, all within a single tool.
  • 51. Building applications with ActionScript 51 Third-party ActionScript editor Because ActionScript (.as) files are stored as simple text files, any program that is capable of editing plain text files can be used to write ActionScript files. In addition to Adobe’s ActionScript products, several third-party text editing programs with ActionScript-specific capabilities have been created. You can write an MXML file or ActionScript classes using any text editor program. You can then create a SWF application (either a Flex or an ActionScript- only application) from those files using the Flex SDK, which includes the Flex framework classes as well as the Flex compiler. Alternatively, many developers use a third-party ActionScript editor for writing ActionScript classes, in combination with the Flash authoring tool for creating graphical content. You might choose to use a third-party ActionScript editor if: ■ You prefer to write ActionScript code in a separate program in conjunction with designing visual elements in Flash. ■ You use an application for non-ActionScript programming (such as creating HTML pages or building applications in another programming language), and you want to use the same application for your ActionScript coding as well. ■ You want to create ActionScript-only or Flex projects using the Flex SDK without the expense of Flash or Flex Builder. Some of the notable code editors providing ActionScript-specific support include: ■ Adobe Dreamweaver® CS3 ■ ASDT ■ FDT ■ FlashDevelop ■ PrimalScript ■ SE|PY ■ XCode (with ActionScript template and code-hint files) The ActionScript development process No matter whether your ActionScript project is large or small, using a process to design and develop your application will help you work more efficiently and effectively. The following steps describe a basic development process for building an application that uses ActionScript 3.0: 1. Design your application. You should describe your application in some way before you start building it.
  • 52. 52 Getting started with ActionScript 2. Compose your ActionScript 3.0 code. You can create ActionScript code using Flash, Flex Builder, Dreamweaver, or a text editor. 3. Create a Flash or Flex application file to run your code. In the Flash authoring tool, this involves creating a new FLA file, setting up the publish settings, adding user interface components to the application, and referencing the ActionScript code. In the Flex development environment, creating a new application file involves defining the application and adding user interface components using MXML, and referencing the ActionScript code. 4. Publish and test your ActionScript application. This involves running your application from within the Flash authoring or Flex development environment, and making sure it does everything you intended. Note that you don’t necessarily have to follow these steps in order, or completely finish one step before working on another. For example, you might design one screen of your application (step 1), and then create the graphics, buttons, and so forth (step 3), before writing ActionScript code (step 2) and testing (step 4). Or you might design part of it, and then add one button or interface element at a time, writing ActionScript for each one and testing it as it’s built. Although it’s helpful to remember these four stages of the development process, in real-world development it’s usually more effective to move back and forth among the stages as appropriate.
  • 53. Creating your own classes 53 Creating your own classes The process of creating classes for use in your projects can seem daunting. However, the more difficult part of creating a class is the task of designing the class—identifying the methods, properties, and events that it will include. Strategies for designing a class The topic of object-oriented design is a complex one; entire careers have been devoted to the academic study and professional practice of this discipline. Nevertheless, here are a few suggested approaches that can help you get started. 1. Think about the role that the instances of this class will play in the application. Generally, objects serve one of these three roles: ■ Value object: These objects serve primarily as containers of data—that is, they likely have several properties and fewer methods (or sometimes no methods). They are generally code representations of clearly defined items, such as a Song class (representing a single real-world song) or Playlist class (representing a conceptual group of songs) in a music player application. ■ Display object: These are objects that actually appear on the screen. Examples include user-interface elements like a drop-down list or status readout, graphical elements like creatures in a video game, and so forth. ■ Application structure: These objects play a broad range of supporting roles in the logic or processing performed by applications. Examples include an object that performs certain calculations in a biology simulation; one that is responsible for synchronizing values between a dial control and a volume readout in a music player application; one that manages the rules in a video game; or one that loads a saved picture in a drawing application. 2. Decide the specific functionality that the class will need. The different types of functionality often become the methods of the class. 3. If the class is intended to serve as a value object, decide the data that the instances will include. These items are good candidates for properties. 4. Since your class is being designed specifically for your project, what’s most important is that you provide the functionality that your application needs. It might help to answer these questions for yourself:
  • 54. 54 Getting started with ActionScript ■ What pieces of information will your application be storing, tracking, and manipulating? Deciding this helps you identify any value objects and properties you may want. ■ What sets of actions will need to be performed—for example, when the application first loads, when a particular button is clicked, when a movie stops playing, and so forth? These are good candidates for methods (or properties, if the “actions” just involve changing individual values). ■ For any given action, what information will the class need to know in order to perform that action? Those pieces of information become the parameters of the method. ■ As the application proceeds to do its work, what things will change in your class that other parts of your application will need to know about? These are good candidates for events. 5. If there is an existing object that is similar to the object you need, except that it’s lacking some additional functionality you want to add, consider creating a subclass (a class which builds on the functionality of an existing class, rather than defining all of its own functionality). For example, if you want to create a class that will be a visual object on the screen, you’ll want to use the behavior of one of the existing display objects (for example, Sprite or MovieClip) as a basis for your class. In that case, MovieClip (or Sprite) would be the base class, and your class would extend that class. For more information about creating a subclass, see “Inheritance” on page 162. Writing the code for a class Once you have a design plan for your class, or at least some idea of what information it will need to keep track of and what actions it will need to carry out, the actual syntax of writing a class is fairly straightforward. Here are the minimum steps to create your own ActionScript class: 1. Open a new text document, in an ActionScript-specific program such as Flex Builder or Flash, in a general programming tool such as Dreamweaver, or in any program that allows you to work with plain text documents. 2. Enter a class statement to define the name of the class. To do this, enter the words public class, and then the class’s name, followed by opening and closing curly braces that will surround the contents of the class (the method and property definitions). For example: public class MyClass { }
  • 55. Creating your own classes 55 The word public indicates that the class can be accessed from any other code. For other alternatives, see “Access control namespace attributes” on page 144. 3. Type a package statement to indicate the name of the package in which your class will be found. The syntax is the word package, followed by the full package name, followed by opening and closing curly braces (which will surround the class statement block). For example, we’d change the code in the previous step to this: package mypackage { public class MyClass { } } 4. Define each property in the class using the var statement within the class body; the syntax is the same as you use to declare any variable (with the addition of the public modifier). For example, adding these lines between the opening and closing curly braces of the class definition will create properties named textVariable, numericVariable, and dateVariable: public var textVariable:String = "some default value"; public var numericVariable:Number = 17; public var dateVariable:Date; 5. Define each method in the class using the same syntax that’s used to define a function. For example: ■ To create a myMethod() method, enter: public function myMethod(param1:String, param2:Number):void { // do something with parameters } ■ To create a constructor (the special method that is called as part of the process of creating an instance of a class), create a method whose name matches exactly the name of the class: public function MyClass() { // do stuff to set initial values for properties // and otherwise set up the object textVariable = "Hello there!"; dateVariable = new Date(2001, 5, 11); } If you don’t include a constructor method in your class, the compiler will automatically create an empty constructor (one with no parameters and no statements) in your class.
  • 56. 56 Getting started with ActionScript There are a few more class elements that you can define.These elements are more involved. ■ Accessors are a special cross between a method and a property. When you write the code to define the class, you write the accessor like a method so you can perform multiple actions (rather than just reading or assigning a value, which is all you can do when you define a property). However, when you create an instance of your class, you treat the accessor like a property—using just the name to read or assign the value. For more information, see “Get and set accessor methods” on page 153. ■ Events in ActionScript aren’t defined using a specific syntax. Instead, you define events in your class by using the functionality of the EventDispatcher class to keep track of event listeners and notify them of events. For more on creating events in your own classes, see Chapter 10, “Handling events,” on page 313. Suggestions for organizing your classes Unlike previous ActionScript versions, ActionScript 3.0 does not have the one file, one class restriction that limits you to using only one class per file. Using ActionScript 3.0, you can save the source code for more than one class in a single .as file. In some cases, it might seem more convenient to pack multiple classes into a single source file, but in general, this is considered a bad programming practice, for a couple of reasons: ■ It is difficult to reuse individual classes if they are packed together into a single large file. ■ It is difficult to locate the source code for a specific class when its filename does not correspond to the class name. For these reasons, Adobe recommends that you always save the source code for each individual class in its own file, and give the file the same name as the class. Example: Creating a basic application You can create external ActionScript source files with an .as extension using Flash, Flex Builder, Dreamweaver, or any text editor. ActionScript 3.0 can be used within a number of application development environments, including the Flash authoring and Flex Builder tools. This section walks through the steps in creating and enhancing a simple ActionScript 3.0 application using the Flash authoring tool or the Flex Builder 2 tool. The application you’ll build presents a simple pattern for using external ActionScript 3.0 class files in Flash and Flex applications. That pattern will apply to all of the other sample applications in this manual.
  • 57. Example: Creating a basic application 57 Designing your ActionScript application You should have some idea about the application you want to build before you start building it. The representation of your design can be as simple as the name of the application and a brief statement of its purpose, or as complicated as a set of requirements documents containing numerous Unified Modeling Language (UML) diagrams. This manual doesn’t discuss the discipline of software design in detail, but it’s important to keep in mind that application design is an essential step in the development of ActionScript applications. Our first example of an ActionScript application will be a standard “Hello World” application, so its design is very simple: ■ The application will be called HelloWorld. ■ It will display a single text field containing the words “Hello World!” ■ In order to be easily reused, it will use a single object-oriented class, named Greeter, which can be used from within a Flash document or a Flex application. ■ After you create a basic version of the application, you will add new functionality to have the user enter a user name and have the application check the name against a list of known users. With that concise definition in place, you can start building the application itself. Creating the HelloWorld project and the Greeter class The design statement for the Hello World application said that its code should be easy to reuse. With this goal in mind, the application uses a single object-oriented class, named Greeter, which is used from within an application that you create in Flex Builder or the Flash authoring tool. To create the Greeter class in the Flash authoring tool: 1. In the Flash authoring tool, select File > New. 2. In the New Document dialog box, select ActionScript file, and click OK. A new ActionScript editing window is displayed. 3. Select File > Save. Select a folder to contain your application, name the ActionScript file Greeter.as, and then click OK. Continue with “Adding code to the Greeter class” on page 58.
  • 58. 58 Getting started with ActionScript Adding code to the Greeter class The Greeter class defines an object, Greeter, that you will be able to use in your HelloWorld application. To add code to the Greeter class: 1. Type the following code into the new file: package { public class Greeter { public function sayHello():String { var greeting:String; greeting = "Hello World!"; return greeting; } } } The Greeter class includes a single sayHello() method, which returns a string that says “Hello” to the user name that is given. 2. Select File > Save to save this ActionScript file. The Greeter class is now ready to be used in a Flash or Flex application. Creating an application that uses your ActionScript code The Greeter class that you have built defines a self-contained set of software functions, but it does not represent a complete application. To use the class, you need to create a Flash document or Flex application. The HelloWorld application creates an new instance of the Greeter class. Here’s how to attach the Greeter class to your application. To create an ActionScript application using the Flash authoring tool: 1. Select File > New. 2. In the New Document dialog box, select Flash Document, and click OK. A new Flash window is displayed. 3. Select File > Save. Select the same folder that contains the Greeter.as class file, name the Flash document HelloWorld.fla, and click OK.
  • 59. Example: Creating a basic application 59 4. In the Flash Tools palette, select the Text tool, and drag across the Stage to define a new text field, approximately 300 pixels wide and 100 pixels high. 5. In the Properties window, with the text field still selected on the Stage, type mainText as the instance name of the text field. 6. Click the first frame of the main timeline. 7. In the Actions panel, type the following script: var myGreeter:Greeter = new Greeter(); mainText.text = myGreeter.sayHello("Bob"); 8. Save the file. Continue with “Publishing and testing your ActionScript application” on page 59. Publishing and testing your ActionScript application Software development is an iterative process. You write some code, try to compile it, and edit the code until it compiles cleanly. You run the compiled application, test it to see if it fulfills the intended design, and if it doesn’t, you edit the code again until it does. The Flash and Flex Builder development environments offer a number of ways to publish, test, and debug your applications. Here are the basic steps for testing the HelloWorld application in each environment. To publish and test an ActionScript application using the Flash authoring tool: 1. Publish your application and watch for compilation errors. In the Flash authoring tool, select Control > Test Movie to compile your ActionScript code and run the HelloWorld application. 2. If any errors or warnings are displayed in the Output window when you test your application, fix the causes of these errors in the HelloWorld.fla or HelloWorld.as files, and then try testing the application again. 3. If there are no compilation errors, you will see a Flash Player window showing the Hello World application. The “Hello, Bob” text is displayed. You have just created a simple but complete object-oriented application that uses ActionScript 3.0. Continue with “Enhancing the HelloWorld application” on page 60.
  • 60. 60 Getting started with ActionScript Enhancing the HelloWorld application To make the application a little more interesting, you’ll now make it ask for and validate a user name against a predefined list of names. First, you will update the Greeter class to add new functionality. Then you will update the Flex or Flash application to use the new functionality. To update the Greeter.as file: 1. Open the Greeter.as file. 2. Change the contents of the file to the following (new and changed lines are shown in boldface): package { public class Greeter { /** * Defines the names that should receive a proper greeting. */ public static var validNames:Array = ["Sammy", "Frank", "Dean"]; /** * Builds a greeting string using the given name. */ public function sayHello(userName:String = ""):String { var greeting:String; if (userName == "") { greeting = "Hello. Please type your user name, and then press the Enter key."; } else if (validName(userName)) { greeting = "Hello, " + userName + "."; } else { greeting = "Sorry, " + userName + ", you are not on the list."; } return greeting; } /** * Checks whether a name is in the validNames list. */ public static function validName(inputName:String = ""):Boolean {
  • 61. Example: Creating a basic application 61 if (validNames.indexOf(inputName) > -1) { return true; } else { return false; } } } } The Greeter class now has a number of new features: ■ The validNames array lists valid user names. The array is initialized to a list of three names when the Greeter class is loaded. ■ The sayHello() method now accepts a user name and changes the greeting based on some conditions. If the userName is an empty string (""), the greeting property is set to prompt the user for a name. If the user name is valid, the greeting becomes "Hello, userName.” Finally, if either of those two conditions are not met, the greeting variable is set to "Sorry, userName, you are not on the list." ■ The validName() method returns true if the inputName is found in the validNames array, and false if it is not found. The statement validNames.indexOf(inputName) checks each of the strings in the validNames array against the inputName string. The Array.indexOf() method returns the index position of the first instance of an object in an array, or the value -1 if the object is not found in the array. Next you will edit the Flash or Flex file that references this ActionScript class. To modify the application using the Flash authoring tool: 1. Open the HelloWorld.fla file. 2. Modify the script in Frame 1 so that an empty string ("") is passed to the Greeter class’s sayHello() method: var myGreeter:Greeter = new Greeter(); mainText.text = myGreeter.sayHello(""); 3. Select the Text tool in the Tools palette, and then create two new text fields on the Stage, side-by-side and directly under the existing mainText text field. 4. In the first new text field, type the text User Name: to serve as a label. 5. Select the other new text field, and in the Property inspector, select InputText as the type of text field. Type textIn as the instance name. 6. Click the first frame of the main timeline. 7. In the Actions panel, add the following lines to the end of the existing script:
  • 62. 62 Getting started with ActionScript mainText.border = true; textIn.border = true; textIn.addEventListener(KeyboardEvent.KEY_UP, keyPressed); function keyPressed(event:Event):void { if (event.keyCode == Keyboard.ENTER) { mainText.text = myGreeter.sayHello(textIn.text); } } The new code adds the following functionality: ■ The first two lines simply define borders for two text fields. ■ An input text field, such as the textIn field, has a set of events that it can dispatch. The addEventListener() method lets you define a function that runs when a type of event occurs. In this case, that event is the pressing of the Enter key on the keyboard. ■ The keyPressed() custom function calls the sayHello() method of the myGreeter object, passing the text from the textIn text field as a parameter. That method returns a string greeting based on the value passed in. The returned string is then assigned to the text property of the mainText text field. The complete script for Frame 1 is the following: mainText.border = true; textIn.border = true; var myGreeter:Greeter = new Greeter(); mainText.text = myGreeter.sayHello(""); textIn.addEventListener(KeyboardEvent.KEY_UP, keyPressed); function keyPressed(event:Event):void { if (event.keyCode == Keyboard.ENTER) { mainText.text = myGreeter.sayHello(textIn.text); } } 8. Save the file. 9. Select Control > Test Movie to run the application. When you run the application, you will be prompted to enter a user name. If it is valid (Sammy, Frank, or Dean), the application displays the “hello” confirmation message.
  • 63. Running subsequent examples 63 Running subsequent examples Now that you’ve developed and run the “Hello World” ActionScript 3.0 application, you should have the basic knowledge you need to run the other code examples presented in this manual. Testing in-chapter example code listings As you’re working through this manual, you may want to try out the example code listings that are used to illustrate the various topics. That testing may involve displaying the value of variables at certain points in the program, or it may involve viewing or interacting with on- screen content. For testing visual content or interaction, the necessary elements will be described before or within the code listing—you’ll just need to create a document with the elements as described in order to test the code. In the case where you want to view the value of a variable at a given point in the program, there are a few ways you can accomplish this. One way is to use a debugger, such as the ones built into Flex Builder and Flash. For simple testing, however, it might be easiest to just print variable values out to some place where you can view them. The following steps will help you create a Flash document that you can use for testing a code listing and viewing variable values: To create a Flash document for testing in-chapter examples: 1. Create a new Flash document and save it on your hard drive. 2. To display test values in a text field on the Stage, activate the Text tool and create a new Dynamic text field on the Stage. A wide, tall text field with the Line type set to Multiline and the border turned on will be most useful. In the Property inspector, give the text field an instance name (for example “outputText”). To write values into the text field, you will add code that calls the appendText() method to the example code (described below). 3. Alternatively, you can add a trace() function call to the code listing (as described below) to view the results of the example. 4. To test a given example, copy the code listing into the Actions panel; if necessary, add a trace() function call or add a value to the text field using its appendText() method. 5. From the main menu, choose Control > Test Movie to create a SWF file and view the results. Since this approach is for viewing values of variables, there are two ways that you can easily view the values of variables as you’re trying out the examples: write values into a text field instance on the Stage, or use the trace() function to print values to the Output panel.
  • 64. 64 Getting started with ActionScript ■ The trace() function: The ActionScript trace() function writes the values of any parameters passed to it (either variables or literal expressions) to the Output panel. Many of the example listings in this manual already include a trace() function call, so for those listings you’ll only need to copy the code into your document and test the project. If you want to use trace() to test the value of a variable in a code listing that doesn’t already include it, just add a trace() call to the code listing, passing the variable as a parameter. For example, if you encountered a code listing such as this one in the chapter, var albumName:String = "Three for the money"; you could copy the code into the Actions panel, then add a call to the trace() function such as this one to test the outcome of the code listing: var albumName:String = "Three for the money"; trace("albumName =", albumName); When you run the program, this line will print out: albumName = Three for the money Each trace() function call can take multiple parameters, which are all strung together as a single printed line. A line break is added to the end of each trace() function call, so separate trace() calls print out on separate lines. ■ A text field on the Stage: If you prefer not to use the trace() function, you can add a Dynamic text field to the Stage using the Text tool, and write out values to that text field to view the results of a code listing. The appendText() method of the TextField class can be used to add a String value to the end of the text field’s contents. To access the text field using ActionScript, you must give it an instance name in the Property inspector. For instance, if your text field has the instance name outputText, the following code could be used to check the value of the variable albumName: var albumName:String = "Three for the money"; outputText.appendText("albumName = "); outputText.appendText(albumName); This code would write the following text to the text field named outputText: albumName = Three for the money As the example shows, the appendText() method will add the text to the same line as the previous contents, so multiple values can be added to the same line of text using multiple appendText() calls. To force the text to the next line you can append a newline character ("n"): outputText.appendText("n"); // adds a line break to the text field
  • 65. Running subsequent examples 65 Unlike the trace() function, the appendText() method only accepts one value as a parameter. That value must be a string (either a String instance or a string literal). To print out the value of a non-string variable, you must first convert the value to a String. The easiest way to do that is to call the object’s toString() method: var albumYear:int = 1999; outputText.appendText("albumYear = "); outputText.appendText(albumYear.toString()); Working with end-of-chapter examples Like this chapter, most chapters in this manual include a significant end-of-chapter example that ties together many of the concepts discussed in the chapter. However, unlike the Hello World example in this chapter, those examples will not be presented in a step-by-step tutorial format. The relevant ActionScript 3.0 code in each example will be highlighted and discussed, but instructions about running the examples in specific development environments won’t be provided. However, the example files distributed with this manual will include all of the files you need to compile and run the examples easily in your chosen development environment.
  • 66. 66 Getting started with ActionScript
  • 67. 67 3 CHAPTER 3 ActionScript language and syntax ActionScript 3.0 comprises both the core ActionScript language and the Adobe Flash Player Application Programming Interface (API). The core language is the part of ActionScript that implements the draft ECMAScript (ECMA-262), Edition 4 draft language specification. The Flash Player API provides programmatic access to Flash Player. This chapter provides a brief introduction to the core ActionScript language and syntax. After reading this chapter, you should have a basic understanding of how to work with data types and variables, how to use proper syntax, and how to control the flow of data in your program. Contents Language overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Objects and classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Packages and namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Data types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Conditionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Looping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .119 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
  • 68. 68 ActionScript language and syntax Language overview Objects lie at the heart of the ActionScript 3.0 language—they are its fundamental building blocks. Every variable you declare, every function you write, and every class instance you create is an object. You can think of an ActionScript 3.0 program as a group of objects that carry out tasks, respond to events, and communicate with one another. Programmers familiar with object-oriented programming (OOP) in Java or C++ may think of objects as modules that contain two kinds of members: data stored in member variables or properties, and behavior accessible through methods. The ECMAScript edition 4 draft, the standard upon which ActionScript 3.0 is based, defines objects in a similar but slightly different way. In the ECMAScript draft, objects are simply collections of properties. These properties are containers that can hold not only data, but also functions or other objects. If a function is attached to an object in this way, it is called a method. While the ECMAScript draft definition may seem a little odd to programmers with a Java or C++ background, in practice, defining object types with ActionScript 3.0 classes is very similar to the way classes are defined in Java or C++. The distinction between the two definitions of object is important when discussing the ActionScript object model and other advanced topics, but in most other situations the term properties means class member variables as opposed to methods. The ActionScript 3.0 Language and Components Reference, for example, uses the term properties to mean variables or getter-setter properties. It uses the term methods to mean functions that are part of a class. One subtle difference between classes in ActionScript and classes in Java or C++ is that in ActionScript, classes are not just abstract entities. ActionScript classes are represented by class objects that store the class’s properties and methods. This allows for techniques that may seem alien to Java and C++ programmers, such as including statements or executable code at the top level of a class or package. Another difference between ActionScript classes and Java or C++ classes is that every ActionScript class has something called a prototype object. In previous versions of ActionScript, prototype objects, linked together into prototype chains, served collectively as the foundation of the entire class inheritance hierarchy. In ActionScript 3.0, however, prototype objects play only a small role in the inheritance system. The prototype object can still be useful, however, as an alternative to static properties and methods if you want to share a property and its value among all the instances of a class.
  • 69. Objects and classes 69 In the past, advanced ActionScript programmers could directly manipulate the prototype chain with special built-in language elements. Now that the language provides a more mature implementation of a class-based programming interface, many of these special language elements, such as __proto__ and __resolve, are no longer part of the language. Moreover, optimizations of the internal inheritance mechanism that provide significant Flash Player performance improvements preclude direct access to the inheritance mechanism. Objects and classes In ActionScript 3.0, every object is defined by a class. A class can be thought of as a template or a blueprint for a type of object. Class definitions can include variables and constants, which hold data values, and methods, which are functions that encapsulate behavior bound to the class. The values stored in properties can be primitive values or other objects. Primitive values are numbers, strings, or Boolean values. ActionScript contains a number of built-in classes that are part of the core language. Some of these built-in classes, such as Number, Boolean and String, represent the primitive values available in ActionScript. Others, such as the Array, Math, and XML classes, define more complex objects that are part of the ECMAScript standard. All classes, whether built-in or user-defined, derive from the Object class. For programmers with previous ActionScript experience, it is important to note that the Object data type is no longer the default data type, even though all other classes still derive from it. In ActionScript 2.0, the following two lines of code were equivalent because the lack of a type annotation meant that a variable would be of type Object: var someObj:Object; var someObj; ActionScript 3.0, however, introduces the concept of untyped variables, which can be designated in the following two ways: var someObj:*; var someObj; An untyped variable is not the same as a variable of type Object. The key difference is that untyped variables can hold the special value undefined, while a variable of type Object cannot hold that value. You can define your own classes using the class keyword. You can declare class properties in three ways: constants can be defined with the const keyword, variables are defined with the var keyword, and getter and setter properties are defined by using the get and set attributes in a method declaration. You can declare methods with the function keyword.
  • 70. 70 ActionScript language and syntax You create an instance of a class by using the new operator. The following example creates an instance of the Date class called myBirthday. var myBirthday:Date = new Date(); Packages and namespaces Packages and namespaces are related concepts. Packages allow you to bundle class definitions together in a way that facilitates code sharing and minimizes naming conflicts. Namespaces allow you to control the visibility of identifiers, such as property and method names, and can be applied to code whether it resides inside or outside a package. Packages let you organize your class files, and namespaces let you manage the visibility of individual properties and methods. Packages Packages in ActionScript 3.0 are implemented with namespaces, but are not synonymous with them. When you declare a package, you are implicitly creating a special type of namespace that is guaranteed to be known at compile time. Namespaces, when created explicitly, are not necessarily known at compile time. The following example uses the package directive to create a simple package containing one class: package samples { public class SampleCode { public var sampleGreeting:String; public function sampleFunction() { trace(sampleGreeting + " from sampleFunction()"); } } } The name of the class in this example is SampleCode. Because the class is inside the samples package, the compiler automatically qualifies the class name at compile time into its fully qualified name: samples.SampleCode. The compiler also qualifies the names of any properties or methods, so that sampleGreeting and sampleFunction() become samples.SampleCode.sampleGreeting and samples.SampleCode.sampleFunction(), respectively.
  • 71. Packages and namespaces 71 Many developers, especially those with Java programming backgrounds, may choose to place only classes at the top level of a package. ActionScript 3.0, however, supports not only classes at the top level of a package, but also variables, functions, and even statements. One advanced use of this feature is to define a namespace at the top level of a package so that it will be available to all classes in that package. Note, however, that only two access specifiers, public and internal, are allowed at the top level of a package. Unlike Java, which allows you to declare nested classes as private, ActionScript 3.0 supports neither nested nor private classes. In many other ways, however, ActionScript 3.0 packages are similar to packages in the Java programming language. As you can see in the previous example, fully qualified package references are expressed using the dot operator (.), just as they are in Java. You can use packages to organize your code into an intuitive hierarchical structure for use by other programmers. This facilitates code sharing by allowing you to create your own package to share with others, and to use packages created by others in your code. The use of packages also helps to ensure that the identifier names that you use are unique and do not conflict with other identifier names. In fact, some would argue that this is the primary benefit of packages. For example, two programmers who wish to share their code with each other may have each created a class called SampleCode. Without packages, this would create a name conflict, and the only resolution would be to rename one of the classes. With packages, however, the name conflict is easily avoided by placing one, or preferably both, of the classes in packages with unique names. You can also include embedded dots in your package name to create nested packages. This allows you to create a hierarchical organization of packages. A good example of this is the flash.xml package provided by the Flash Player API. The flash.xml package is nested inside the flash package. The flash.xml package contains the legacy XML parser that was used in previous versions of ActionScript. One reason that it now resides in the flash.xml package is that the name of the legacy XML class conflicts with the name of the new XML class that implements the XML for ECMAScript (E4X) specification functionality available in ActionScript 3.0. Although moving the legacy XML class into a package is a good first step, most users of the legacy XML classes will import the flash.xml package, which will generate the same name conflict unless you remember to always use the fully qualified name of the legacy XML class (flash.xml.XML). To avoid this situation, the legacy XML class is now named XMLDocument, as the following example shows:
  • 72. 72 ActionScript language and syntax package flash.xml { class XMLDocument {} class XMLNode {} class XMLSocket {} } Most of the Flash Player API is organized under the flash package. For example, the flash.display package contains the display list API, and the flash.events package contains the new event model. Creating packages ActionScript 3.0 provides significant flexibility in the way you organize your packages, classes, and source files. Previous versions of ActionScript allowed only one class per source file and required that the name of the source file match the name of the class. ActionScript 3.0 allows you to include multiple classes in one source file, but only one class in each file can be made available to code that is external to that file. In other words, only one class in each file can be declared inside a package declaration. You must declare any additional classes outside your package definition, which makes those classes invisible to code outside that source file. The name of the class declared inside the package definition must match the name of the source file. ActionScript 3.0 also provides more flexibility in the way you declare packages. In previous versions of ActionScript, packages merely represented directories in which you placed source files, and you didn’t declare packages with the package statement, but rather included the package name as part of the fully qualified class name in your class declaration. Although packages still represent directories in ActionScript 3.0, packages can contain more than just classes. In ActionScript 3.0, you use the package statement to declare a package, which means that you can also declare variables, functions, and namespaces at the top level of a package. You can even include executable statements at the top level of a package. If you do declare variables, functions, or namespaces at the top level of a package, the only attributes available at that level are public and internal, and only one package-level declaration per file can use the public attribute, whether that declaration is a class, variable, function, or namespace. Packages are useful for organizing your code and for preventing name conflicts. You should not confuse the concept of packages with the unrelated concept of class inheritance. Two classes that reside in the same package will have a namespace in common, but are not necessarily related to each other in any other way. Likewise, a nested package may have no semantic relationship to its parent package.
  • 73. Packages and namespaces 73 Importing packages If you want to use a class that is inside a package, you must import either the package or the specific class. This differs from ActionScript 2.0, where importing classes was optional. For example, consider the SampleCode class example from earlier in this chapter. If the class resides in a package named samples, you must use one of the following import statements before using the SampleCode class: import samples.*; or import samples.SampleCode; In general, import statements should be as specific as possible. If you plan to use only the SampleCode class from the samples package, you should import only the SampleCode class rather than the entire package to which it belongs. Importing entire packages may lead to unexpected name conflicts. You must also place the source code that defines the package or class within your classpath. The classpath is a user-defined list of local directory paths that determines where the compiler will search for imported packages and classes. The classpath is sometimes called the build path or source path. After you have properly imported the class or package, you can use either the fully qualified name of the class (samples.SampleCode) or merely the class name by itself (SampleCode). Fully qualified names are useful when identically named classes, methods, or properties result in ambiguous code, but can be difficult to manage if used for all identifiers. For example, the use of the fully qualified name results in verbose code when you instantiate a SampleCode class instance: var mySample:samples.SampleCode = new samples.SampleCode(); As the levels of nested packages increase, the readability of your code decreases. In situations where you are confident that ambiguous identifiers will not be a problem, you can make your code easier to read by using simple identifiers. For example, instantiating a new instance of the SampleCode class is much less verbose if you use only the class identifier: var mySample:SampleCode = new SampleCode(); If you attempt to use identifier names without first importing the appropriate package or class, the compiler will not be able to find the class definitions. On the other hand, if you do import a package or class, any attempt to define a name that conflicts with an imported name will generate an error.
  • 74. 74 ActionScript language and syntax When a package is created, the default access specifier for all members of that package is internal, which means that, by default, package members are only visible to other members of that package. If you want a class to be available to code outside the package, you must declare that class to be public. For example, the following package contains two classes, SampleCode and CodeFormatter: // SampleCode.as file package samples { public class SampleCode {} } // CodeFormatter.as file package samples { class CodeFormatter {} } The SampleCode class is visible outside the package because it is declared as a public class. The CodeFormatter class, however, is visible only within the samples package itself. If you attempt to access the CodeFormatter class outside the samples package, you will generate an error, as the following example shows: import samples.SampleCode; import samples.CodeFormatter; var mySample:SampleCode = new SampleCode(); // okay, public class var myFormatter:CodeFormatter = new CodeFormatter(); // error If you want both classes to be available outside the package, you must declare both classes to be public. You cannot apply the public attribute to the package declaration. Fully qualified names are useful for resolving name conflicts that may occur when using packages. Such a scenario may arise if you import two packages that define classes with the same identifier. For example, consider the following package, which also has a class named SampleCode: package langref.samples { public class SampleCode {} } If you import both classes, as follows, you will have a name conflict when referring to the SampleCode class: import samples.SampleCode; import langref.samples.SampleCode; var mySample:SampleCode = new SampleCode(); // name conflict
  • 75. Packages and namespaces 75 The compiler has no way of knowing which SampleCode class to use. To resolve this conflict, you must use the fully qualified name of each class, as follows: var sample1:samples.SampleCode = new samples.SampleCode(); var sample2:langref.samples.SampleCode = new langref.samples.SampleCode(); Namespaces Namespaces give you control over the visibility of the properties and methods that you create. Think of the public, private, protected, and internal access control specifiers as built- in namespaces. If these predefined access control specifiers do not suit your needs, you can create your own namespaces. If you are familiar with XML namespaces, much of this discussion will not be new to you, although the syntax and details of the ActionScript implementation are slightly different from those of XML. If you have never worked with namespaces before, the concept itself is straightforward, but the implementation has specific terminology that you will need to learn. To understand how namespaces work, it helps to know that the name of a property or method always contains two parts: an identifier and a namespace. The identifier is what you generally think of as a name. For example, the identifiers in the following class definition are sampleGreeting and sampleFunction(): class SampleCode { var sampleGreeting:String; function sampleFunction () { trace(sampleGreeting + " from sampleFunction()"); } } Whenever definitions are not preceded by a namespace attribute, their names are qualified by the default internal namespace, which means they are visible only to callers in the same package. If the compiler is set to strict mode, the compiler issues a warning that the internal namespace applies to any identifier without a namespace attribute. To ensure that an identifier is available everywhere, you must specifically precede the identifier name with the public attribute. In the previous example code, both sampleGreeting and sampleFunction() have a namespace value of internal. NOTE Programmers with a C++ background often confuse the import statement with #include. The #include directive is necessary in C++ because C++ compilers process one file at a time, and will not look in other files for class definitions unless a header file is explicitly included. ActionScript 3.0 has an include directive, but it is not designed to import classes and packages. To import classes or packages in ActionScript 3.0, you must use the import statement and place the source file that contains the package in the class path.
  • 76. 76 ActionScript language and syntax There are three basic steps to follow when using namespaces. First, you must define the namespace using the namespace keyword. For example, the following code defines the version1 namespace: namespace version1; Second, you apply your namespace by using it instead of an access control specifier in a property or method declaration. The following example places a function named myFunction() into the version1 namespace: version1 function myFunction() {} Third, once you’ve applied the namespace, you can reference it with the use directive or by qualifying the name of an identifier with a namespace. The following example references the myFunction() function through the use directive: use namespace version1; myFunction(); You can also use a qualified name to reference the myFunction() function, as the following example shows: version1::myFunction(); Defining namespaces Namespaces contain one value, the Uniform Resource Identifier (URI), which is sometimes called the namespace name. A URI allows you to ensure that your namespace definition is unique. You create a namespace by declaring a namespace definition in one of two ways. You can either define a namespace with an explicit URI, as you would define an XML namespace, or you can omit the URI. The following example shows how a namespace can be defined using a URI: namespace flash_proxy = “http://www.adobe.com/flash/proxy”; The URI serves as a unique identification string for that namespace. If you omit the URI, as in the following example, the compiler will create a unique internal identification string in place of the URI. You do not have access to this internal identification string. namespace flash_proxy; Once you define a namespace, with or without a URI, that namespace cannot be redefined in the same scope. An attempt to define a namespace that has been defined earlier in the same scope results in a compiler error.
  • 77. Packages and namespaces 77 If a namespace is defined within a package or a class, the namespace may not be visible to code outside that package or class unless the appropriate access control specifier is used. For example, the following code shows the flash_proxy namespace defined within the flash.utils package. In the following example, the lack of an access control specifier means that the flash_proxy namespace would be visible only to code within the flash.utils package and would not be visible to any code outside the package: package flash.utils { namespace flash_proxy; } The following code uses the public attribute to make the flash_proxy namespace visible to code outside the package: package flash.utils { public namespace flash_proxy; } Applying namespaces Applying a namespace means placing a definition into a namespace. Definitions that can be placed into namespaces include functions, variables, and constants (you cannot place a class into a custom namespace). Consider, for example, a function declared using the public access control namespace. Using the public attribute in a function definition places the function into the public namespace, which makes the function available to all code. Once you have defined a namespace, you can use the namespace that you defined the same way you would use the public attribute, and the definition will be available to code that can reference your custom namespace. For example, if you define a namespace example1, you can add a method called myFunction() using example1 as an attribute, as the following example shows: namespace example1; class someClass { example1 myFunction() {} }
  • 78. 78 ActionScript language and syntax Declaring the myFunction() method using the namespace example1 as an attribute means that the method belongs to the example1 namespace. You should bear in mind the following when applying namespaces: ■ You can apply only one namespace to each declaration. ■ There is no way to apply a namespace attribute to more than one definition at a time. In other words, if you want to apply your namespace to ten different functions, you must add your namespace as an attribute to each of the ten function definitions. ■ If you apply a namespace, you cannot also specify an access control specifier because namespaces and access control specifiers are mutually exclusive. In other words, you cannot declare a function or property as public, private, protected, or internal in addition to applying your namespace. Referencing namespaces There is no need to explicitly reference a namespace when you use a method or property declared with any of the access control namespaces, such as public, private, protected, and internal. This is because access to these special namespaces is controlled by context. For example, definitions placed into the private namespace are automatically available to code within the same class. For namespaces that you define, however, such context sensitivity does not exist. In order to use a method or property that you have placed into a custom namespace, you must reference the namespace. You can reference namespaces with the use namespace directive or you can qualify the name with the namespace using the name qualifier (::) punctuator. Referencing a namespace with the use namespace directive “opens” the namespace, so that it can apply to any identifiers that are not qualified. For example, if you have defined the example1 namespace, you can access names in that namespace by using use namespace example1: use namespace example1; myFunction(); You can open more than one namespace at a time. Once you open a namespace with use namespace, it remains open throughout the block of code in which it was opened. There is no way to explicitly close a namespace. Having more than one open namespace, however, increases the likelihood of name conflicts. If you prefer not to open a namespace, you can avoid the use namespace directive by qualifying the method or property name with the namespace and the name qualifier punctuator. For example, the following code shows how you can qualify the name myFunction() with the example1 namespace: example1::myFunction();
  • 79. Packages and namespaces 79 Using namespaces You can find a real-world example of a namespace that is used to prevent name conflicts in the flash.utils.Proxy class that is part of the Flash Player API. The Proxy class, which is the replacement for the Object.__resolve property from ActionScript 2.0, allows you to intercept references to undefined properties or methods before an error occurs. All of the methods of the Proxy class reside in the flash_proxy namespace in order to prevent name conflicts. To better understand how the flash_proxy namespace is used, you need to understand how to use the Proxy class. The functionality of the Proxy class is available only to classes that inherit from it. In other words, if you want to use the methods of the Proxy class on an object, the object’s class definition must extend the Proxy class. For example, if you want to intercept attempts to call an undefined method, you would extend the Proxy class and then override the callProperty() method of the Proxy class. You may recall that implementing namespaces is usually a three-step process of defining, applying, and then referencing a namespace. Because you never explicitly call any of the Proxy class methods, however, the flash_proxy namespace is only defined and applied, but never referenced. The Flash Player API defines the flash_proxy namespace and applies it in the Proxy class. Your code only needs to apply the flash_proxy namespace to classes that extend the Proxy class. The flash_proxy namespace is defined in the flash.utils package in a manner similar to the following: package flash.utils { public namespace flash_proxy; } The namespace is applied to the methods of the Proxy class as shown in the following excerpt from the Proxy class: public class Proxy { flash_proxy function callProperty(name:*, ... rest):* flash_proxy function deleteProperty(name:*):Boolean ... } As the following code shows, you must first import both the Proxy class and the flash_proxy namespace. You must then declare your class such that it extends the Proxy class (you must also add the dynamic attribute if you are compiling in strict mode). When you override the callProperty() method, you must use the flash_proxy namespace.
  • 80. 80 ActionScript language and syntax package { import flash.utils.Proxy; import flash.utils.flash_proxy; dynamic class MyProxy extends Proxy { flash_proxy override function callProperty(name:*, ...rest):* { trace("method call intercepted: " + name); } } } If you create an instance of the MyProxy class and call an undefined method, such as the testing() method called in the following example, your Proxy object intercepts the method call and executes the statements inside the overridden callProperty() method (in this case, a simple trace() statement). var mySample:MyProxy = new MyProxy(); mySample.testing(); // method call intercepted: testing There are two advantages to having the methods of the Proxy class inside the flash_proxy namespace. First, having a separate namespace reduces clutter in the public interface of any class that extends the Proxy class. (There are about a dozen methods in the Proxy class that you can override, all of which are not designed to be called directly. Placing all of them in the public namespace could be confusing.) Second, use of the flash_proxy namespace avoids name conflicts in case your Proxy subclass contains instance methods with names that match any of the Proxy class methods. For example, you may want to name one of your own methods callProperty(). The following code is acceptable, because your version of the callProperty() method is in a different namespace: dynamic class MyProxy extends Proxy { public function callProperty() {} flash_proxy override function callProperty(name:*, ...rest):* { trace("method call intercepted: " + name); } } Namespaces can also be helpful when you want to provide access to methods or properties in a way that cannot be accomplished with the four access control specifiers (public, private, internal, and protected). For example, you may have a few utility methods that are spread out across several packages. You want these methods available to all of your packages, but you don’t want the methods to be public. To accomplish this, you can create a new namespace and use it as your own special access control specifier.
  • 81. Packages and namespaces 81 The following example uses a user-defined namespace to group together two functions that reside in different packages. By grouping them into the same namespace, you can make both functions visible to a class or package through a single use namespace statement. This example uses four files to demonstrate the technique. All of the files must be within your classpath. The first file, myInternal.as, is used to define the myInternal namespace. Because the file is in a package named example, you must place the file into a folder named example. The namespace is marked as public so that it can be imported into other packages. // myInternal.as in folder example package example { public namespace myInternal = "http://www.adobe.com/2006/actionscript/ examples"; } The second and third files, Utility.as and Helper.as, define the classes that contain methods that should be available to other packages. The Utility class is in the example.alpha package, which means that the file should be placed inside a folder named alpha that is a subfolder of the example folder. The Helper class is in the example.beta package, which means that the file should be placed inside a folder named beta that is also a subfolder of the example folder. Both of these packages, example.alpha and example.beta, must import the namespace before using it. // Utility.as in the example/alpha folder package example.alpha { import example.myInternal; public class Utility { private static var _taskCounter:int = 0; public static function someTask() { _taskCounter++; } myInternal static function get taskCounter():int { return _taskCounter; } } }
  • 82. 82 ActionScript language and syntax // Helper.as in the example/beta folder package example.beta { import example.myInternal; public class Helper { private static var _timeStamp:Date; public static function someTask() { _timeStamp = new Date(); } myInternal static function get lastCalled():Date { return _timeStamp; } } } The fourth file, NamespaceUseCase.as, is the main application class, and should be a sibling to the example folder. In Adobe Flash CS3 Professional, this class would be used as the document class for the FLA. The NamespaceUseCase class also imports the myInternal namespace and uses it to call the two static methods that reside in the other packages. The example uses static methods only to simplify the code. Both static and instance methods can be placed in the myInternal namespace. // NamespaceUseCase.as package { import flash.display.MovieClip; import example.myInternal; // import namespace import example.alpha.Utility; // import Utility class import example.beta.Helper; // import Helper class public class NamespaceUseCase extends MovieClip { public function NamespaceUseCase() { use namespace myInternal; Utility.someTask(); Utility.someTask(); trace(Utility.taskCounter); // 2
  • 83. Variables 83 Helper.someTask(); trace(Helper.lastCalled); // [time someTask() was last called] } } } Variables Variables allow you to store values that you use in your program. To declare a variable, you must use the var statement with the variable name. In ActionScript 2.0, use of the var statement is only required if you use type annotations. In ActionScript 3.0, use of the var statement is always required. For example, the following line of ActionScript declares a variable named i: var i; If you omit the var statement when declaring a variable, you will get a compiler error in strict mode and run-time error in standard mode. For example, the following line of code will result in an error if the variable i has not been previously defined: i; // error if i was not previously defined To associate a variable with a data type, you must do so when you declare the variable. Declaring a variable without designating the variable’s type is legal, but will generate a compiler warning in strict mode. You designate a variable’s type by appending the variable name with a colon (:), followed by the variable’s type. For example, the following code declares a variable i that is of type int: var i:int; You can assign a value to a variable using the assignment operator (=). For example, the following code declares a variable i and assigns the value 20 to it: var i:int; i = 20; You may find it more convenient to assign a value to a variable at the same time that you declare the variable, as in the following example: var i:int = 20; The technique of assigning a value to a variable at the time it is declared is commonly used not only when assigning primitive values such as integers and strings, but also when creating an array or instantiating an instance of a class. The following example shows an array that is declared and assigned a value using one line of code. var numArray:Array = ["zero", "one", "two"];
  • 84. 84 ActionScript language and syntax You can create an instance of a class by using the new operator. The following example creates an instance of a named CustomClass, and assigns a reference to the newly created class instance to the variable named customItem: var customItem:CustomClass = new CustomClass(); If you have more than one variable to declare, you can declare them all on one line of code by using the comma operator (,) to separate the variables. For example, the following code declares three variables on one line of code: var a:int, b:int, c:int; You can also assign values to each of the variables on the same line of code. For example, the following code declares three variables (a, b, and c) and assigns each a value: var a:int = 10, b:int = 20, c:int = 30; Although you can use the comma operator to group variable declarations into one statement, doing so may reduce the readability of your code. Understanding variable scope The scope of a variable is the area of your code where the variable can be accessed by a lexical reference. A global variable is one that is defined in all areas of your code, whereas a local variable is one that is defined in only one part of your code. In ActionScript 3.0, variables are always assigned the scope of the function or class in which they are declared. A global variable is a variable that you define outside of any function or class definition. For example, the following code creates a global variable strGlobal by declaring it outside of any function. The example shows that a global variable is available both inside and outside the function definition. var strGlobal:String = "Global"; function scopeTest() { trace(strGlobal); // Global } scopeTest(); trace(strGlobal); // Global You declare a local variable by declaring the variable inside a function definition. The smallest area of code for which you can define a local variable is a function definition. A local variable declared within a function will exist only in that function. For example, if you declare a variable named str2 within a function named localScope(), that variable will not be available outside the function. function localScope() { var strLocal:String = "local";
  • 85. Variables 85 } localScope(); trace(strLocal); // error because strLocal is not defined globally If the variable name you use for your local variable is already declared as a global variable, the local definition hides (or shadows) the global definition while the local variable is in scope. The global variable will still exist outside of the function. For example, the following code creates a global string variable named str1, and then creates a local variable of the same name inside the scopeTest() function. The trace statement inside the function outputs the local value of the variable, but the trace statement outside the function outputs the global value of the variable. var str1:String = "Global"; function scopeTest () { var str1:String = "Local"; trace(str1); // Local } scopeTest(); trace(str1); // Global ActionScript variables, unlike variables in C++ and Java, do not have block-level scope. A block of code is any group of statements between an opening curly brace ( { ) and a closing curly brace ( } ). In some programming languages, such as C++ and Java, variables declared inside a block of code are not available outside that block of code. This restriction of scope is called block-level scope, and does not exist in ActionScript. If you declare a variable inside a block of code, that variable will be available not only in that block of code, but also in any other parts of the function to which the code block belongs. For example, the following function contains variables that are defined in various block scopes. All the variables are available throughout the function. function blockTest (testArray:Array) { var numElements:int = testArray.length; if (numElements > 0) { var elemStr:String = "Element #"; for (var i:int = 0; i < numElements; i++) { var valueStr:String = i + ": " + testArray[i]; trace(elemStr + valueStr); } trace(elemStr, valueStr, i); // all still defined } trace(elemStr, valueStr, i); // all defined if numElements > 0 } blockTest(["Earth", "Moon", "Sun"]);
  • 86. 86 ActionScript language and syntax An interesting implication of the lack of block-level scope is that you can read or write to a variable before it is declared, as long as it is declared before the function ends. This is because of a technique called hoisting, which means that the compiler moves all variable declarations to the top of the function. For example, the following code compiles even though the initial trace() function for the num variable happens before the num variable is declared: trace(num); // NaN var num:Number = 10; trace(num); // 10 The compiler will not, however, hoist any assignment statements. This explains why the initial trace() of num results in NaN (not a number), which is the default value for variables of the Number data type. This means that you can assign values to variables even before they are declared, as shown in the following example: num = 5; trace(num); // 5 var num:Number = 10; trace(num); // 10 Default values A default value is the value that a variable contains before you set its value. You initialize a variable when you set its value for the first time. If you declare a variable, but do not set its value, that variable is uninitialized. The value of an uninitialized variable depends on its data type. The following table describes the default values of variables, organized by data type: For variables of type Number, the default value is NaN (not a number), which is a special value defined by the IEEE-754 standard to mean a value that does not represent a number. Data type Default value Boolean false int 0 Number NaN Object null String null uint 0 Not declared (equivalent to type annotation *) undefined All other classes, including user-defined classes. null
  • 87. Data types 87 If you declare a variable, but do not declare its data type, the default data type * will apply, which actually means that the variable is untyped. If you also do not initialize an untyped variable with a value, its default value is undefined. For data types other than Boolean, Number, int, and uint, the default value of any uninitialized variable is null. This applies to all the classes defined by the Flash Player API, as well as any custom classes that you create. The value null is not a valid value for variables of type Boolean, Number, int, or uint. If you attempt to assign a value of null to a such a variable, the value is converted to the default value for that data type. For variables of type Object, you can assign a value of null. If you attempt to assign the value undefined to a variable of type Object, the value is converted to null. For variables of type Number, there is a special top-level function named isNaN() that returns the Boolean value true if the variable is not a number, and false otherwise. Data types A data type defines a set of values. For example, the Boolean data type is the set of exactly two values: true and false. In addition to the Boolean data type, ActionScript 3.0 defines several more commonly used data types, such as String, Number, and Array. You can define your own data types by using classes or interfaces to define a custom set of values. All values in ActionScript 3.0, whether they are primitive or complex, are objects. A primitive value is a value that belongs to one of the following data types: Boolean, int, Number, String, and uint. Working with primitive values is usually faster than working with complex values, because ActionScript stores primitive values in a special way that makes memory and speed optimizations possible. A complex value is a value that is not a primitive value. Data types that define sets of complex values include Array, Date, Error, Function, RegExp, XML, and XMLList. NOTE For readers interested in the technical details, ActionScript stores primitive values internally as immutable objects. The fact that they are stored as immutable objects means that passing by reference is effectively the same as passing by value. This cuts down on memory usage and increases execution speed, because references are usually significantly smaller than the values themselves.
  • 88. 88 ActionScript language and syntax Many programming languages distinguish between primitive values and their wrapper objects. Java, for example, has an int primitive and the java.lang.Integer class that wraps it. Java primitives are not objects, but their wrappers are, which makes primitives useful for some operations, and wrapper objects better suited for other operations. In ActionScript 3.0, primitive values and their wrapper objects are, for practical purposes, indistinguishable. All values, even primitive values, are objects. Flash Player treats these primitive types as special cases that behave like objects but that don’t require the normal overhead associated with creating objects. This means that the following two lines of code are equivalent: var someInt:int = 3; var someInt:int = new int(3); All the primitive and complex data types listed above are defined by the ActionScript 3.0 core classes. The core classes allow you to create objects using literal values instead of using the new operator. For example, you can create an array using a literal value or the Array class constructor, as follows: var someArray:Array = [1, 2, 3]; // literal value var someArray:Array = new Array(1,2,3); // Array constructor Type checking Type checking can occur at either compile time or run time. Statically typed languages, such as C++ and Java, do type checking at compile time. Dynamically typed languages, such as Smalltalk and Python, handle type checking at run time. As a dynamically typed language, ActionScript 3.0 has run-time type checking, but also supports compile-time type checking with a special compiler mode called strict mode. In strict mode, type checking occurs at both compile time and run time, but in standard mode, type checking occurs only at run time. Dynamically typed languages offer tremendous flexibility when you structure your code, but at the cost of allowing type errors to manifest at run time. Statically typed languages report type errors at compile time, but at the cost of requiring that type information be known at compile time. Compile-time type checking Compile-time type checking is often favored in larger projects because as the size of a project grows, data type flexibility usually becomes less important than catching type errors as early as possible. This is why, by default, the ActionScript compiler in Adobe Flash CS3 Professional and Adobe Flex Builder 2 is set to run in strict mode.
  • 89. Data types 89 In order to provide compile-time type checking, the compiler needs to know the data type information for the variables or expressions in your code. To explicitly declare a data type for a variable, add the colon operator (:) followed by the data type as a suffix to the variable name. To associate a data type with a parameter, use the colon operator followed by the data type. For example, the following code adds data type information to the xParam parameter, and declares a variable myParam with an explicit data type: function runtimeTest(xParam:String) { trace(xParam); } var myParam:String = “hello”; runtimeTest(myParam); In strict mode, the ActionScript compiler reports type mismatches as compiler errors. For example, the following code declares a function parameter xParam, of type Object, but later attempts to assign values of type String and Number to that parameter. This produces a compiler error in strict mode. function dynamicTest(xParam:Object) { if (xParam is String) { var myStr:String = xParam; // compiler error in strict mode trace("String: " + myStr); } else if (xParam is Number) { var myNum:Number = xParam; // compiler error in strict mode trace("Number: " + myNum); } } Even in strict mode, however, you can selectively opt of out compile-time type checking by leaving the right side of an assignment statement untyped. You can mark a variable or expression as untyped by either omitting a type annotation, or using the special asterisk (*) type annotation. For example, if the xParam parameter in the previous example is modified so that it no longer has a type annotation, the code will compile in strict mode: function dynamicTest(xParam) { if (xParam is String) { var myStr:String = xParam; trace("String: " + myStr); } else if (xParam is Number) {
  • 90. 90 ActionScript language and syntax var myNum:Number = xParam; trace("Number: " + myNum); } } dynamicTest(100) dynamicTest("one hundred"); Run-time type checking Run-time type checking occurs in ActionScript 3.0 whether you compile in strict mode or standard mode. Consider a situation in which the value 3 is passed as an argument to a function that expects an array. In strict mode, the compiler will generate an error, because the value 3 is not compatible with the data type Array. If you disable strict mode, and run in standard mode, the compiler does not complain about the type mismatch, but run-time type checking by Flash Player results in a run-time error. The following example shows a function named typeTest() that expects an Array argument but is passed a value of 3. This causes a run-time error in standard mode, because the value 3 is not a member of the parameter’s declared data type (Array). function typeTest(xParam:Array) { trace(xParam); } var myNum:Number = 3; typeTest(myNum); // run-time error in ActionScript 3.0 standard mode There may also be situations where you get a run-time type error even when you are operating in strict mode. This is possible if you use strict mode, but opt out of compile-time type checking by using an untyped variable. When you use an untyped variable, you are not eliminating type checking but rather deferring it until run time. For example, if the myNum variable in the previous example does not have a declared data type, the compiler cannot detect the type mismatch, but Flash Player will generate a run-time error because it compares the run-time value of myNum, which is set to 3 as a result of the assignment statement, with the type of xParam, which is set to the Array data type. function typeTest(xParam:Array) { trace(xParam); } var myNum = 3; typeTest(myNum); // run-time error in ActionScript 3.0
  • 91. Data types 91 Run-time type checking also allows more flexible use of inheritance than does compile-time checking. By deferring type checking to run time, standard mode allows you to reference properties of a subclass even if you upcast. An upcast occurs when you use a base class to declare the type of a class instance but use a subclass to instantiate it. For example, you can create a class named ClassBase that can be extended (classes with the final attribute cannot be extended): class ClassBase { } You can subsequently create a subclass of ClassBase named ClassExtender, which has one property named someString, as follows: class ClassExtender extends ClassBase { var someString:String; } Using both classes, you can create a class instance that is declared using the ClassBase data type, but instantiated using the ClassExtender constructor. An upcast is considered a safe operation, because the base class does not contain any properties or methods that are not in the subclass. var myClass:ClassBase = new ClassExtender(); A subclass, however, does contain properties or methods that its base class does not. For example, the ClassExtender class contains the someString property, which does not exist in the ClassBase class. In ActionScript 3.0 standard mode, you can reference this property using the myClass instance without generating a compile-time error, as shown in the following example: var myClass:ClassBase = new ClassExtender(); myClass.someString = "hello"; // no error in ActionScript 3.0 standard mode The is operator The is operator, which is new for ActionScript 3.0, allows you to test whether a variable or expression is a member of a given data type. In previous versions of ActionScript, the instanceof operator provided this functionality, but in ActionScript 3.0 the instanceof operator should not be used to test for data type membership. The is operator should be used instead of the instanceof operator for manual type checking, because the expression x instanceof y merely checks the prototype chain of x for the existence of y (and in ActionScript 3.0, the prototype chain does not provide a complete picture of the inheritance hierarchy).
  • 92. 92 ActionScript language and syntax The is operator examines the proper inheritance hierarchy and can be used to check not only whether an object is an instance of a particular class, but also whether an object is an instance of a class that implements a particular interface. The following example creates an instance of the Sprite class named mySprite and uses the is operator to test whether mySprite is an instance of the Sprite and DisplayObject classes, and whether it implements the IEventDispatcher interface: var mySprite:Sprite = new Sprite(); trace(mySprite is Sprite); // true trace(mySprite is DisplayObject); // true trace(mySprite is IEventDispatcher); // true The is operator checks the inheritance hierarchy and properly reports that mySprite is compatible with the Sprite and DisplayObject classes (the Sprite class is a subclass of the DisplayObject class). The is operator also checks whether mySprite inherits from any classes that implement the IEventDispatcher interface. Because the Sprite class inherits from the EventDispatcher class, which implements the IEventDispatcher interface, the is operator correctly reports that mySprite implements the same interface. The following example shows the same tests from the previous example, but with instanceof instead of the is operator. The instanceof operator correctly identifies that mySprite is an instance of Sprite or DisplayObject, but it returns false when used to test whether mySprite implements the IEventDispatcher interface. trace(mySprite instanceof Sprite); // true trace(mySprite instanceof DisplayObject); // true trace(mySprite instanceof IEventDispatcher); // false The as operator The as operator, which is new in ActionScript 3.0, also allows you to check whether an expression is a member of a given data type. Unlike the is operator, however, the as operator does not return a Boolean value. Rather, the as operator returns the value of the expression instead of true, and null instead of false. The following example shows the results of using the as operator instead of the is operator in the simple case of checking whether a Sprite instance is a member of the DisplayObject, IEventDispatcher, and Number data types. var mySprite:Sprite = new Sprite(); trace(mySprite as Sprite); // [object Sprite] trace(mySprite as DisplayObject); // [object Sprite] trace(mySprite as IEventDispatcher); // [object Sprite] trace(mySprite as Number); // null When you use the as operator, the operand on the right must be a data type. An attempt to use an expression other than a data type as the operand on the right will result in an error.
  • 93. Data types 93 Dynamic classes A dynamic class defines an object that can be altered at run time by adding or changing properties and methods. A class that is not dynamic, such as the String class, is a sealed class. You cannot add properties or methods to a sealed class at run time. You create dynamic classes by using the dynamic attribute when you declare a class. For example, the following code creates a dynamic class named Protean: dynamic class Protean { private var privateGreeting:String = "hi"; public var publicGreeting:String = "hello"; function Protean() { trace("Protean instance created"); } } If you subsequently instantiate an instance of the Protean class, you can add properties or methods to it outside the class definition. For example, the following code creates an instance of the Protean class and adds a property named aString and a property named aNumber to the instance: var myProtean:Protean = new Protean(); myProtean.aString = "testing"; myProtean.aNumber = 3; trace(myProtean.aString, myProtean.aNumber); // testing 3 Properties that you add to an instance of a dynamic class are run-time entities, so any type checking is done at run time. You cannot add a type annotation to a property that you add in this manner. You can also add a method to the myProtean instance by defining a function and attaching the function to a property of the myProtean instance. The following code moves the trace statement into a method named traceProtean(): var myProtean:Protean = new Protean(); myProtean.aString = "testing"; myProtean.aNumber = 3; myProtean.traceProtean = function () { trace(this.aString, this.aNumber); }; myProtean.traceProtean(); // testing 3
  • 94. 94 ActionScript language and syntax Methods created in this way, however, do not have access to any private properties or methods of the Protean class. Moreover, even references to public properties or methods of the Protean class must be qualified with either the this keyword or the class name. The following example shows the traceProtean() method attempting to access the private and public variables of the Protean class. myProtean.traceProtean = function () { trace(myProtean.privateGreeting); // undefined trace(myProtean.publicGreeting); // hello }; myProtean.traceProtean(); Data type descriptions The primitive data types include Boolean, int, Null, Number, String, uint, and void. The ActionScript core classes also define the following complex data types: Object, Array, Date, Error, Function, RegExp, XML, and XMLList. Boolean data type The Boolean data type comprises two values: true and false. No other values are valid for variables of Boolean type. The default value of a Boolean variable that has been declared but not initialized is false. int data type The int data type is stored internally as a 32-bit integer and comprises the set of integers from -2,147,483,648 (-231) to 2,147,483,647 (231 - 1), inclusive. Previous versions of ActionScript offered only the Number data type, which was used for both integers and floating-point numbers. In ActionScript 3.0, you now have access to low-level machine types for 32-bit signed and unsigned integers. If your variable will not use floating-point numbers, using the int data type instead of the Number data type should be faster and more efficient. For integer values outside the range of the minimum and maximum int values, use the Number data type, which can handle values between positive and negative 9,007,199,254,740,992 (53-bit integer values). The default value for variables that are of the data type int is 0.
  • 95. Data types 95 Null data type The Null data type contains only one value, null. This is the default value for the String data type and all classes that define complex data types, including the Object class. None of the other primitive data types, such as Boolean, Number, int and uint, contain the value null. Flash Player will convert the value null to the appropriate default value if you attempt to assign null to variables of type Boolean, Number, int, or uint. You cannot use this data type as a type annotation. Number data type In ActionScript 3.0, the Number data type can represent integers, unsigned integers, and floating-point numbers. However, to maximize performance, you should use the Number data type only for integer values larger than the 32-bit int and uint types can store or for floating-point numbers. To store a floating-point number, include a decimal point in the number. If you omit a decimal point, the number will be stored as an integer. The Number data type uses the 64-bit double-precision format as specified by the IEEE Standard for Binary Floating-Point Arithmetic (IEEE-754). This standard dictates how floating-point numbers are stored using the 64 available bits. One bit is used to designate whether the number is positive or negative. Eleven bits are used for the exponent, which is stored as base 2. The remaining 52 bits are used to store the significand (also called the mantissa), which is the number that is raised to the power indicated by the exponent. By using some of its bits to store an exponent, the Number data type can store floating-point numbers significantly larger than if it used all of its bits for the significand. For example, if the Number data type used all 64 bits to store the significand, it could store a number as large as 265 - 1. By using 11 bits to store an exponent, the Number data type can raise its significand to a power of 21023. The maximum and minimum values that the Number type can represent are stored in static properties of the Number class called Number.MAX_VALUE and Number.MIN_VALUE. Number.MAX_VALUE == 1.79769313486231e+308 Number.MIN_VALUE == 4.940656458412467e-324 Although this range of numbers is enormous, the cost of this range is precision. The Number data type uses 52 bits to store the significand, with the result that numbers that require more than 52 bits to represent precisely, such as the fraction 1/3, are only approximations. If your application requires absolute precision with decimal numbers, you need to use software that implements decimal floating-point arithmetic as opposed to binary floating-point arithmetic.
  • 96. 96 ActionScript language and syntax When you store integer values with the Number data type, only the 52 bits of the significand are used. The Number data type uses these 52 bits and a special hidden bit to represent integers from -9,007,199,254,740,992 (-253) to 9,007,199,254,740,992 (253). Flash Player uses the NaN value not only as the default value for variables of type Number, but also as the result of any operation that should return a number but does not. For example, if you attempt to calculate the square root of a negative number, the result will be NaN. Other special Number values include positive infinity and negative infinity. String data type The String data type represents a sequence of 16-bit characters. Strings are stored internally as Unicode characters, using the UTF-16 format. Strings are immutable values, just as they are in the Java programming language. An operation on a String value returns a new instance of the string. The default value for a variable declared with the String data type is null. The value null is not the same as the empty string (""), even though they both represent the absence of any characters. uint data type The uint data type is stored internally as a 32-bit unsigned integer and comprises the set of integers from 0 to 4,294,967,295 (232 - 1), inclusive. Use the uint data type for special circumstances that call for non-negative integers. For example, you must use the uint data type to represent pixel color values, because the int data type has an internal sign bit that is not appropriate for handling color values. For integer values larger than the maximum uint value, use the Number data type, which can handle 53-bit integer values. The default value for variables that are of data type uint is 0. void data type The void data type contains only one value, undefined. In previous versions of ActionScript, undefined was the default value for instances of the Object class. In ActionScript 3.0, the default value for Object instances is null. If you attempt to assign the value undefined to an instance of the Object class, Flash Player will convert the value to null. You can only assign a value of undefined to variables that are untyped. Untyped variables are variables that either lack any type annotation, or use the asterisk (*) symbol for type annotation. You can use void only as a return type annotation. NOTE The result of division by 0 is only NaN if the divisor is also 0. Division by 0 produces infinity when the dividend is positive or -infinity when the dividend is negative.
  • 97. Data types 97 Object data type The Object data type is defined by the Object class. The Object class serves as the base class for all class definitions in ActionScript. The ActionScript 3.0 version of the Object data type differs from that of previous versions in three ways. First, the Object data type is no longer the default data type assigned to variables with no type annotation. Second, the Object data type no longer includes the value undefined, which used to be the default value of Object instances. Third, in ActionScript 3.0, the default value for instances of the Object class is null. In previous versions of ActionScript, a variable with no type annotation was automatically assigned the Object data type. This is no longer true in ActionScript 3.0, which now includes the idea of a truly untyped variable. Variables with no type annotation are now considered untyped. If you prefer to make it clear to readers of your code that your intention is to leave a variable untyped, you can use the new asterisk (*) symbol for the type annotation, which is equivalent to omitting a type annotation. The following example shows two equivalent statements, both of which declare an untyped variable x: var x var x:* Only untyped variables can hold the value undefined. If you attempt to assign the value undefined to a variable that has a data type, Flash Player will convert the value undefined to the default value of that data type. For instances of the Object data type, the default value is null, which means that Flash Player will convert the value undefined to null if you attempt to assign undefined to an Object instance. Type conversions A type conversion is said to occur when a value is transformed into a value of a different data type. Type conversions can be either implicit or explicit. Implicit conversion, which is also called coercion, is sometimes performed by Flash Player at run time. For example, if the value 2 is assigned to a variable of the Boolean data type, Flash Player converts the value 2 to the Boolean value true before assigning the value to the variable. Explicit conversion, which is also called casting, occurs when your code instructs the compiler to treat a variable of one data type as if it belongs to a different data type. When primitive values are involved, casting actually converts values from one data type to another. To cast an object to a different type, you wrap the object name in parentheses and precede it with the name of the new type. For example, the following code takes a Boolean value and casts it to an integer: var myBoolean:Boolean = true; var myINT:int = int(myBoolean); trace(myINT); // 1
  • 98. 98 ActionScript language and syntax Implicit conversions Implicit conversions happen at run time in a number of contexts: ■ In assignment statements ■ When values are passed as function arguments ■ When values are returned from functions ■ In expressions using certain operators, such as the addition (+) operator For user-defined types, implicit conversions succeed when the value to be converted is an instance of the destination class or a class that derives from the destination class. If an implicit conversion is unsuccessful, an error occurs. For example, the following code contains a successful implicit conversion and an unsuccessful implicit conversion: class A {} class B extends A {} var objA:A = new A(); var objB:B = new B(); var arr:Array = new Array(); objA = objB; // Conversion succeeds. objB = arr; // Conversion fails. For primitive types, implicit conversions are handled by calling the same internal conversion algorithms that are called by the explicit conversion functions. The following sections discuss these primitive type conversions in detail. Explicit conversions It’s helpful to use explicit conversions, or casting, when you compile in strict mode, because there may be times when you do not want a type mismatch to generate a compile-time error. This may be the case when you know that coercion will convert your values correctly at run time. For example, when working with data received from a form, you may want to rely on coercion to convert certain string values to numeric values. The following code generates a compile-time error even though the code would run correctly in standard mode: var quantityField:String = "3"; var quantity:int = quantityField; // compile time error in strict mode If you want to continue using strict mode, but would like the string converted to an integer, you can use explicit conversion, as follows: var quantityField:String = "3"; var quantity:int = int(quantityField); // Explicit conversion succeeds.
  • 99. Data types 99 Casting to int, uint, and Number You can cast any data type into one of the three number types: int, uint, and Number. If Flash Player is unable to convert the number for some reason, the default value of 0 is assigned for the int and uint data types, and the default value of NaN is assigned for the Number data type. If you convert a Boolean value to a number, true becomes the value 1 and false becomes the value 0. var myBoolean:Boolean = true; var myUINT:uint = uint(myBoolean); var myINT:int = int(myBoolean); var myNum:Number = Number(myBoolean); trace(myUINT, myINT, myNum); // 1 1 1 myBoolean = false; myUINT = uint(myBoolean); myINT = int(myBoolean); myNum = Number(myBoolean); trace(myUINT, myINT, myNum); // 0 0 0 String values that contain only digits can be successfully converted into one of the number types. The number types can also convert strings that look like negative numbers or strings that represent a hexadecimal value (for example, 0x1A). The conversion process ignores leading and trailing white space characters in the string value. You can also cast strings that look like floating-point numbers using Number(). The inclusion of a decimal point causes uint() and int() to return an integer, truncating the decimal and the characters following it. For example, the following string values can be cast into numbers: trace(uint("5")); // 5 trace(uint("-5")); // 4294967291. It wraps around from MAX_VALUE trace(uint(" 27 ")); // 27 trace(uint("3.7")); // 3 trace(int("3.7")); // 3 trace(int("0x1A")); // 26 trace(Number("3.7")); // 3.7 String values that contain non-numeric characters return 0 when cast with int() or uint() and NaN when cast with Number(). The conversion process ignores leading and trailing white space, but returns 0 or NaN if a string has white space separating two numbers. trace(uint("5a")); // 0 trace(uint("ten")); // 0 trace(uint("17 63")); // 0
  • 100. 100 ActionScript language and syntax In ActionScript 3.0, the Number() function no longer supports octal, or base 8, numbers. If you supply a string with a leading zero to the ActionScript 2.0 Number() function, the number is interpreted as an octal number, and converted to its decimal equivalent. This is not true with the Number() function in ActionScript 3.0, which instead ignores the leading zero. For example, the following code generates different output when compiled using different versions of ActionScript: trace(Number("044")); // ActionScript 3.0 44 // ActionScript 2.0 36 Casting is not necessary when a value of one numeric type is assigned to a variable of a different numeric type. Even in strict mode, the numeric types are implicitly converted to the other numeric types. This means that in some cases, unexpected values may result when the range of a type is exceeded. The following examples all compile in strict mode, though some will generate unexpected values: var myUInt:uint = -3; // Assign int/Number value to uint variable trace(myUInt); // 4294967293 var myNum:Number = sampleUINT; // Assign int/uint value to Number variable trace(myNum) // 4294967293 var myInt:int = uint.MAX_VALUE + 1; // Assign Number value to uint variable trace(myInt); // 0 myInt = int.MAX_VALUE + 1; // Assign uint/Number value to int variable trace(myInt); // -2147483648 The following table summarizes the results of casting to the Number, int, or uint data type from other data types. Data type or value Result of conversion to Number, int or uint Boolean If the value is true, 1; otherwise, 0. Date The internal representation of the Date object, which is the number of milliseconds since midnight January 1, 1970, universal time. null 0 Object If the instance is null and converted to Number, NaN; otherwise, 0. String A number if Flash Player can convert the string to a number; otherwise, NaN if converted to Number, or 0 if converted to int or uint. undefined If converted to Number, NaN; if converted to int or uint, 0.
  • 101. Data types 101 Casting to Boolean Casting to Boolean from any of the numeric data types (uint, int, and Number) results in false if the numeric value is 0, and true otherwise. For the Number data type, the value NaN also results in false. The following example shows the results of casting the numbers -1, 0, and 1: var myNum:Number; for (myNum = -1; myNum<2; myNum++) { trace("Boolean(" + myNum +") is " + Boolean(myNum)); } The output from the example shows that, of the three numbers, only 0 returns a value of false: Boolean(-1) is true Boolean(0) is false Boolean(1) is true Casting to Boolean from a String value returns false if the string is either null or an empty string (""). Otherwise, it returns true. var str1:String; // Uninitialized string is null. trace(Boolean(str1)); // false var str2:String = ""; // empty string trace(Boolean(str2)); // false var str3:String = " "; // white space only trace(Boolean(str3)); // true Casting to Boolean from an instance of the Object class returns false if the instance is null; otherwise, it returns true: var myObj:Object; // Uninitialized object is null. trace(Boolean(myObj)); // false myObj = new Object(); // instantiate trace(Boolean(myObj)); // true
  • 102. 102 ActionScript language and syntax Boolean variables get special treatment in strict mode in that you can assign values of any data type to a Boolean variable without casting. Implicit coercion from all data types to the Boolean data type occurs even in strict mode. In other words, unlike almost all other data types, casting to Boolean is not necessary to avoid strict mode errors. The following examples all compile in strict mode and behave as expected at run time: var myObj:Object = new Object(); // instantiate var bool:Boolean = myObj; trace(bool); // true bool = "random string"; trace(bool); // true bool = new Array(); trace(bool); // true bool = NaN; trace(bool); // false The following table summarizes the results of casting to the Boolean data type from other data types: Casting to String Casting to the String data type from any of the numeric data types returns a string representation of the number. Casting to the String data type from a Boolean value returns the string “true” if the value is true, and returns the string “false” if the value is false. Casting to the String data type from an instance of the Object class returns the string “null” if the instance is null. Otherwise, casting to the String type from the Object class returns the string “[object Object]”. Casting to String from an instance of the Array class returns a string comprising a comma- delimited list of all the array elements. For example, the following cast to the String data type returns one string containing all three elements of the array: var myArray:Array = ["primary", "secondary", "tertiary"]; trace(String(myArray)); // primary,secondary,tertiary Data type or value Result of conversion to Boolean String false if the value is null or the empty string (""); true otherwise. null false Number, int or uint false if the value is NaN or 0; true otherwise. Object false if the instance is null; true otherwise.
  • 103. Syntax 103 Casting to String from an instance of the Date class returns a string representation of the date that the instance contains. For example, the following example returns a string representation of the Date class instance (the output shows result for Pacific Daylight Time): var myDate:Date = new Date(2005,6,1); trace(String(myDate)); // Fri Jul 1 00:00:00 GMT-0700 2005 The following table summarizes the results of casting to the String data type from other data types. Syntax The syntax of a language defines a set of rules that must be followed when writing executable code. Case sensitivity ActionScript 3.0 is a case-sensitive language. Identifiers that differ only in case are considered different identifiers. For example, the following code creates two different variables: var num1:int; var Num1:int; Dot syntax The dot operator (.) provides a way to access the properties and methods of an object. Using dot syntax, you can refer to a class property or method by using an instance name, followed by the dot operator and name of the property or method. For example, consider the following class definition: class DotExample { Data type or value Result of conversion to string Array A string comprising all array elements. Boolean "true" or "false" Date A string representation of the Date object. null "null" Number, int or uint A string representation of the number. Object If the instance is null, "null"; otherwise, "[object Object]”.
  • 104. 104 ActionScript language and syntax public var prop1:String; public function method1():void {} } Using dot syntax, you can access the prop1 property and the method1() method by using the instance name created in the following code: var myDotEx:DotExample = new DotExample(); myDotEx.prop1 = “hello”; myDotEx.method1(); You can use dot syntax when you define packages. You use the dot operator to refer to nested packages. For example, the EventDispatcher class resides in a package named events that is nested within the package named flash. You can refer to the events package using the following expression: flash.events You can also refer to the EventDispatcher class using this expression: flash.events.EventDispatcher Slash syntax Slash syntax is not supported in ActionScript 3.0. Slash syntax was used in earlier versions of ActionScript to indicate the path of a movie clip or variable. Literals A literal is a value that appears directly in your code. The following examples are all literals: 17 "hello" -3 9.4 null undefined true false Literals can also be grouped to form compound literals. Array literals are enclosed in bracket characters ([]) and use the comma to separate array elements.
  • 105. Syntax 105 An array literal can be used to initialize an array. The following examples show two arrays that are initialized using array literals. You can use the new statement and pass the compound literal as a parameter to the Array class constructor, but you can also assign literal values directly when instantiating instances of the following ActionScript core classes: Object, Array, String, Number, int, uint, XML, XMLList and Boolean. // Use new statement. var myStrings:Array = new Array(["alpha", "beta", "gamma"]); var myNums:Array = new Array([1,2,3,5,8]); // Assign literal directly. var myStrings:Array = ["alpha", "beta", "gamma"]; var myNums:Array = [1,2,3,5,8]; Literals can also be used to initialize a generic object. A generic object is an instance of the Object class. Object literals are enclosed in curly braces ({}) and use the comma to separate object properties. Each property is declared with the colon character (:), which separates the name of the property from the value of the property. You can create a generic object using the new statement, and pass the object literal as a parameter to the Object class constructor, or you can assign the object literal directly to the instance you are declaring. The following example creates a new generic object and initializes the object with three properties (propA, propB, and propC), each with values set to 1, 2, and 3, respectively: // Use new statement. var myObject:Object = new Object({propA:1, propB:2, propC:3}); // Assign literal directly. var myObject:Object = {propA:1, propB:2, propC:3}; For more information, see “Basics of strings” on page 204, “Basics of regular expressions” on page 286, and “Initializing XML variables” on page 354. Semicolons You can use the semicolon character (;) to terminate a statement. Alternatively, if you omit the semicolon character, the compiler will assume that each line of code represents a single statement. Because many programmers are accustomed to using the semicolon to denote the end of a statement, your code may be easier to read if you consistently use semicolons to terminate your statements. Using a semicolon to terminate a statement allows you to place more than one statement on a single line, but this may make your code more difficult to read.
  • 106. 106 ActionScript language and syntax Parentheses You can use parentheses (()) in three ways in ActionScript 3.0. First, you can use parentheses to change the order of operations in an expression. Operations that are grouped inside parentheses are always executed first. For example, parentheses are used to alter the order of operations in the following code: trace(2 + 3 * 4); // 14 trace( (2 + 3) * 4); // 20 Second, you can use parentheses with the comma operator (,) to evaluate a series of expressions and return the result of the final expression, as shown in the following example: var a:int = 2; var b:int = 3; trace((a++, b++, a+b)); // 7 Third, you can use parentheses to pass one or more parameters to functions or methods, as shown in the following example, which passes a String value to the trace() function: trace("hello"); // hello Comments ActionScript 3.0 code supports two types of comments: single-line comments and multiline comments. These commenting mechanisms are similar to the commenting mechanisms in C++ and Java. The compiler will ignore text that is marked as a comment. Single-line comments begin with two forward slash characters (//) and continue until the end of the line. For example, the following code contains a single-line comment: var someNumber:Number = 3; // a single line comment Multiline comments begin with a forward slash and asterisk (/*) and end with an asterisk and forward slash (*/). /* This is multiline comment that can span more than one line of code. */
  • 107. Syntax 107 Keywords and reserved words Reserved words are words that you cannot use as identifiers in your code because the words are reserved for use by ActionScript. Reserved words include lexical keywords, which are removed from the program namespace by the compiler. The compiler will report an error if you use a lexical keyword as an identifier. The following table lists ActionScript 3.0 lexical keywords. There is a small set of keywords, called syntactic keywords, that can be used as identifiers, but that have special meaning in certain contexts. The following table lists ActionScript 3.0 syntactic keywords. There are also several identifiers that are sometimes referred to as future reserved words. These identifiers are not reserved by ActionScript 3.0, though some of them may be treated as keywords by software that incorporates ActionScript 3.0. You might be able to use many of these identifiers in your code, but Adobe recommends that you do not use them because they may appear as keywords in a subsequent version of the language. as break case catch class const continue default delete do else extends false finally for function if implements import in instanceof interface internal is native new null package private protected public return super switch this throw to true try typeof use var void while with each get set namespace include dynamic final native override static abstract boolean byte cast char debugger double enum export float goto intrinsic
  • 108. 108 ActionScript language and syntax Constants ActionScript 3.0 supports the const statement, which you can use to create constants. Constants are properties with a fixed value that cannot be altered. You can assign a value to a constant only once, and the assignment must occur in close proximity to the declaration of the constant. For example, if a constant is declared as a member of a class, you can assign a value to that constant only as part of the declaration or inside the class constructor. The following code declares two constants. The first constant, MINIMUM, has a value assigned as part of the declaration statement. The second constant, MAXIMUM, has a value assigned in the constructor. class A { public const MINIMUM:int = 0; public const MAXIMUM:int; public function A() { MAXIMUM = 10; } } var a:A = new A(); trace(a.MINIMUM); // 0 trace(a.MAXIMUM); // 10 An error results if you attempt to assign an initial value to a constant in any other way. For example, if you attempt to set the initial value of MAXIMUM outside the class, a run-time error will occur. class A { public const MINIMUM:int = 0; public const MAXIMUM:int; } var a:A = new A(); a["MAXIMUM"] = 10; // run-time error long prototype short synchronized throws to transient type virtual volatile
  • 109. Operators 109 The Flash Player API defines a wide range of constants for your use. By convention, constants in ActionScript use all capital letters, with words separated by the underscore character (_). For example, the MouseEvent class definition uses this naming convention for its constants, each of which represents an event related to mouse input: package flash.events { public class MouseEvent extends Event { public static const CLICK:String = "click"; public static const DOUBLE_CLICK:String = "doubleClick"; public static const MOUSE_DOWN:String = "mouseDown"; public static const MOUSE_MOVE:String = "mouseMove"; ... } } Operators Operators are special functions that take one or more operands and return a value. An operand is a value—usually a literal, a variable, or an expression—that an operator uses as input. For example, in the following code, the addition (+) and multiplication (*) operators are used with three literal operands (2, 3, and 4) to return a value. This value is then used by the assignment (=) operator to assign the returned value, 14, to the variable sumNumber. var sumNumber:uint = 2 + 3 * 4; // uint = 14 Operators can be unary, binary, or ternary. A unary operator takes one operand. For example, the increment (++) operator is a unary operator, because it takes only one operand. A binary operator takes two operands. For example, the division (/) operator takes two operands. A ternary operator takes three operands. For example, the conditional (?:) operator takes three operands. Some operators are overloaded, which means that they behave differently depending on the type or quantity of operands passed to them. The addition (+) operator is an example of an overloaded operator that behaves differently depending on the data type of the operands. If both operands are numbers, the addition operator returns the sum of the values. If both operands are strings, the addition operator returns the concatenation of the two operands. The following example code shows how the operator behaves differently depending on the operands: trace(5 + 5); // 10 trace("5" + "5"); // 55
  • 110. 110 ActionScript language and syntax Operators can also behave differently based on the number of operands supplied. The subtraction (-) operator is both a unary and binary operator. When supplied with only one operand, the subtraction operator negates the operand and returns the result. When supplied with two operands, the subtraction operator returns the difference between the operands. The following example shows the subtraction operator used first as a unary operator, and then as a binary operator. trace(-3); // -3 trace(7-2); // 5 Operator precedence and associativity Operator precedence and associativity determine the order in which operators are processed. Although it may seem natural to those familiar with arithmetic that the compiler processes the multiplication (*) operator before the addition (+) operator, the compiler needs explicit instructions about which operators to process first. Such instructions are collectively referred to as operator precedence. ActionScript defines a default operator precedence that you can alter using the parentheses (()) operator. For example, the following code alters the default precedence in the previous example to force the compiler to process the addition operator before the multiplication operator: var sumNumber:uint = (2 + 3) * 4; // uint == 20 You may encounter situations in which two or more operators of the same precedence appear in the same expression. In these cases, the compiler uses the rules of associativity to determine which operator to process first. All of the binary operators, except the assignment operators, are left-associative, which means that operators on the left are processed before operators on the right. The assignment operators and the conditional (?:) operator are right-associative, which means that the operators on the right are processed before operators on the left. For example, consider the less-than (<) and greater-than (>) operators, which have the same precedence. If both operators are used in the same expression, the operator on the left is processed first because both operators are left-associative. This means that the following two statements produce the same output: trace(3 > 2 < 1); // false trace((3 > 2) < 1); // false The greater-than operator is processed first, which results in a value of true, because the operand 3 is greater than the operand 2. The value true is then passed to the less-than operator along with the operand 1. The following code represents this intermediate state: trace((true) < 1);
  • 111. Operators 111 The less-than operator converts the value true to the numeric value 1 and compares that numeric value to the second operand 1 to return the value false (the value 1 is not less than 1). trace(1 < 1); // false You can alter the default left associativity with the parentheses operator. You can instruct the compiler to process the less-than operator first by enclosing that operator and its operands in parentheses. The following example uses the parentheses operator to produce a different output using the same numbers as the previous example: trace(3 > (2 < 1)); // true The less-than operator is processed first, which results in a value of false because the operand 2 is not less than the operand 1. The value false is then passed to the greater-than operator along with the operand 3. The following code represents this intermediate state: trace(3 > (false)); The greater-than operator converts the value false to the numeric value 0 and compares that numeric value to the other operand 3 to return true (the value 3 is greater than 0). trace(3 > 0); // true The following table lists the operators for ActionScript 3.0 in order of decreasing precedence. Each row of the table contains operators of the same precedence. Each row of operators has higher precedence than the row appearing below it in the table. Group Operators Primary [] {x:y} () f(x) new x.y x[y] <></> @ :: .. Postfix x++ x-- Unary ++x --x + - ~ ! delete typeof void Multiplicative * / % Additive + - Bitwise shift << >> >>> Relational < > <= >= as in instanceof is Equality == != === !== Bitwise AND & Bitwise XOR ^ Bitwise OR | Logical AND && Logical OR ||
  • 112. 112 ActionScript language and syntax Primary operators The primary operators include those used for creating Array and Object literals, grouping expressions, calling functions, instantiating class instances, and accessing properties. All the primary operators, as listed in the following table, have equal precedence. Operators that are part of the E4X specification are indicated by the (E4X) notation. Postfix operators The postfix operators take one operator and either increment or decrement the value. Although these operators are unary operators, they are classified separately from the rest of the unary operators because of their higher precedence and special behavior. When a postfix operator is used as part of a larger expression, the expression’s value is returned before the postfix operator is processed. For example, the following code shows how the value of the expression xNum++ is returned before the value is incremented: var xNum:Number = 0; trace(xNum++); // 0 trace(xNum); // 1 Conditional ?: Assignment = *= /= %= += -= <<= >>= >>>= &= ^= |= Comma , Operator Operation performed [] Initializes an array {x:y} Initializes an object () Groups expressions f(x) Calls a function new Calls a constructor x.y x[y] Accesses a property <></> Initializes an XMLList object (E4X) @ Accesses an attribute (E4X) :: Qualifies a name (E4X) .. Accesses a descendant XML element (E4X) Group Operators
  • 113. Operators 113 All the postfix operators, as listed in the following table, have equal precedence: Unary operators The unary operators take one operand. The increment (++) and decrement (--) operators in this group are prefix operators, which means that they appear before the operand in an expression. The prefix operators differ from their postfix counterparts in that the increment or decrement operation is completed before the value of the overall expression is returned. For example, the following code shows how the value of the expression ++xNum is returned after the value is incremented: var xNum:Number = 0; trace(++xNum); // 1 trace(xNum); // 1 All the unary operators, as listed in the following table, have equal precedence: Operator Operation performed ++ Increments (postfix) -- Decrements (postfix) Operator Operation performed ++ Increments (prefix) -- Decrements (prefix) + Unary + - Unary - (negation) ! Logical NOT ~ Bitwise NOT delete Deletes a property typeof Returns type information void Returns undefined value
  • 114. 114 ActionScript language and syntax Multiplicative operators The multiplicative operators take two operands and perform multiplication, division, or modulo calculations. All the multiplicative operators, as listed in the following table, have equal precedence: Additive operators The additive operators take two operands and perform addition or subtraction calculations. All the additive operators, as listed in the following table, have equal precedence: Bitwise shift operators The bitwise shift operators take two operands and shift the bits of the first operand to the extent specified by the second operand. All the bitwise shift operators, as listed in the following table, have equal precedence: Operator Operation performed * Multiplication / Division % Modulo Operator Operation performed + Addition - Subtraction Operator Operation performed << Bitwise left shift >> Bitwise right shift >>> Bitwise unsigned right shift
  • 115. Operators 115 Relational operators The relational operators take two operands, compare their values, and return a Boolean value. All the relational operators, as listed in the following table, have equal precedence: Equality operators The equality operators take two operands, compare their values, and return a Boolean value. All the equality operators, as listed in the following table, have equal precedence: Bitwise logical operators The bitwise logical operators take two operands and perform bit-level logical operations. The bitwise logical operators differ in precedence and are listed in the following table in order of decreasing precedence: Operator Operation performed < Less than > Greater than <= Less than or equal to >= Greater than or equal to as Checks data type in Checks for object properties instanceof Checks prototype chain is Checks data type Operator Operation performed == Equality != Inequality === Strict equality !== Strict inequality Operator Operation performed & Bitwise AND ^ Bitwise XOR | Bitwise OR
  • 116. 116 ActionScript language and syntax Logical operators The logical operators take two operands and return a Boolean result. The logical operators differ in precedence and are listed in the following table in order of decreasing precedence: Conditional operator The conditional operator is a ternary operator, which means that it takes three operands. The conditional operator is a shorthand method of applying the if..else conditional statement. Assignment operators The assignment operators take two operands and assign a value to one operand, based on the value of the other operand. All the assignment operators, as listed in the following table, have equal precedence: Operator Operation performed && Logical AND || Logical OR Operator Operation performed ?: Conditional Operator Operation performed = Assignment *= Multiplication assignment /= Division assignment %= Modulo assignment += Addition assignment -= Subtraction assignment <<= Bitwise left shift assignment >>= Bitwise right shift assignment >>>= Bitwise unsigned right shift assignment &= Bitwise AND assignment ^= Bitwise XOR assignment |= Bitwise OR assignment
  • 117. Conditionals 117 Conditionals ActionScript 3.0 provides three basic conditional statements that you can use to control program flow. if..else The if..else conditional statement allows you to test a condition and execute a block of code if that condition exists, or execute an alternative block of code if the condition does not exist. For example, the following code tests whether the value of x exceeds 20, generates a trace() function if it does, or generates a different trace() function if it does not: if (x > 20) { trace("x is > 20"); } else { trace("x is <= 20"); } If you do not want to execute an alternative block of code, you can use the if statement without the else statement. if..else if You can test for more than one condition using the if..else if conditional statement. For example, the following code not only tests whether the value of x exceeds 20, but also tests whether the value of x is negative: if (x > 20) { trace("x is > 20"); } else if (x < 0) { trace("x is negative"); } If an if or else statement is followed by only one statement, the statement does not need to be enclosed in braces. For example, the following code does not use braces: if (x > 0) trace("x is positive”); else if (x < 0)
  • 118. 118 ActionScript language and syntax trace("x is negative"); else trace("x is 0"); However, Adobe recommends that you always use braces, because unexpected behavior can occur if statements are later added to a conditional statement that lacks braces. For example, in the following code the value of positiveNums increases by 1 whether or not the condition evaluates to true: var x:int; var positiveNums:int = 0; if (x > 0) trace("x is positive"); positiveNums++; trace(positiveNums); // 1 switch The switch statement is useful if you have several execution paths that depend on the same condition expression. It provides functionality similar to a long series of if..else if statements, but is somewhat easier to read. Instead of testing a condition for a Boolean value, the switch statement evaluates an expression and uses the result to determine which block of code to execute. Blocks of code begin with a case statement and end with a break statement. For example, the following switch statement prints the day of the week, based on the day number returned by the Date.getDay() method: var someDate:Date = new Date(); var dayNum:uint = someDate.getDay(); switch(dayNum) { case 0: trace("Sunday"); break; case 1: trace("Monday"); break; case 2: trace("Tuesday"); break; case 3: trace("Wednesday"); break; case 4: trace("Thursday"); break;
  • 119. Looping 119 case 5: trace("Friday"); break; case 6: trace("Saturday"); break; default: trace("Out of range"); break; } Looping Looping statements allow you to perform a specific block of code repeatedly using a series of values or variables. Adobe recommends that you always enclose the block of code in braces ({}). Although you can omit the braces if the block of code contains only one statement, this practice is not recommended for the same reason that it is not recommended for conditionals: it increases the likelihood that statements added later will be inadvertently excluded from the block of code. If you later add a statement that you want to include in the block of code, but forget to add the necessary braces, the statement will not be executed as part of the loop. for The for loop allows you to iterate through a variable for a specific range of values. You must supply three expressions in a for statement: a variable that is set to an initial value, a conditional statement that determines when the looping ends, and an expression that changes the value of the variable with each loop. For example, the following code loops five times. The value of the variable i starts at 0 and ends at 4, and the output will be the numbers 0 through 4, each on its own line. var i:int; for (i = 0; i < 5; i++) { trace(i); } for..in The for..in loop iterates through the properties of an object, or the elements of an array. For example, you can use a for..in loop to iterate through the properties of a generic object (object properties are not kept in any particular order, so properties may appear in a seemingly random order):
  • 120. 120 ActionScript language and syntax var myObj:Object = {x:20, y:30}; for (var i:String in myObj) { trace(i + ": " + myObj[i]); } // output: // x: 20 // y: 30 You can also iterate through the elements of an array: var myArray:Array = ["one", "two", "three"]; for (var i:String in myArray) { trace(myArray[i]); } // output: // one // two // three What you cannot do is iterate through the properties of an object if it is an instance of a user- defined class, unless the class is a dynamic class. Even with instances of dynamic classes, you will be able to iterate only through properties that are added dynamically. for each..in The for each..in loop iterates through the items of a collection, which can be tags in an XML or XMLList object, the values held by object properties, or the elements of an array. For example, as the following excerpt shows, you can use a for each..in loop to iterate through the properties of a generic object, but unlike the for..in loop, the iterator variable in a for each..in loop contains the value held by the property instead of the name of the property: var myObj:Object = {x:20, y:30}; for each (var num in myObj) { trace(num); } // output: // 20 // 30 You can iterate through an XML or XMLList object, as the following example shows: var myXML:XML = <users> <fname>Jane</fname> <fname>Susan</fname> <fname>John</fname> </users>;
  • 121. Looping 121 for each (var item in myXML.fname) { trace(item); } /* output Jane Susan John */ You can also iterate through the elements of an array, as this example shows: var myArray:Array = ["one", "two", "three"]; for each (var item in myArray) { trace(item); } // output: // one // two // three You cannot iterate through the properties of an object if the object is an instance of a sealed class. Even for instances of dynamic classes, you cannot iterate through any fixed properties, which are properties defined as part of the class definition. while The while loop is like an if statement that repeats as long as the condition is true. For example, the following code produces the same output as the for loop example: var i:int = 0; while (i < 5) { trace(i); i++; } One disadvantage of using a while loop instead of a for loop is that infinite loops are easier to write with while loops. The for loop example code does not compile if you omit the expression that increments the counter variable, but the while loop example does compile if you omit that step. Without the expression that increments i, the loop becomes an infinite loop.
  • 122. 122 ActionScript language and syntax do..while The do..while loop is a while loop that guarantees that the code block is executed at least once, because the condition is checked after the code block is executed. The following code shows a simple example of a do..while loop that generates output even though the condition is not met: var i:int = 5; do { trace(i); i++; } while (i < 5); // output: 5 Functions Functions are blocks of code that carry out specific tasks and can be reused in your program. There are two types of functions in ActionScript 3.0: methods and function closures. Whether a function is a called a method or a function closure depends on the context in which the function is defined. A function is called a method if you define it as part of a class definition or attach it to an instance of an object. A function is called a function closure if it is defined in any other way. Functions have always been extremely important in ActionScript. In ActionScript 1.0, for example, the class keyword did not exist, so “classes” were defined by constructor functions. Although the class keyword has since been added to the language, a solid understanding of functions is still important if you want to take full advantage of what the language has to offer. This can be a challenge for programmers who expect ActionScript functions to behave similarly to functions in languages such as C++ or Java. Although basic function definition and invocation should not present a challenge to experienced programmers, some of the more advanced features of ActionScript functions require some explanation. Basic function concepts This section discusses basic function definition and invocation techniques.
  • 123. Functions 123 Calling functions You call a function by using its identifier followed by the parentheses operator (()). You use the parentheses operator to enclose any function parameters you want to send to the function. For example, the trace() function, which is a top-level function in the Flash Player API, is used throughout this book: trace(“Use trace to help debug your script”); If you are calling a function with no parameters, you must use an empty pair of parentheses. For example, you can use the Math.random() method, which takes no parameters, to generate a random number: var randomNum:Number = Math.random(); Defining your own functions There are two ways to define a function in ActionScript 3.0: you can use a function statement or a function expression. The technique you choose depends on whether you prefer a more static or dynamic programming style. Define your functions with function statements if you prefer static, or strict mode, programming. Define your functions with function expressions if you have a specific need to do so. Function expressions are more often used in dynamic, or standard mode, programming. Function statements Function statements are the preferred technique for defining functions in strict mode. A function statement begins with the function keyword, followed by: ■ The function name ■ The parameters, in a comma-delimited list enclosed in parentheses ■ The function body—that is, the ActionScript code to be executed when the function is invoked, enclosed in curly braces For example, the following code creates a function that defines a parameter and then invokes the function using the string “hello” as the parameter value: function traceParameter(aParam:String) { trace(aParam); } traceParameter("hello"); // hello
  • 124. 124 ActionScript language and syntax Function expressions The second way to declare a function is to use an assignment statement with a function expression, which is also sometimes called a function literal or an anonymous function. This is a more verbose method that is widely used in earlier versions of ActionScript. An assignment statement with a function expression begins with the var keyword, followed by: ■ The function name ■ The colon operator (:) ■ The Function class to indicate the data type ■ The assignment operator (=) ■ The function keyword ■ The parameters, in a comma-delimited list enclosed in parentheses ■ The function body—that is, the ActionScript code to be executed when the function is invoked, enclosed in curly braces For example, the following code declares the traceParameter function using a function expression: var traceParameter:Function = function (aParam:String) { trace(aParam); }; traceParameter("hello"); // hello Notice that you do not specify a function name, as you do in a function statement. Another important difference between function expressions and function statements is that a function expression is an expression rather than a statement. This means that a function expression cannot stand on its own as a function statement can. A function expression can be used only as a part of a statement, usually an assignment statement. The following example shows a function expression assigned to an array element: var traceArray:Array = new Array(); traceArray[0] = function (aParam:String) { trace(aParam); }; traceArray[0]("hello");
  • 125. Functions 125 Choosing between statements and expressions As a general rule, use a function statement unless specific circumstances call for the use of an expression. Function statements are less verbose, and they provide a more consistent experience between strict mode and standard mode than function expressions. Function statements are easier to read than assignment statements that contain function expressions. Function statements make your code more concise; they are less confusing than function expressions, which require you to use both the var and function keywords. Function statements provide a more consistent experience between the two compiler modes in that you can use dot syntax in both strict and standard mode to invoke a method declared using a function statement. This is not necessarily true for methods declared with a function expression. For example, the following code defines a class named Example with two methods: methodExpression(), which is declared with a function expression, and methodStatement(), which is declared with a function statement. In strict mode, you cannot use dot syntax to invoke the methodExpression() method. class Example { var methodExpression = function() {} function methodStatement() {} } var myEx:Example = new Example(); myEx.methodExpression(); // error in strict mode; okay in standard mode myEx.methodStatement(); // okay in strict and standard modes Function expressions are considered better suited to programming that focuses on run-time, or dynamic, behavior. If you prefer to use strict mode, but also need to call a method declared with a function expression, you can use either of two techniques. First, you can call the method using square brackets ([]) instead of the dot (.) operator. The following method call succeeds in both strict mode and standard mode: myExample["methodLiteral"](); Second, you can declare the entire class as a dynamic class. Although this allows you to call the method using the dot operator, the downside is that you sacrifice some strict mode functionality for all instances of that class. For example, the compiler does not generate an error if you attempt to access an undefined property on an instance of a dynamic class. There are some circumstances in which function expressions are useful. One common use of function expressions is for functions that are used only once and then discarded. Another less common use is for attaching a function to a prototype property. For more information, see “The prototype object” on page 177.
  • 126. 126 ActionScript language and syntax There are two subtle differences between function statements and function expressions that you should take into account when choosing which technique to use. The first difference is that function expressions do not exist independently as objects with regard to memory management and garbage collection. In other words, when you assign a function expression to another object, such as an array element or an object property, you create the only reference to that function expression in your code. If the array or object to which your function expression is attached goes out of scope or is otherwise no longer available, you will no longer have access to the function expression. If the array or object is deleted, the memory that the function expression uses will become eligible for garbage collection, which means that the memory is eligible to be reclaimed and reused for other purposes. The following example shows that for a function expression, once the property to which the expression is assigned is deleted, the function is no longer available. The class Test is dynamic, which means that you can add a property named functionExp that holds a function expression. The functionExp() function can be called with the dot operator, but once the functionExp property is deleted, the function is no longer accessible. dynamic class Test {} var myTest:Test = new Test(); // function expression myTest.functionExp = function () { trace("Function expression") }; myTest.functionExp(); // Function expression delete myTest.functionExp; myTest.functionExp(); // error If, on the other hand, the function is first defined with a function statement, it exists as its own object and continues to exist even after you delete the property to which it is attached. The delete operator only works on properties of objects, so even a call to delete the function stateFunc() itself does not work. dynamic class Test {} var myTest:Test = new Test(); // function statement function stateFunc() { trace("Function statement") } myTest.statement = stateFunc; myTest.statement(); // Function statement delete myTest.statement; delete stateFunc; // no effect stateFunc(); // Function statement myTest.statement(); // error
  • 127. Functions 127 The second difference between function statements and function expressions is that function statements exist throughout the scope in which they are defined, including in statements that appear before the function statement. Function expressions, by contrast, are defined only for subsequent statements. For example, the following code successfully calls the scopeTest() function before it is defined: statementTest(); // statementTest function statementTest():void { trace("statementTest"); } Function expressions are not available before they are defined, so the following code results in a run-time error: expressionTest(); // run-time error var expressionTest:Function = function () { trace("expressionTest"); } Returning values from functions To return a value from your function, use the return statement followed by the expression or literal value that you want to return. For example, the following code returns an expression representing the parameter: function doubleNum(baseNum:int):int { return (baseNum * 2); } Notice that the return statement terminates the function, so that any statements below a return statement will not be executed, as follows: function doubleNum(baseNum:int):int { return (baseNum * 2); trace("after return"); // This trace statement will not be executed. } In strict mode, you must return a value of the appropriate type if you choose to specify a return type. For example, the following code generates an error in strict mode, because it does not return a valid value: function doubleNum(baseNum:int):int { trace("after return"); }
  • 128. 128 ActionScript language and syntax Nested functions You can nest functions, which means that functions can be declared within other functions. A nested function is available only within its parent function unless a reference to the function is passed to external code. For example, the following code declares two nested functions inside the getNameAndVersion() function: function getNameAndVersion():String { function getVersion():String { return "9"; } function getProductName():String { return "Flash Player"; } return (getProductName() + " " + getVersion()); } trace(getNameAndVersion()); // Flash Player 9 When nested functions are passed to external code, they are passed as function closures, which means that the function retains any definitions that are in scope when the function is defined. For more information, see “Function closures” on page 136. Function parameters ActionScript 3.0 provides some functionality for function parameters that may seem novel for programmers new to the language. Although the idea of passing parameters by value or reference should be familiar to most programmers, the arguments object and the ... (rest) parameter may be new to many of you.
  • 129. Functions 129 Passing arguments by value or by reference In many programming languages, it’s important to understand the distinction between passing arguments by value or by reference; the distinction can affect the way code is designed. To be passed by value means that the value of the argument is copied into a local variable for use within the function. To be passed by reference means that only a reference to the argument is passed instead of the actual value. No copy of the actual argument is made. Instead, a reference to the variable passed as an argument is created and assigned to a local variable for use within the function. As a reference to a variable outside the function, the local variable gives you the ability to change the value of the original variable. In ActionScript 3.0, all arguments are passed by reference, because all values are stored as objects. However, objects that belong to the primitive data types, which includes Boolean, Number, int, uint, and String, have special operators that make them behave as if they were passed by value. For example, the following code creates a function named passPrimitives() that defines two parameters named xParam and yParam, both of type int. These parameters are similar to local variables declared inside the body of the passPrimitives() function. When the function is called with the arguments xValue and yValue, the parameters xParam and yParam are initialized with references to the int objects represented by xValue and yValue. Because the arguments are primitives, they behave as if passed by value. Although xParam and yParam initially contain only references to the xValue and yValue objects, any changes to the variables within the function body generate new copies of the values in memory. function passPrimitives(xParam:int, yParam:int):void { xParam++; yParam++; trace(xParam, yParam); } var xValue:int = 10; var yValue:int = 15; trace(xValue, yValue); // 10 15 passPrimitives(xValue, yValue); // 11 16 trace(xValue, yValue); // 10 15 Within the passPrimitives() function, the values of xParam and yParam are incremented, but this does not affect the values of xValue and yValue, as shown in the last trace statement. This would be true even if the parameters were named identically to the variables, xValue and yValue, because the xValue and yValue inside the function would point to new locations in memory that exist separately from the variables of the same name outside the function.
  • 130. 130 ActionScript language and syntax All other objects—that is, objects that do not belong to the primitive data types—are always passed by reference, which gives you ability to change the value of the original variable. For example, the following code creates an object named objVar with two properties, x and y. The object is passed as an argument to the passByRef() function. Because the object is not a primitive type, the object is not only passed by reference, but also stays a reference. This means that changes made to the parameters within the function will affect the object properties outside the function. function passByRef(objParam:Object):void { objParam.x++; objParam.y++; trace(objParam.x, objParam.y); } var objVar:Object = {x:10, y:15}; trace(objVar.x, objVar.y); // 10 15 passByRef(objVar); // 11 16 trace(objVar.x, objVar.y); // 11 16 The objParam parameter references the same object as the global objVar variable. As you can see from the trace statements in the example, changes to the x and y properties of the objParam object are reflected in the objVar object. Default parameter values New in ActionScript 3.0 is the ability to declare default parameter values for a function. If a call to a function with default parameter values omits a parameter with default values, the value specified in the function definition for that parameter is used. All parameters with default values must be placed at the end of the parameter list. The values assigned as default values must be compile-time constants. The existence of a default value for a parameter effectively makes that parameter an optional parameter. A parameter without a default value is considered a required parameter. For example, the following code creates a function with three parameters, two of which have default values. When the function is called with only one parameter, the default values for the parameters are used. function defaultValues(x:int, y:int = 3, z:int = 5):void { trace(x, y, z); } defaultValues(1); // 1 3 5
  • 131. Functions 131 The arguments object When parameters are passed to a function, you can use the arguments object to access information about the parameters passed to your function. Some important aspects of the arguments object include the following: ■ The arguments object is an array that includes all the parameters passed to the function. ■ The arguments.length property reports the number of parameters passed to the function. ■ The arguments.callee property provides a reference to the function itself, which is useful for recursive calls to function expressions. ActionScript 3.0 allows function calls to include more parameters than those defined in the function definition, but will generate a compiler error in strict mode if the number of parameters is less than the number of required parameters. You can use the array aspect of the arguments object to access any parameter passed to the function, whether or not that parameter is defined in the function definition. The following example uses the arguments array along with the arguments.length property to trace all the parameters passed to the traceArgArray() function: function traceArgArray(x:int):void { for (var i:uint = 0; i < arguments.length; i++) { trace(arguments[i]); } } traceArgArray(1, 2, 3); // output: // 1 // 2 // 3 NOTE The arguments object is not available if any parameter is named arguments or if you use the ... (rest) parameter.
  • 132. 132 ActionScript language and syntax The arguments.callee property is often used in anonymous functions to create recursion. You can use it to add flexibility to your code. If the name of a recursive function changes over the course of your development cycle, you need not worry about changing the recursive call in your function body if you use arguments.callee instead of the function name. The arguments.callee property is used in the following function expression to enable recursion: var factorial:Function = function (x:uint) { if(x == 0) { return 1; } else { return (x * arguments.callee(x - 1)); } } trace(factorial(5)); // 120 If you use the ... (rest) parameter in your function declaration, the arguments object will not be available to you. Instead, you must access the parameters using the parameter names that you declared for them. You should also be careful to avoid using the string “arguments” as a parameter name, because it will shadow the arguments object. For example, if the function traceArgArray() is rewritten so that an arguments parameter is added, the references to arguments in the function body refer to the parameter rather than the arguments object. The following code produces no output: function traceArgArray(x:int, arguments:int):void { for (var i:uint = 0; i < arguments.length; i++) { trace(arguments[i]); } } traceArgArray(1, 2, 3); // no output The arguments object in previous versions of ActionScript also contained a property named caller, which is a reference to the function that called the current function. The caller property is not present in ActionScript 3.0, but if you need a reference to the calling function, you can alter the calling function so that it passes an extra parameter that is a reference to itself.
  • 133. Functions 133 The ... (rest) parameter ActionScript 3.0 introduces a new parameter declaration called the ... (rest) parameter. This parameter allows you to specify an array parameter that accepts any number of comma- delimited arguments. The parameter can have any name that is not a reserved word. This parameter declaration must be the last parameter specified. Use of this parameter makes the arguments object unavailable. Although the ... (rest) parameter gives you the same functionality as the arguments array and arguments.length property, it does not provide functionality similar to that provided by arguments.callee. You should ensure that you do not need to use arguments.callee before using the ... (rest) parameter. The following example rewrites the traceArgArray() function using the ... (rest) parameter instead of the arguments object: function traceArgArray(... args):void { for (var i:uint = 0; i < args.length; i++) { trace(args[i]); } } traceArgArray(1, 2, 3); // output: // 1 // 2 // 3 The ... (rest) parameter can also be used with other parameters, as long as it is the last parameter listed. The following example modifies the traceArgArray() function so that its first parameter, x, is of type int, and the second parameter uses the ... (rest) parameter. The output skips the first value, because the first parameter is no longer part of the array created by the ... (rest) parameter. function traceArgArray(x: int, ... args) { for (var i:uint = 0; i < args.length; i++) { trace(args[i]); } } traceArgArray(1, 2, 3); // output: // 2 // 3
  • 134. 134 ActionScript language and syntax Functions as objects Functions in ActionScript 3.0 are objects. When you create a function, you are creating an object that can not only be passed as a parameter to another function, but also have properties and methods attached to it. Functions passed as arguments to another function are passed by reference and not by value. When you pass a function as an argument, you use only the identifier and not the parentheses operator that you use to call the method. For example, the following code passes a function named clickListener() as an argument to the addEventListener() method: addEventListener(MouseEvent.CLICK, clickListener); The Array.sort() method also defines a parameter that accepts a function. For an example of a custom sort function that is used as an argument to the Array.sort() function, see “Sorting an array” on page 229. Although it may seem strange to programmers new to ActionScript, functions can have properties and methods, just as any other object can. In fact, every function has a read-only property named length that stores the number of parameters defined for the function. This is different from the arguments.length property, which reports the number of arguments sent to the function. Recall that in ActionScript, the number of arguments sent to a function can exceed the number of parameters defined for that function. The following example, which compiles only in standard mode because strict mode requires an exact match between the number of arguments passed and the number of parameters defined, shows the difference between the two properties: function traceLength(x:uint, y:uint):void { trace("arguments received: " + arguments.length); trace("arguments expected: " + traceLength.length); } traceLength(3, 5, 7, 11); /* output: arguments received: 4 arguments expected: 2 */
  • 135. Functions 135 You can define your own function properties by defining them outside your function body. Function properties can serve as quasi-static properties that allow you to save the state of a variable related to the function. For example, you may want to track the number of times a particular function is called. Such functionality could be useful if you are writing a game and want to track the number of times a user uses a specific command, although you could also use a static class property for this. The following code creates a function property outside the function declaration and increments the property each time the function is called: someFunction.counter = 0; function someFunction():void { someFunction.counter++; } someFunction(); someFunction(); trace(someFunction.counter); // 2 Function scope A function’s scope determines not only where in a program that function can be called, but also what definitions the function can access. The same scope rules that apply to variable identifiers apply to function identifiers. A function declared in the global scope is available throughout your code. For example, ActionScript 3.0 contains global functions, such as isNaN() and parseInt(), that are available anywhere in your code. A nested function—a function declared within another function—can be used anywhere in the function in which it was declared. The scope chain Any time a function begins execution, a number of objects and properties are created. First, a special object called an activation object is created that stores the parameters and any local variables or functions declared in the function body. You cannot access the activation object directly, because it is an internal mechanism. Second, a scope chain is created that contains an ordered list of objects that Flash Player checks for identifier declarations. Every function that executes has a scope chain that is stored in an internal property. For a nested function, the scope chain starts with its own activation object, followed by its parent function’s activation object. The chain continues in this manner until it reaches the global object. The global object is created when an ActionScript program begins, and contains all global variables and functions.
  • 136. 136 ActionScript language and syntax Function closures A function closure is an object that contains a snapshot of a function and its lexical environment. A function’s lexical environment includes all the variables, properties, methods, and objects in the function’s scope chain, along with their values. Function closures are created any time a function is executed apart from an object or a class. The fact that function closures retain the scope in which they were defined creates interesting results when a function is passed as an argument or a return value into a different scope. For example, the following code creates two functions: foo(), which returns a nested function named rectArea() that calculates the area of a rectangle, and bar(), which calls foo() and stores the returned function closure in a variable named myProduct. Even though the bar() function defines its own local variable x (with a value of 2), when the function closure myProduct() is called, it retains the variable x (with a value of 40) defined in function foo(). The bar() function therefore returns the value 160 instead of 8. function foo():Function { var x:int = 40; function rectArea(y:int):int // function closure defined { return x * y } return rectArea; } function bar():void { var x:int = 2; var y:int = 4; var myProduct:Function = foo(); trace(myProduct(4)); // function closure called } bar(); // 160 Methods behave similarly in that they also retain information about the lexical environment in which they were created. This characteristic is most noticeable when a method is extracted from its instance, which creates a bound method. The main difference between a function closure and a bound method is that the value of the this keyword in a bound method always refers to the instance to which it was originally attached, whereas in a function closure the value of the this keyword can change. For more information, see “Bound methods” on page 154.
  • 137. 137 4 CHAPTER 4 Object-oriented programming in ActionScript This chapter describes the elements of ActionScript that support object-oriented programming (OOP). The chapter does not describe general OOP principles such as object design, abstraction, encapsulation, inheritance, and polymorphism. The chapter focuses on how to apply these principles using ActionScript 3.0. Because of ActionScript’s roots as a scripting language, ActionScript 3.0 OOP support is optional. This affords programmers flexibility in choosing the best approach for projects of varying scope and complexity. For small tasks, you may find that using ActionScript with a procedural programming paradigm is all you need. For larger projects, applying OOP principles can make your code easier to understand, maintain, and extend. Contents Basics of object-oriented programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Example: GeometricShapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
  • 138. 138 Object-oriented programming in ActionScript Basics of object-oriented programming Introduction to object-oriented programming Object-oriented programming (OOP) is a way of organizing the code in a program by grouping it into objects—individual elements that include information (data values) and functionality. Using an object-oriented approach to organizing a program allows you to group particular pieces of information (for example, music information like album title, track title, or artist name) together with common functionality or actions associated with that information (such as “add track to playlist” or “play all songs by this artist”). These items are combined into a single item, an object (for example, an “Album” or “MusicTrack”). Being able to bundle these values and functions together provides several benefits, including only needing to keep track of a single variable rather than multiple ones, organizing related functionality together, and being able to structure programs in ways that more closely match the real world. Common object-oriented programming tasks In practice, object-oriented programming has two parts. One part is the strategies and techniques for designing a program (often called object-oriented design). This is a broad subject and is not discussed in this chapter. The other part of OOP is the actual programming structures that are available in a given programming language to build a program using an object-oriented approach. This chapter covers the following common tasks in OOP: ■ Defining classes ■ Creating properties, methods, and get and set accessors (accessor methods) ■ Controlling access to classes, properties, methods, and accessors ■ Creating static properties and methods ■ Creating enumeration-like structures ■ Defining and using interfaces ■ Working with inheritance, including overriding class elements
  • 139. Basics of object-oriented programming 139 Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Attribute: A characteristic assigned to a class element (such as a property or method) in the class definition. Attributes are commonly used to define whether the property or method will be available for access by code in other parts of the program. For example, private and public are attributes. A private method can be called only by code within the class, while a public method can be called by any code in the program. ■ Class: The definition of the structure and behavior of objects of a certain type (like a template or blueprint for objects of that data type). ■ Class hierarchy: The structure of multiple related classes, specifying which classes inherit functionality from other classes. ■ Constructor: A special method you can define in a class, which is called when an instance of the class is created. A constructor is commonly used to specify default values or otherwise perform setup operations for the object. ■ Data type: The type of information that a particular variable can store. In general, data type means the same thing as class. ■ Dot operator: The period sign (.), which in ActionScript (and many other programming languages) is used to indicate that a name refers to a child element of an object (such as a property or method). For instance, in the expression myObject.myProperty, the dot operator indicates that the term myProperty is referring to some value that is an element of the object named myObject. ■ Enumeration: A set of related constant values, grouped together for convenience as properties of a single class. ■ Inheritance: The OOP mechanism that allows one class definition to include all the functionality of a different class definition (and generally add to that functionality). ■ Instance: An actual object created in a program. ■ Namespace: Essentially a custom attribute, allowing more refined control over which code can access other code.
  • 140. 140 Object-oriented programming in ActionScript Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because the code listings in this chapter deal primarily with defining and manipulating data types, testing the examples will involve creating an instance of the class being defined, manipulating that instance using its properties or methods, and then viewing the values of the that instance’s properties. For viewing those values, you’ll want to write values into a text field instance on the Stage, or use the trace() function to print values to the Output panel. These techniques are described in detail in “Testing in-chapter example code listings” on page 63. Classes A class is an abstract representation of an object. A class stores information about the types of data that an object can hold and the behaviors that an object can exhibit. The usefulness of such an abstraction may not be apparent when you write small scripts that contain only a few objects interacting with one another. As the scope of a program grows, however, and the number of objects that must be managed increases, you may find that classes allow you to better control how objects are created and how they interact with one another. As far back as ActionScript 1.0, ActionScript programmers could use Function objects to create constructs that resembled classes. ActionScript 2.0 added formal support for classes with keywords such as class and extends. ActionScript 3.0 not only continues to support the keywords introduced in ActionScript 2.0, but also adds some new capabilities, such as enhanced access control with the protected and internal attributes, and better control over inheritance with the final and override keywords. If you have ever created classes in programming languages like Java, C++, or C#, you will find that ActionScript provides a familiar experience. ActionScript shares many of the same keywords and attribute names, such as class, extends, and public, all of which are discussed in the following sections. NOTE In this chapter, the term property means any member of an object or class, including variables, constants, and methods. In addition, although the terms class and static are often used interchangeably, in this chapter these terms are distinct. For example, in this chapter the phrase class properties refers to all the members of a class, rather than only the static members.
  • 141. Classes 141 Class definitions ActionScript 3.0 class definitions use syntax that is similar to that used in ActionScript 2.0 class definitions. Proper syntax for a class definition calls for the class keyword followed by the class name. The class body, which is enclosed by curly braces ({}), follows the class name. For example, the following code creates a class named Shape that contains one variable, named visible: public class Shape { var visible:Boolean = true; } One significant syntax change involves class definitions that are inside a package. In ActionScript 2.0, if a class is inside a package, the package name must be included in the class declaration. In ActionScript 3.0, which introduces the package statement, the package name must be included in the package declaration instead of in the class declaration. For example, the following class declarations show how the BitmapData class, which is part of the flash.display package, is defined in ActionScript 2.0 and ActionScript 3.0: // ActionScript 2.0 class flash.display.BitmapData {} // ActionScript 3.0 package flash.display { public class BitmapData {} } Class attributes ActionScript 3.0 allows you to modify class definitions using one of the following four attributes: Attribute Definition dynamic Allow properties to be added to instances at run time. final Must not be extended by another class. internal (default) Visible to references inside the current package. public Visible to references everywhere.
  • 142. 142 Object-oriented programming in ActionScript For each of these attributes, except for internal, you must explicitly include the attribute to get the associated behavior. For example, if you do not include the dynamic attribute when defining a class, you will not be able to add properties to a class instance at run time. You explicitly assign an attribute by placing it at the beginning of the class definition, as the following code demonstrates: dynamic class Shape {} Notice that the list does not include an attribute named abstract. This is because abstract classes are not supported in ActionScript 3.0. Notice also that the list does not include attributes named private and protected. These attributes have meaning only inside a class definition, and cannot be applied to classes themselves. If you do not want a class to be publicly visible outside a package, place the class inside a package and mark the class with the internal attribute. Alternatively, you can omit both the internal and public attributes, and the compiler will automatically add the internal attribute for you. If you do not want a class to be visible outside the source file in which it is defined, place the class at the bottom of your source file, below the closing curly brace of the package definition.
  • 143. Classes 143 Class body The class body, which is enclosed by curly braces, is used to define the variables, constants, and methods of your class. The following example shows the declaration for the Accessibility class in the Adobe Flash Player API: public final class Accessibility { public static function get active():Boolean; public static function updateProperties():void; } You can also define a namespace inside a class body. The following example shows how a namespace can be defined within a class body and used as an attribute of a method in that class: public class SampleClass { public namespace sampleNamespace; sampleNamespace function doSomething():void; } ActionScript 3.0 allows you to include not only definitions in a class body, but also statements. Statements that are inside a class body, but outside a method definition, are executed exactly once—when the class definition is first encountered and the associated class object is created. The following example includes a call to an external function, hello(), and a trace statement that outputs a confirmation message when the class is defined: function hello():String { trace("hola"); } class SampleClass { hello(); trace("class created"); } // output when class is created hola class created In contrast to previous versions of ActionScript, in ActionScript 3.0 it is permissible to define a static property and an instance property with the same name in the same class body. For example, the following code declares a static variable named message and an instance variable of the same name: class StaticTest { static var message:String = "static variable"; var message:String = "instance variable";
  • 144. 144 Object-oriented programming in ActionScript } // In your script var myST:StaticTest = new StaticTest(); trace(StaticTest.message); // output: static variable trace(myST.message); // output: instance variable Class property attributes In discussions of the ActionScript object model, the term property means anything that can be a member of a class, including variables, constants, and methods. This differs from the way the term is used in the ActionScript 3.0 Language and Components Reference, where the term is used more narrowly and includes only class members that are variables or are defined by a getter or setter method. In ActionScript 3.0, there is a set of attributes that can be used with any property of a class. The following table lists this set of attributes. Access control namespace attributes ActionScript 3.0 provides four special attributes that control access to properties defined inside a class: public, private, protected, and internal. The public attribute makes a property visible anywhere in your script. For example, to make a method available to code outside its package, you must declare the method with the public attribute. This is true for any property, whether it is declared using the var, const, or function keywords. The private attribute makes a property visible only to callers within the property’s defining class. This behavior differs from that of the private attribute in ActionScript 2.0, which allowed a subclass to access a private property in a superclass. Another significant change in behavior has to do with run-time access. In ActionScript 2.0, the private keyword prohibited access only at compile time and was easily circumvented at run time. In ActionScript 3.0, this is no longer true. Properties that are marked as private are unavailable at both compile time and run time. Attribute Definition internal (default) Visible to references inside the same package. private Visible to references in the same class. protected Visible to references in the same class and derived classes. public Visible to references everywhere. static Specifies that a property belongs to the class, as opposed to instances of the class. UserDefinedNamespace Custom namespace name defined by user.
  • 145. Classes 145 For example, the following code creates a simple class named PrivateExample with one private variable, and then attempts to access the private variable from outside the class. In ActionScript 2.0, compile-time access was prohibited, but the prohibition was easily circumvented by using the property access operator ([]), which does the property lookup at run time rather than at compile time. class PrivateExample { private var privVar:String = "private variable"; } var myExample:PrivateExample = new PrivateExample(); trace(myExample.privVar); // compile-time error in strict mode trace(myExample["privVar"]); // ActionScript 2.0 allows access, but in ActionScript 3.0, this is a run-time error. In ActionScript 3.0, an attempt to access a private property using the dot operator (myExample.privVar) results in a compile-time error if you are using strict mode. Otherwise, the error is reported at run time, just as it is when you use the property access operator (myExample["privVar"]). The following table summarizes the results of attempting to access a private property that belongs to a sealed (not dynamic) class: In classes declared with the dynamic attribute, attempts to access a private variable will not result in a run-time error. Instead, the variable is simply not visible, so Flash Player returns the value undefined. A compile-time error occurs, however, if you use the dot operator in strict mode. The following example is the same as the previous example, except that the PrivateExample class is declared as a dynamic class: dynamic class PrivateExample { private var privVar:String = "private variable"; } var myExample:PrivateExample = new PrivateExample(); trace(myExample.privVar); // compile-time error in strict mode trace(myExample["privVar"]); // output: undefined Strict mode Standard mode dot operator (.) compile-time error run-time error bracket operator ([]) run-time error run-time error
  • 146. 146 Object-oriented programming in ActionScript Dynamic classes generally return the value undefined instead of generating an error when code external to a class attempts to access a private property. The following table shows that an error is generated only when the dot operator is used to access a private property in strict mode: The protected attribute, which is new for ActionScript 3.0, makes a property visible to callers within its own class or in a subclass. In other words, a protected property is available within its own class or to classes that lie anywhere below it in the inheritance hierarchy. This is true whether the subclass is in the same package or in a different package. For those familiar with ActionScript 2.0, this functionality is similar to the private attribute in ActionScript 2.0. The ActionScript 3.0 protected attribute is also similar to the protected attribute in Java, but differs in that the Java version also permits access to callers within the same package. The protected attribute is useful when you have a variable or method that your subclasses need but that you want to hide from code that is outside the inheritance chain. The internal attribute, which is new for ActionScript 3.0, makes a property visible to callers within its own package. This is the default attribute for code inside a package, and it applies to any property that does not have any of the following attributes: ■ public ■ private ■ protected ■ a user-defined namespace The internal attribute is similar to the default access control in Java, although in Java there is no explicit name for this level of access, and it can be achieved only through the omission of any other access modifier. The internal attribute is available in ActionScript 3.0 to give you the option of explicitly signifying your intent to make a property visible only to callers within its own package. static attribute The static attribute, which can be used with properties declared with the var, const, or function keywords, allows you to attach a property to the class rather than to instances of the class. Code external to the class must call static properties by using the class name instead of an instance name. Strict mode Standard mode dot operator (.) compile-time error undefined bracket operator ([]) undefined undefined
  • 147. Classes 147 Static properties are not inherited by subclasses, but the properties are part of a subclass’s scope chain. This means that within the body of a subclass, a static variable or method can be used without referencing the class in which it was defined. For more information, see “Static properties not inherited” on page 169. User-defined namespace attributes As an alternative to the predefined access control attributes, you can create a custom namespace for use as an attribute. Only one namespace attribute can be used per definition, and you cannot use a namespace attribute in combination with any of the access control attributes (public, private, protected, internal). For more information about using namespaces, see “Namespaces” on page 75. Variables Variables can be declared with either the var or const keywords. Variables declared with the var keyword can have their values changed multiple times throughout the execution of a script. Variables declared with the const keyword are called constants, and can have values assigned to them only once. An attempt to assign a new value to an initialized constant results in an error. For more information, see “Constants” on page 108. Static variables Static variables are declared using a combination of the static keyword and either the var or const statement. Static variables, which are attached to a class rather than an instance of a class, are useful for storing and sharing information that applies to an entire class of objects. For example, a static variable is appropriate if you want to keep a tally of the number of times a class is instantiated or if you want to store the maximum number of class instances that are allowed. The following example creates a totalCount variable to track the number of class instantiations and a MAX_NUM constant to store the maximum number of instantiations. The totalCount and MAX_NUM variables are static, because they contain values that apply to the class as a whole rather than to a particular instance. class StaticVars { public static var totalCount:int = 0; public static const MAX_NUM:uint = 16; }
  • 148. 148 Object-oriented programming in ActionScript Code that is external to the StaticVars class and any of its subclasses can reference the totalCount and MAX_NUM properties only through the class itself. For example, the following code works: trace(StaticVars.totalCount); // output: 0 trace(StaticVars.MAX_NUM); // output: 16 You cannot access static variables through an instance of the class, so the following code returns errors: var myStaticVars:StaticVars = new StaticVars(); trace(myStaticVars.totalCount); // error trace(myStaticVars.MAX_NUM); // error Variables that are declared with both the static and const keywords must be initialized at the same time as you declare the constant, as the StaticVars class does for MAX_NUM. You cannot assign a value to MAX_NUM inside the constructor or an instance method. The following code will generate an error, because it is not a valid way to initialize a static constant: // !! Error to initialize static constant this way class StaticVars2 { public static const UNIQUESORT:uint; function initializeStatic():void { UNIQUESORT = 16; } } Instance variables Instance variables include properties declared with the var and const keywords, but without the static keyword. Instance variables, which are attached to class instances rather than to an entire class, are useful for storing values that are specific to an instance. For example, the Array class has an instance property named length, which stores the number of array elements that a particular instance of the Array class holds. Instance variables, whether declared as var or const, cannot be overridden in a subclass. You can, however, achieve functionality that is similar to overriding variables by overriding getter and setter methods. For more information, see “Get and set accessor methods” on page 153. Methods Methods are functions that are part of a class definition. Once an instance of the class is created, a method is bound to that instance. Unlike a function declared outside a class, a method cannot be used apart from the instance to which it is attached.
  • 149. Classes 149 Methods are defined using the function keyword. You can use a function statement such as the following: public function sampleFunction():String {} Or you can use a variable to which you assign a function expression, as follows: public var sampleFunction:Function = function () {} In most cases you will want to use a function statement instead of a function expression for the following reasons: ■ Function statements are more concise and easier to read. ■ Function statements allow you to use the override and final keywords. For more information, see “Overriding methods” on page 167. ■ Function statements create a stronger bond between the identifier—that is, the name of the function—and the code within the method body. Because the value of a variable can be changed with an assignment statement, the connection between a variable and its function expression can be severed at any time. Although you can work around this issue by declaring the variable with const instead of var, such a technique is not considered a best practice, because it makes the code hard to read and prevents the use of the override and final keywords. One case in which you must use a function expression is when you choose to attach a function to the prototype object. For more information, see “The prototype object” on page 177. Constructor methods Constructor methods, sometimes simply called constructors, are functions that share the same name as the class in which they are defined. Any code that you include in a constructor method is executed whenever an instance of the class is created with the new keyword. For example, the following code defines a simple class named Example that contains a single property named status. The initial value of the status variable is set inside the constructor function. class Example { public var status:String; public function Example() { status = "initialized"; } } var myExample:Example = new Example(); trace(myExample.status); // output: initialized
  • 150. 150 Object-oriented programming in ActionScript Constructor methods can only be public, but the use of the public attribute is optional. You cannot use any of the other access control specifiers, including private, protected, or internal, on a constructor. You also cannot use a user-defined namespace with a constructor method. A constructor can make an explicit call to the constructor of its direct superclass by using the super() statement. If the superclass constructor is not explicitly called, the compiler automatically inserts a call before the first statement in the constructor body. You can also call methods of the superclass by using the super prefix as a reference to the superclass. If you decide to use both super() and super in the same constructor body, be sure to call super() first. Otherwise, the super reference will not behave as expected. The super() constructor should also be called before any throw or return statement. The following example demonstrates what happens if you attempt to use the super reference before calling the super() constructor. A new class, ExampleEx, extends the Example class. The ExampleEx constructor attempts to access the status variable defined in its superclass, but does so before calling super(). The trace() statement inside the ExampleEx constructor produces the value null, because the status variable is not available until the super() constructor executes. class ExampleEx extends Example { public function ExampleEx() { trace(super.status); super(); } } var mySample:ExampleEx = new ExampleEx(); // output: null Although it is legal to use the return statement inside a constructor, it is not permissible to return a value. In other words, return statements must not have associated expressions or values. Accordingly, constructor methods are not allowed to return values, which means that no return type may be specified. If you do not define a constructor method in your class, the compiler will automatically create an empty constructor for you. If your class extends another class, the compiler will include a super() call in the constructor it generates.
  • 151. Classes 151 Static methods Static methods, also called class methods, are methods that are declared with the static keyword. Static methods, which are attached to a class rather than to an instance of a class, are useful for encapsulating functionality that affects something other than the state of an individual instance. Because static methods are attached to a class as a whole, static methods can be accessed only through a class and not through an instance of the class. Static methods are useful for encapsulating functionality that is not limited to affecting the state of class instances. In other words, a method should be static if it provides functionality that does not directly affect the value of a class instance. For example, the Date class has a static method named parse(), which takes a string and converts it to a number. The method is static because it does not affect an individual instance of the class. Instead, the parse() method takes a string that represents a date value, parses the string, and returns a number in a format compatible with the internal representation of a Date object. This method is not an instance method, because it does not make sense to apply the method to an instance of the Date class. Contrast the static parse() method with one of the instance methods of the Date class, such as getMonth(). The getMonth() method is an instance method, because it operates directly on the value of an instance by retrieving a specific component, the month, of a Date instance. Because static methods are not bound to individual instances, you cannot use the keywords this or super within the body of a static method. Both the this reference and the super reference have meaning only within the context of an instance method. In contrast with some other class-based programming languages, static methods in ActionScript 3.0 are not inherited. For more information, see “Static properties not inherited” on page 169. Instance methods Instance methods are methods that are declared without the static keyword. Instance methods, which are attached to instances of a class instead of the class as a whole, are useful for implementing functionality that affects individual instances of a class. For example, the Array class contains an instance method named sort(), which operates directly on Array instances.
  • 152. 152 Object-oriented programming in ActionScript Within the body of an instance method, both static and instance variables are in scope, which means that variables defined in the same class can be referenced using a simple identifier. For example, the following class, CustomArray, extends the Array class. The CustomArray class defines a static variable named arrayCountTotal to track the total number of class instances, an instance variable named arrayNumber that tracks the order in which the instances were created, and an instance method named getPosition() that returns the values of these variables. public class CustomArray extends Array { public static var arrayCountTotal:int = 0; public var arrayNumber:int; public function CustomArray() { arrayNumber = ++arrayCountTotal; } public function getArrayPosition():String { return ("Array " + arrayNumber + " of " + arrayCountTotal); } } Although code external to the class must refer to the arrayCountTotal static variable through the class object using CustomArray.arrayCountTotal, code that resides inside the body of the getPosition() method can refer directly to the static arrayCountTotal variable. This is true even for static variables in superclasses. Though static properties are not inherited in ActionScript 3.0, static properties in superclasses are in scope. For example, the Array class has a few static variables, one of which is a constant named DESCENDING. Code that resides in an Array subclass can refer to the static constant DESCENDING using a simple identifier: public class CustomArray extends Array { public function testStatic():void { trace(DESCENDING); // output: 2 } } The value of the this reference within the body of an instance method is a reference to the instance to which the method is attached. The following code demonstrates that the this reference points to the instance that contains the method: class ThisTest {
  • 153. Classes 153 function thisValue():ThisTest { return this; } } var myTest:ThisTest = new ThisTest(); trace(myTest.thisValue() == myTest); // output: true Inheritance of instance methods can be controlled with the keywords override and final. You can use the override attribute to redefine an inherited method, and the final attribute to prevent subclasses from overriding a method. For more information, see “Overriding methods” on page 167. Get and set accessor methods Get and set accessor functions, also called getters and setters, allow you to adhere to the programming principles of information hiding and encapsulation while providing an easy-to- use programming interface for the classes that you create. Get and set functions allow you to keep your class properties private to the class, but allow users of your class to access those properties as if they were accessing a class variable instead of calling a class method. The advantage of this approach is that it allows you to avoid the traditional accessor functions with unwieldy names, such as getPropertyName() and setPropertyName(). Another advantage of getters and setters is that you can avoid having two public-facing functions for each property that allows both read and write access. The following example class, named GetSet, includes get and set accessor functions named publicAccess() that provide access to the private variable named privateProperty: class GetSet { private var privateProperty:String; public function get publicAccess():String { return privateProperty; } public function set publicAccess(setValue:String):void { privateProperty = setValue; } }
  • 154. 154 Object-oriented programming in ActionScript If you attempt to access the property privateProperty directly, an error will result, as follows: var myGetSet:GetSet = new GetSet(); trace(myGetSet.privateProperty); // error occurs Instead, a user of the GetSet class will use something that appears to be a property named publicAccess, but that is really a pair of get and set accessor functions that operate on the private property named privateProperty. The following example instantiates the GetSet class, and then sets the value of the privateProperty using the public accessor named publicAccess: var myGetSet:GetSet = new GetSet(); trace(myGetSet.publicAccess); // output: null myGetSet.publicAccess = "hello"; trace(myGetSet.publicAccess); // output: hello Getter and setter functions also make it possible to override properties that are inherited from a superclass, something that is not possible when you use regular class member variables. Class member variables that are declared using the var keyword cannot be overridden in a subclass. Properties that are created using getter and setter functions, however, do not have this restriction. You can use the override attribute on getter and setter functions that are inherited from a superclass. Bound methods A bound method, sometimes called a method closure, is simply a method that is extracted from its instance. Examples of bound methods include methods that are passed as arguments to a function or returned as values from a function. New in ActionScript 3.0, a bound method is similar to a function closure in that it retains its lexical environment even when extracted from its instance. The key difference, however, between a bound method and a function closure is that the this reference for a bound method remains linked, or bound, to the instance that implements the method. In other words, the this reference in a bound method always points to the original object that implemented the method. For function closures, the this reference is generic, which means that it points to whatever object the function is associated with at the time it is invoked.
  • 155. Classes 155 Understanding bound methods is important if you use the this keyword. Recall that the this keyword provides a reference to a method’s parent object. Most ActionScript programmers expect that the this keyword always refers to the object or class that contains the definition of a method. Without method binding, however, this would not always be true. In previous versions of ActionScript, for example, the this reference did not always refer to the instance that implemented the method. When methods are extracted from an instance in ActionScript 2.0, not only is the this reference not bound to the original instance, but also the member variables and methods of the instance’s class are not available. This is not a problem in ActionScript 3.0, because bound methods are automatically created when you pass a method as a parameter. Bound methods ensure that the this keyword always references the object or class in which a method is defined. The following code defines a class named ThisTest, which contains a method named foo() that defines the bound method, and a method named bar() that returns the bound method. Code external to the class creates an instance of the ThisTest class, calls the bar() method, and stores the return value in a variable named myFunc. class ThisTest { private var num:Number = 3; function foo():void // bound method defined { trace("foo's this: " + this); trace("num: " + num); } function bar():Function { return foo; // bound method returned } } var myTest:ThisTest = new ThisTest(); var myFunc:Function = myTest.bar(); trace(this); // output: [object global] myFunc(); /* output: foo's this: [object ThisTest] output: num: 3 */ The last two lines of code show that the this reference in the bound method foo() still points to an instance of ThisTest class, even though the this reference in the line just before it points to the global object. Moreover, the bound method stored in the myFunc variable still has access to the member variables of the ThisTest class. If this same code is run in ActionScript 2.0, the this references would match, and the num variable would be undefined.
  • 156. 156 Object-oriented programming in ActionScript One area where the addition of bound methods is most noticeable is with event handlers, because the addEventListener() method requires that you pass a function or method as an argument. For more information, see “Listener function defined as a class method” on page 329. Enumerations with classes Enumerations are custom data types that you create to encapsulate a small set of values. ActionScript 3.0 does not support a specific enumeration facility, unlike C++ with its enum keyword or Java with its Enumeration interface. You can, however, create enumerations using classes and static constants. For example, the PrintJob class in the Flash Player API uses an enumeration named PrintJobOrientation to store the set of values comprising "landscape" and "portrait", as shown in the following code: public final class PrintJobOrientation { public static const LANDSCAPE:String = "landscape"; public static const PORTRAIT:String = "portrait"; } By convention, an enumeration class is declared with the final attribute, because there is no need to extend the class. The class comprises only static members, which means that you do not create instances of the class. Instead, you access the enumeration values directly through the class object, as shown in the following code excerpt: var pj:PrintJob = new PrintJob(); if(pj.start()) { if (pj.orientation == PrintJobOrientation.PORTRAIT) { ... } ... } All of the enumeration classes in the Flash Player API contain only variables of type String, int, or uint. The advantage of using enumerations instead of literal string or number values is that typographical mistakes are easier to find with enumerations. If you mistype the name of an enumeration, the ActionScript compiler generates an error. If you use literal values, the compiler does not complain if you spell a word incorrectly or use the wrong number. In the previous example, the compiler generates an error if the name of the enumeration constant is incorrect, as the following excerpt shows: if (pj.orientation == PrintJobOrientation.PORTRAI) // compiler error
  • 157. Classes 157 However, the compiler does not generate an error if you misspell a string literal value, as follows: if (pj.orientation == "portrai") // no compiler error A second technique for creating enumerations also involves creating a separate class with static properties for the enumeration. This technique differs, however, in that each of the static properties contains an instance of the class instead of a string or integer value. For example, the following code creates an enumeration class for the days of the week: public final class Day { public static const MONDAY:Day = new Day(); public static const TUESDAY:Day = new Day(); public static const WEDNESDAY:Day = new Day(); public static const THURSDAY:Day = new Day(); public static const FRIDAY:Day = new Day(); public static const SATURDAY:Day = new Day(); public static const SUNDAY:Day = new Day(); } This technique is not used by the Flash Player API but is used by many developers who prefer the improved type checking that the technique provides. For example, a method that returns an enumeration value can restrict the return value to the enumeration data type. The following code shows not only a function that returns a day of the week, but also a function call that uses the enumeration type as a type annotation: function getDay():Day { var date:Date = new Date(); var retDay:Day; switch (date.day) { case 0: retDay = Day.MONDAY; break; case 1: retDay = Day.TUESDAY; break; case 2: retDay = Day.WEDNESDAY; break; case 3: retDay = Day.THURSDAY; break; case 4: retDay = Day.FRIDAY; break; case 5: retDay = Day.SATURDAY;
  • 158. 158 Object-oriented programming in ActionScript break; case 6: retDay = Day.SUNDAY; break; } return retDay; } var dayOfWeek:Day = getDay(); You can also enhance the Day class so that it associates an integer with each day of the week, and provides a toString() method that returns a string representation of the day. You might want to enhance the Day class in this manner as an exercise. Embedded asset classes ActionScript 3.0 uses special classes, called embedded asset classes, to represent embedded assets. An embedded asset is an asset, such as a sound, image, or font, that is included in a SWF file at compile time. Embedding an asset instead of loading it dynamically ensures that it will be available at run time, but at the cost of increased SWF file size. Using embedded asset classes in Flash To embed an asset, first place the asset into a FLA file’s library. Next, use the asset’s linkage property to provide a name for the asset’s embedded asset class. If a class by that name cannot be found in the classpath, a class is automatically generated for you. You can then create an instance of the embedded asset class and use any properties and methods defined or inherited by that class. For example, the following code can be used to play an embedded sound that is linked to an embedded asset class named PianoMusic: var piano:PianoMusic = new PianoMusic(); var sndChannel:SoundChannel = piano.play(); Interfaces An interface is a collection of method declarations that allows unrelated objects to communicate with one another. For example, the Flash Player API defines the IEventDispatcher interface, which contains method declarations that a class can use to handle event objects. The IEventDispatcher interface establishes a standard way for objects to pass event objects to one another. The following code shows the definition of the IEventDispatcher interface:
  • 159. Interfaces 159 public interface IEventDispatcher { function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean = false):void; function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void; function dispatchEvent(event:Event):Boolean; function hasEventListener(type:String):Boolean; function willTrigger(type:String):Boolean; } Interfaces are based on the distinction between a method’s interface and its implementation. A method’s interface includes all the information necessary to invoke that method, including the name of the method, all of its parameters, and its return type. A method’s implementation includes not only the interface information, but also the executable statements that carry out the method’s behavior. An interface definition contains only method interfaces, and any class that implements the interface is responsible for defining the method implementations. In the Flash Player API, the EventDispatcher class implements the IEventDispatcher interface by defining all of the IEventDispatcher interface methods and adding method bodies to each of the methods. The following code is an excerpt from the EventDispatcher class definition: public class EventDispatcher implements IEventDispatcher { function dispatchEvent(event:Event):Boolean { /* implementation statements */ } ... } The IEventDispatcher interface serves as a protocol that EventDispatcher instances use to process event objects and pass them to other objects that have also implemented the IEventDispatcher interface. Another way to describe an interface is to say that it defines a data type just as a class does. Accordingly, an interface can be used as a type annotation, just as a class can. As a data type, an interface can also be used with operators, such as the is and as operators, that require a data type. Unlike a class, however, an interface cannot be instantiated. This distinction has led many programmers to think of interfaces as abstract data types and classes as concrete data types.
  • 160. 160 Object-oriented programming in ActionScript Defining an interface The structure of an interface definition is similar to that of a class definition, except that an interface can contain only methods with no method bodies. Interfaces cannot include variables or constants but can include getters and setters. To define an interface, use the interface keyword. For example, the following interface, IExternalizable, is part of the flash.utils package in the Flash Player API. The IExternalizable interface defines a protocol for serializing an object, which means converting an object into a format suitable for storage on a device or for transport across a network. public interface IExternalizable { function writeExternal(output:IDataOutput):void; function readExternal(input:IDataInput):void; } Note that the IExternalizable interface is declared with the public access control modifier. Interface definitions may only be modified by the public and internal access control specifiers. The method declarations inside an interface definition cannot have any access control specifiers. The Flash Player API follows a convention in which interface names begin with an uppercase I, but you can use any legal identifier as an interface name. Interface definitions are often placed at the top level of a package. Interface definitions cannot be placed inside a class definition or inside another interface definition. Interfaces can extend one or more other interfaces. For example, the following interface, IExample, extends the IExternalizable interface: public interface IExample extends IExternalizable { function extra():void; } Any class that implements the IExample interface must include implementations not only for the extra() method, but also for the writeExternal() and readExternal() methods inherited from the IExternalizable interface.
  • 161. Interfaces 161 Implementing an interface in a class A class is the only ActionScript 3.0 language element that can implement an interface. Use the implements keyword in a class declaration to implement one or more interfaces. The following example defines two interfaces, IAlpha and IBeta, and a class, Alpha, that implements them both: interface IAlpha { function foo(str:String):String; } interface IBeta { function bar():void; } class Alpha implements IAlpha, IBeta { public function foo(param:String):String {} public function bar():void {} } In a class that implements an interface, implemented methods must do the following: ■ Use the public access control identifier. ■ Use the same name as the interface method. ■ Have the same number of parameters, each with data types that match the interface method parameter data types. ■ Use the same return type. You do have some flexibility, however, in how you name the parameters of methods that you implement. Although the number of parameters and the data type of each parameter in the implemented method must match that of the interface method, the parameter names do not need to match. For example, in the previous example the parameter of the Alpha.foo() method is named param: public function foo(param:String):String {} But the parameter is named str in the IAlpha.foo() interface method: function foo(str:String):String;
  • 162. 162 Object-oriented programming in ActionScript You also have some flexibility with default parameter values. An interface definition can include function declarations with default parameter values. A method that implements such a function declaration must have a default parameter value that is a member of the same data type as the value specified in the interface definition, but the actual value does not have to match. For example, the following code defines an interface that contains a method with a default parameter value of 3: interface IGamma { function doSomething(param:int = 3):void; } The following class definition implements the Igamma interface but uses a different default parameter value: class Gamma implements IGamma { public function doSomething(param:int = 4):void {} } The reason for this flexibility is that the rules for implementing an interface are designed specifically to ensure data type compatibility, and requiring identical parameter names and default parameter values is not necessary to achieve that objective. Inheritance Inheritance is a form of code reuse that allows programmers to develop new classes that are based on existing classes. The existing classes are often referred to as base classes or superclasses, while the new classes are usually called subclasses. A key advantage of inheritance is that it allows you to reuse code from a base class yet leave the existing code unmodified. Moreover, inheritance requires no changes to the way that other classes interact with the base class. Rather than modifying an existing class that may have been thoroughly tested or may already be in use, using inheritance you can treat that class as an integrated module that you can extend with additional properties or methods. Accordingly, you use the extends keyword to indicate that a class inherits from another class.
  • 163. Inheritance 163 Inheritance also allows you to take advantage of polymorphism in your code. Polymorphism is the ability to use a single method name for a method that behaves differently when applied to different data types. A simple example is a base class named Shape with two subclasses named Circle and Square. The Shape class defines a method named area(), which returns the area of the shape. If polymorphism is implemented, you can call the area() method on objects of type Circle and Square and have the correct calculations done for you. Inheritance enables polymorphism by allowing subclasses to inherit and redefine, or override, methods from the base class. In the following example, the area() method is redefined by the Circle and Square classes: class Shape { public function area():Number { return NaN; } } class Circle extends Shape { private var radius:Number = 1; override public function area():Number { return (Math.PI * (radius * radius)); } } class Square extends Shape { private var side:Number = 1; override public function area():Number { return (side * side); } } var cir:Circle = new Circle(); trace(cir.area()); // output: 3.141592653589793 var sq:Square = new Square(); trace(sq.area()); // output: 1 Because each class defines a data type, the use of inheritance creates a special relationship between a base class and a class that extends it. A subclass is guaranteed to possess all the properties of its base class, which means that an instance of a subclass can always be substituted for an instance of the base class. For example, if a method defines a parameter of type Shape, it is legal to pass an argument of type Circle because Circle extends Shape, as in the following:
  • 164. 164 Object-oriented programming in ActionScript function draw(shapeToDraw:Shape) {} var myCircle:Circle = new Circle(); draw(myCircle); Instance properties and inheritance An instance property, whether defined with the function, var, or const keywords, is inherited by all subclasses as long as the property is not declared with the private attribute in the base class. For example, the Event class in the Flash Player API has a number of subclasses that inherit properties common to all event objects. For some types of events, the Event class contains all the properties necessary to define the event. These types of events do not require instance properties beyond those defined in the Event class. Examples of such events are the complete event, which occurs when data has loaded successfully, and the connect event, which occurs when a network connection has been established. The following example is an excerpt from the Event class that shows some of the properties and methods that are inherited by subclasses. Because the properties are inherited, an instance of any subclass can access these properties. public class Event { public function get type():String; public function get bubbles():Boolean; ... public function stopPropagation():void {} public function stopImmediatePropagation():void {} public function preventDefault():void {} public function isDefaultPrevented():Boolean {} ... } Other types of events require unique properties not available in the Event class. These events are defined using subclasses of the Event class so that new properties can be added to the properties defined in the Event class. An example of such a subclass is the MouseEvent class, which adds properties unique to events associated with mouse movement or mouse clicks, such as the mouseMove and click events. The following example is an excerpt from the MouseEvent class that shows the definition of properties that exist on the subclass but not on the base class: public class MouseEvent extends Event { public static const CLICK:String = "click"; public static const MOUSE_MOVE:String = "mouseMove";
  • 165. Inheritance 165 ... public function get stageX():Number {} public function get stageY():Number {} ... } Access control specifiers and inheritance If a property is declared with the public keyword, the property is visible to code anywhere. This means that the public keyword, unlike the private, protected, and internal keywords, places no restrictions on property inheritance. If a property is declared with private keyword, it is visible only in the class that defines it, which means that it is not inherited by any subclasses. This behavior is different from previous versions of ActionScript, where the private keyword behaved more like the ActionScript 3.0 protected keyword. The protected keyword indicates that a property is visible not only within the class that defines it, but also to all subclasses. Unlike the protected keyword in the Java programming language, the protected keyword in ActionScript 3.0 does not make a property visible to all other classes in the same package. In ActionScript 3.0, only subclasses can access a property declared with the protected keyword. Moreover, a protected property is visible to a subclass whether the subclass is in the same package as the base class or in a different package. To limit the visibility of a property to the package in which it is defined, use the internal keyword or do not use any access control specifier. The internal access control specifier is the default access control specifier that applies when one is not specified. A property marked as internal will be inherited only by a subclass that resides in the same package. You can use the following example to see how each of the access control specifiers affects inheritance across package boundaries. The following code defines a main application class named AccessControl and two other classes named Base and Extender. The Base class is in a package named foo and the Extender class, which is a subclass of the Base class, is in a package named bar. The AccessControl class imports only the Extender class and creates an instance of Extender that attempts to access a variable named str that is defined in the Base class. The str variable is declared as public so that the code compiles and runs as shown in the following excerpt: // Base.as in a folder named foo package foo { public class Base { public var str:String = "hello"; // change public on this line }
  • 166. 166 Object-oriented programming in ActionScript } // Extender.as in a folder named bar package bar { import foo.Base; public class Extender extends Base { public function getString():String { return str; } } } // main application class in file named ProtectedExample.as import flash.display.MovieClip; import bar.Extender; public class AccessControl extends MovieClip { public function AccessControl() { var myExt:Extender = new Extender(); trace(myExt.testString); // error if str is not public trace(myExt.getString()); // error if str is private or internal } } } To see how the other access control specifiers affect compilation and execution of the preceding example, change the str variable’s access control specifier to private, protected, or internal after deleting or commenting out the following line from the AccessControl class: trace(myExt.testString); // error if str is not public Overriding variables not permitted Properties that are declared with the var or const keywords are inherited but cannot be overridden. To override a property means to redefine the property in a subclass. The only type of property that can be overridden are methods—that is, properties declared with the function keyword. Although you cannot override an instance variable, you can achieve similar functionality by creating getter and setter methods for the instance variable and overriding the methods. For more information, see “Overriding getters and setters” on page 168.
  • 167. Inheritance 167 Overriding methods To override a method means to redefine the behavior of an inherited method. Static methods are not inherited and cannot be overridden. Instance methods, however, are inherited by subclasses and can be overridden as long as the following two criteria are met: ■ The instance method is not declared with the final keyword in the base class. When used with an instance method, the final keyword indicates the programmer’s intent to prevent subclasses from overriding the method. ■ The instance method is not declared with the private access control specifier in the base class. If a method is marked as private in the base class, there is no need to use the override keyword when defining an identically named method in the subclass, because the base class method will not be visible to the subclass. To override an instance method that meets these criteria, the method definition in the subclass must use the override keyword and must match the superclass version of the method in the following ways: ■ The override method must have the same level of access control as the base class method. Methods marked as internal have the same level of access control as methods that have no access control specifier. ■ The override method must have the same number of parameters as the base class method. ■ The override method parameters must have the same data type annotations as the parameters in the base class method. ■ The override method must have the same return type as the base class method. The names of the parameters in the override method, however, do not have to match the names of the parameters in the base class, as long as the number of parameters and the data type of each parameter matches. The super statement When overriding a method, programmers often want to add to the behavior of the superclass method they are overriding instead of completely replacing the behavior. This requires a mechanism that allows a method in a subclass to call the superclass version of itself. The super statement provides such a mechanism, in that it contains a reference to the immediate superclass. The following example defines a class named Base that contains a method named thanks() and a subclass of the Base class named Extender that overrides the thanks() method. The Extender.thanks() method uses the super statement to call Base.thanks(). package { import flash.display.MovieClip; public class SuperExample extends MovieClip {
  • 168. 168 Object-oriented programming in ActionScript public function SuperExample() { var myExt:Extender = new Extender() trace(myExt.thanks()); // output: Mahalo nui loa } } } class Base { public function thanks():String { return "Mahalo"; } } class Extender extends Base { override public function thanks():String { return super.thanks() + " nui loa"; } } Overriding getters and setters Although you cannot override variables defined in a superclass, you can override getters and setters. For example, the following code overrides a getter named currentLabel that is defined in the MovieClip class in the Flash Player API.: package { import flash.display.MovieClip; public class OverrideExample extends MovieClip { public function OverrideExample() { trace(currentLabel) } override public function get currentLabel():String { var str:String = "Override: "; str += super.currentLabel; return str; } } }
  • 169. Inheritance 169 The output of the trace() statement in the OverrideExample class constructor is Override: null, which shows that the example was able to override the inherited currentLabel property. Static properties not inherited Static properties are not inherited by subclasses. This means that static properties cannot be accessed through an instance of a subclass. A static property can be accessed only through the class object on which it is defined. For example, the following code defines a base class named Base and a subclass that extends Base named Extender. A static variable named test is defined in the Base class. The code as written in the following excerpt does not compile in strict mode and generates a run-time error in standard mode. package { import flash.display.MovieClip; public class StaticExample extends MovieClip { public function StaticExample() { var myExt:Extender = new Extender(); trace(myExt.test); // error } } } class Base { public static var test:String = "static"; } class Extender extends Base { } The only way to access the static variable test is through the class object, as shown in the following code: Base.test; It is permissible, however, to define an instance property using the same name as a static property. Such an instance property can be defined in the same class as the static property or in a subclass. For example, the Base class in the preceding example could have an instance property named test. The following code compiles and executes because the instance property is inherited by the Extender class. The code would also compile and execute if the definition of the test instance variable is moved, but not copied, to the Extender class. package { import flash.display.MovieClip; public class StaticExample extends MovieClip {
  • 170. 170 Object-oriented programming in ActionScript public function StaticExample() { var myExt:Extender = new Extender(); trace(myExt.test); // output: instance } } } class Base { public static var test:String = "static"; public var test:String = "instance"; } class Extender extends Base {} Static properties and the scope chain Although static properties are not inherited, they are within the scope chain of the class that defines them and any subclass of that class. As such, static properties are said to be in scope of both the class in which they are defined and any subclasses. This means that a static property is directly accessible within the body of the class that defines the static property and any subclass of that class. The following example modifies the classes defined in the previous example to show that the static test variable defined in the Base class is in scope of the Extender class. In other words, the Extender class can access the static test variable without prefixing the variable with the name of the class that defines test. package { import flash.display.MovieClip; public class StaticExample extends MovieClip { public function StaticExample() { var myExt:Extender = new Extender(); } } } class Base { public static var test:String = "static"; } class Extender extends Base { public function Extender()
  • 171. Advanced topics 171 { trace(test); // output: static } } If an instance property is defined that uses the same name as a static property in the same class or a superclass, the instance property has higher precedence in the scope chain. The instance property is said to shadow the static property, which means that the value of the instance property is used instead of the value of the static property. For example, the following code shows that if the Extender class defines an instance variable named test, the trace() statement uses the value of the instance variable instead of the value of the static variable.: package { import flash.display.MovieClip; public class StaticExample extends MovieClip { public function StaticExample() { var myExt:Extender = new Extender(); } } } class Base { public static var test:String = "static"; } class Extender extends Base { public var test:String = "instance"; public function Extender() { trace(test); // output: instance } } Advanced topics This section begins with a brief history of ActionScript and OOP and continues with a discussion of the ActionScript 3.0 object model and how it enables the new ActionScript Virtual Machine (AVM2) to perform significantly faster than previous versions of Flash Player that contain the old ActionScript Virtual Machine (AVM1).
  • 172. 172 Object-oriented programming in ActionScript History of ActionScript OOP support Because ActionScript 3.0 builds upon previous versions of ActionScript, it may be helpful to understand how the ActionScript object model has evolved. ActionScript began as a simple scripting mechanism for early versions of the Flash authoring tool. Subsequently, programmers began building increasingly complex applications with ActionScript. In response to the needs of such programmers, each subsequent release has added language features that facilitate the creation of complex applications. ActionScript 1.0 ActionScript 1.0 refers to the version of the language used in Flash Player 6 and earlier. Even at this early stage of development, the ActionScript object model was based on the concept of the object as a fundamental data type. An ActionScript object is a compound data type with a group of properties. When discussing the object model, the term properties includes everything that is attached to an object, such as variables, functions, or methods. Although this first generation of ActionScript does not support the definition of classes with a class keyword, you can define a class using a special kind of object called a prototype object. Instead of using a class keyword to create an abstract class definition that you instantiate into concrete objects, as you do in class-based languages like Java and C++, prototype-based languages like ActionScript 1.0 use an existing object as a model (or prototype) for other objects. While objects in a class-based language may point to a class that serves as its template, objects in a prototype-based language point instead to another object, its prototype, that serves as its template. To create a class in ActionScript 1.0, you define a constructor function for that class. In ActionScript, functions are actual objects, not just abstract definitions. The constructor function that you create serves as the prototypical object for instances of that class. The following code creates a class named Shape and defines one property named visible that is set to true by default: // base class function Shape() {} // Create a property named visible. Shape.prototype.visible = true; This constructor function defines a Shape class that you can instantiate with the new operator, as follows: myShape = new Shape(); Just as the Shape() constructor function object serves as the prototype for instances of the Shape class, it can also serve as the prototype for subclasses of Shape—that is, other classes that extend the Shape class.
  • 173. Advanced topics 173 The creation of a class that is a subclass of the Shape class is a two-step process. First, create the class by defining a constructor function for the class, as follows: // child class function Circle(id, radius) { this.id = id; this.radius = radius; } Second, use the new operator to declare that the Shape class is the prototype for the Circle class. By default, any class you create uses the Object class as its prototype, which means that Circle.prototype currently contains a generic object (an instance of the Object class). To specify that Circle’s prototype is Shape instead of Object, use the following code to change the value of Circle.prototype so that it contains a Shape object instead of a generic object: // Make Circle a subclass of Shape. Circle.prototype = new Shape(); The Shape class and the Circle class are now linked together in an inheritance relationship that is commonly known as the prototype chain. The diagram illustrates the relationships in a prototype chain: The base class at the end of every prototype chain is the Object class. The Object class contains a static property named Object.prototype that points to the base prototype object for all objects created in ActionScript 1.0. The next object in our example prototype chain is the Shape object. This is because the Shape.prototype property was never explicitly set, so it still holds a generic object (an instance of the Object class). The final link in this chain is the Circle class, which is linked to its prototype, the Shape class (the Circle.prototype property holds a Shape object). If we create an instance of the Circle class, as in the following example, the instance inherits the prototype chain of the Circle class: // Create an instance of the Circle class. myCircle = new Circle(); Object.prototype Shape.prototype Circle.prototype
  • 174. 174 Object-oriented programming in ActionScript Recall that we created a property named visible as a member of the Shape class. In our example, the visible property does not exist as a part of the myCircle object, only as a member of the Shape object, yet the following line of code outputs true: trace(myCircle.visible); // output: true Flash Player is able to ascertain that the myCircle object inherits the visible property by walking up the prototype chain. When executing this code, Flash Player first searches through the properties of the myCircle object for a property named visible, but does not find such a property. Flash Player looks next in the Circle.prototype object, but still does not find a property named visible. Continuing up the prototype chain, Flash Player finally finds the visible property defined on the Shape.prototype object and outputs the value of that property. In the interest of simplicity, this section omits many of the details and intricacies of the prototype chain, and aims instead to provide enough information to help you understand the ActionScript 3.0 object model. ActionScript 2.0 ActionScript 2.0 introduced new keywords such as class, extends, public, and private, that allowed you to define classes in a way that is familiar to anyone who works with class- based languages like Java and C++. It’s important to understand that the underlying inheritance mechanism did not change between ActionScript 1.0 and ActionScript 2.0. ActionScript 2.0 merely added a new syntax for defining classes. The prototype chain works the same way in both versions of the language. The new syntax introduced by ActionScript 2.0, shown in the following excerpt, allows you to define classes in a way that many programmers find more intuitive: // base class class Shape { var visible:Boolean = true; } Note that ActionScript 2.0 also introduced type annotations for use with compile-time type checking. This allows you to declare that the visible property in the previous example should contain only a Boolean value. The new extends keyword also simplifies the process of creating a subclass. In the following example, the two-step process necessary in ActionScript 1.0 is accomplished in one step with the extends keyword: // child class class Circle extends Shape { var id:Number; var radius:Number;
  • 175. Advanced topics 175 function Circle(id, radius) { this.id = id; this.radius = radius; } } The constructor is now declared as part of the class definition, and the class properties id and radius must also be declared explicitly. ActionScript 2.0 also added support for the definition of interfaces, which allow you to further refine your object-oriented programs with formally defined protocols for inter-object communication. The ActionScript 3.0 class object A common object-oriented programming paradigm, most commonly associated with Java and C++, uses classes to define types of objects. Programming languages that adopt this paradigm also tend to use classes to construct instances of the data type that the class defines. ActionScript uses classes for both of these purposes, but its roots as a prototype-based language add an interesting characteristic. ActionScript creates for each class definition a special class object that allows sharing of both behavior and state. For many ActionScript programmers, however, this distinction may have no practical coding implications. ActionScript 3.0 is designed such that you can create sophisticated object-oriented ActionScript applications without using, or even understanding, these special class objects. For advanced programmers who want to take advantage of class objects, this section discusses the issues in depth.
  • 176. 176 Object-oriented programming in ActionScript The following diagram shows the structure of a class object that represents a simple class named A that is defined with the statement class A {}: Each rectangle in the diagram represents an object. Each object in the diagram has a subscript character A to represent that it belongs to class A. The class object (CA) contains references to a number of other important objects. An instance traits object (TA) stores the instance properties that are defined within a class definition. A class traits object (TCA) represents the internal type of the class and stores the static properties defined by the class (the subscript character C stands for “class”). The prototype object (PA) always refers to the class object to which it was originally attached through the constructor property. The traits object The traits object, which is new in ActionScript 3.0, was implemented with performance in mind. In previous versions of ActionScript, name lookup could be a time-consuming process as Flash Player walked the prototype chain. In ActionScript 3.0, name lookup is much more efficient and less time consuming, because inherited properties are copied down from superclasses into the traits object of subclasses. The traits object is not directly accessible to programmer code, but its presence can be felt by the improvements in performance and memory usage. The traits object provides the AVM2 with detailed information about the layout and contents of a class. With such knowledge, the AVM2 is able to significantly reduce execution time, because it can often generate direct machine instructions to access properties or call methods directly without a time-consuming name lookup. TCA PACA TA Class.prototype Object.prototype delegate constructor delegate prototype type traits
  • 177. Advanced topics 177 Thanks to the traits object, an object’s memory footprint can be significantly smaller than a similar object in previous versions of ActionScript. For example, if a class is sealed (that is, the class is not declared dynamic), an instance of the class does not need a hash table for dynamically added properties, and can hold little more than a pointer to the traits objects and some slots for the fixed properties defined in the class. As a result, an object that required 100 bytes of memory in ActionScript 2.0 could require as little as 20 bytes of memory in ActionScript 3.0. The prototype object Every ActionScript class object has a property named prototype, which is a reference to the class’s prototype object. The prototype object is a legacy of ActionScript’s roots as prototype- based language. For more information, see “ActionScript 1.0” on page 172. The prototype property is read-only, which means that it cannot be modified to point to different objects. This differs from the class prototype property in previous versions of ActionScript, where the prototype could be reassigned so that it pointed to a different class. Although the prototype property is read-only, the prototype object that it references is not. In other words, new properties can be added to the prototype object. Properties added to the prototype object are shared among all instances of the class. The prototype chain, which was the only inheritance mechanism in previous versions of ActionScript, serves only a secondary role in ActionScript 3.0. The primary inheritance mechanism, fixed property inheritance, is handled internally by the traits object. A fixed property is a variable or method that is defined as part of a class definition. Fixed property inheritance is also called class inheritance, because it is the inheritance mechanism that is associated with keywords such as class, extends, and override. The prototype chain provides an alternative inheritance mechanism that is more dynamic than fixed property inheritance. You can add properties to a class’s prototype object not only as part of the class definition, but also at run time through the class object’s prototype property. Note, however, that if you set the compiler to strict mode, you may not be able to access properties added to a prototype object unless you declare a class with the dynamic keyword. NOTE The traits object is an internal implementation detail, and there is no guarantee that it will not change or even disappear in future versions of ActionScript.
  • 178. 178 Object-oriented programming in ActionScript A good example of a class with several properties attached to the prototype object is the Object class. The Object class’s toString() and valueOf() methods are actually functions assigned to properties of the Object class’s prototype object. The following is an example of how the declaration of these methods could, in theory, look (the actual implementation differs slightly because of implementation details): public dynamic class Object { prototype.toString = function() { // statements }; prototype.valueOf = function() { // statements }; } As mentioned previously, you can attach a property to a class’s prototype object outside the class definition. For example, the toString() method can also be defined outside the Object class definition, as follows: Object.prototype.toString = function() { // statements }; Unlike fixed property inheritance, however, prototype inheritance does not require the override keyword if you want to redefine a method in a subclass. For example. if you want to redefine the valueOf() method in a subclass of the Object class, you have three options. First, you can define a valueOf() method on the subclass’s prototype object inside the class definition. The following code creates a subclass of Object named Foo and redefines the valueOf() method on Foo’s prototype object as part of the class definition. Because every class inherits from Object, it is not necessary to use the extends keyword. dynamic class Foo { prototype.valueOf = function() { return "Instance of Foo"; }; } Second, you can define a valueOf() method on Foo’s prototype object outside the class definition, as shown in the following code: Foo.prototype.valueOf = function() {
  • 179. Advanced topics 179 return "Instance of Foo"; }; Third, you can define a fixed property named valueOf() as part of the Foo class. This technique differs from the others in that it mixes fixed property inheritance with prototype inheritance. Any subclass of Foo that wants to redefine valueOf() must use the override keyword. The following code shows valueOf() defined as a fixed property in Foo: class Foo { function valueOf():String { return "Instance of Foo"; } } The AS3 namespace The existence of two separate inheritance mechanisms, fixed property inheritance and prototype inheritance, creates an interesting compatibility challenge with respect to the properties and methods of the core classes. Compatibility with the ECMAScript, Edition 4 draft language specification requires the use of prototype inheritance, which means that the properties and methods of a core class are defined on the prototype object of that class. On the other hand, compatibility with the Flash Player API calls for the use of fixed property inheritance, which means that the properties and methods of a core class are defined in the class definition using the const, var, and function keywords. Moreover, the use of fixed properties instead of the prototype versions can lead to significant increases in run-time performance. ActionScript 3.0 solves this problem by using both prototype inheritance and fixed property inheritance for the core classes. Each core class contains two sets of properties and methods. One set is defined on the prototype object for compatibility with the ECMAScript specification, and the other set is defined with fixed properties and the AS3 namespace for compatibility with the Flash Player API. The AS3 namespace provides a convenient mechanism for choosing between the two sets of properties and methods. If you do not use the AS3 namespace, an instance of a core class inherits the properties and methods defined on the core class’s prototype object. If you decide to use the AS3 namespace, an instance of a core class inherits the AS3 versions because fixed properties are always preferred over prototype properties. In other words, whenever a fixed property is available, it is always used instead of an identically named prototype property.
  • 180. 180 Object-oriented programming in ActionScript You can selectively use the AS3 namespace version of a property or method by qualifying it with the AS3 namespace. For example, the following code uses the AS3 version of the Array.pop() method: var nums:Array = new Array(1, 2, 3); nums.AS3::pop(); trace(nums); // output: 1,2 Alternatively, you can use the use namespace directive to open the AS3 namespace for all the definitions within a block of code. For example, the following code uses the use namespace directive to open the AS3 namespace for both the pop() and push() methods: use namespace AS3; var nums:Array = new Array(1, 2, 3); nums.pop(); nums.push(5); trace(nums) // output: 1,2,5 ActionScript 3.0 also provides compiler options for each set of properties so that you can apply the AS3 namespace to your entire program. The -as3 compiler option represents the AS3 namespace, and the -es compiler option represents the prototype inheritance option (es stands for ECMAScript). To open the AS3 namespace for your entire program, set the -as3 compiler option to true and the -es compiler option to false. To use the prototype versions, set the compiler options to the opposite values. The default compiler settings for Adobe Flex Builder 2 and Adobe Flash CS3 Professional are -as3 = true and -es = false. If you plan to extend any of the core classes and override any methods, you should understand how the AS3 namespace can affect how you must declare an overridden method. If you are using the AS3 namespace, any method override of a core class method must also use the AS3 namespace along with the override attribute. If you are not using the AS3 namespace and want to redefine a core class method in a subclass, you should not use the AS3 namespace or the override keyword. Example: GeometricShapes The GeometricShapes sample application shows how a number of object-oriented concepts and features can be applied using ActionScript 3.0, including: ■ Defining classes ■ Extending classes ■ Polymorphism and the override keyword ■ Defining, extending and implementing interfaces
  • 181. Example: GeometricShapes 181 It also includes a “factory method” that creates class instances, showing how to declare a return value as an instance of an interface, and use that returned object in a generic way. To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The GeometricShapes application files can be found in the folder Samples/GeometricShapes. The application consists of the following files: Defining the GeometricShapes classes The GeometricShapes application lets the user specify a type of geometric shape and a size. It then responds with a description of the shape, its area, and distance around its perimeter. The application user interface is trivial, including a few controls for selecting the type of shape, setting the size, and displaying the description. The most interesting part of this application is under the surface, in the structure of the classes and interfaces themselves. File Description GeometricShapes.mxml or GeometricShapes.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/ geometricshapes/IGeometricShape.as The base interface defining methods to be implemented by all GeometricShapes application classes. com/example/programmingas3/ geometricshapes/IPolygon.as An interface defining methods to be implemented by GeometricShapes application classes that have multiple sides. com/example/programmingas3/ geometricshapes/RegularPolygon.as A type of geometric shape that has sides of equal length postponed symmetrically around the shape’s center. com/example/programmingas3/ geometricshapes/Circle.as A type of geometric shape that defines a circle. com/example/programmingas3/ geometricshapes/EquilateralTriangle.as A subclass of RegularPolygon that defines a triangle with all sides the same length. com/example/programmingas3/ geometricshapes/Square.as A subclass of RegularPolygon defining a rectangle with all four sides the same length. com/example/programmingas3/ geometricshapes/ GeometricShapeFactory.as A class containing a factory method for creating shapes given a shape type and size.
  • 182. 182 Object-oriented programming in ActionScript This application deals with geometric shapes, but it doesn’t display them graphically. It provides a small library of classes and interfaces that will be reused in a later chapter’s example (see “Example: SpriteArranger” on page 423). The SpriteArranger example displays the shapes graphically and lets the user manipulate them, based on the class framework provided here in the GeometricShapes application. The classes and interfaces that define the geometric shapes in this example are shown in the following diagram using Unified Modeling Language (UML) notation: Defining common behavior with interfaces This GeometricShapes application deals with three types of shapes: circles, squares, and equilateral triangles. The GeometricShapes class structure begins with a very simple interface, IGeometricShape, that lists methods common to all three types of shapes: GeometricShapes Example Classes << interface >> IGeometricShape +getArea (): Number +describe (): Strin << interface >> IPolygon +getPerimeter (): Number +getSumOfAngles (): Number Circle +diameter:Number +Circle () : Circle +getArea () : Number +describe () : String +getCircumference () : Number +numSides : int +sideLength : Number +RegularPolygon (): RegularPolygon +getSumOfAngles (): Number +getPerimeter (): Number +getArea (): Number +describe (): String RegularPolygon +EquilateralTriangle (): EquilateralTriangle +getArea (): Number +describe (): String EquilateralTriangle +Square (): Square +getArea (): Number +describe (): String Square
  • 183. Example: GeometricShapes 183 package com.example.programmingas3.geometricshapes { public interface IGeometricShape { function getArea():Number; function describe():String; } } The interface defines two methods: the getArea() method, which calculates and returns the area of the shape, and the describe() method, which assembles a text description of the shape’s properties. We also want to know the distance around the perimeter of each shape. However, the perimeter of a circle is called the circumference, and it’s calculated in a unique way, so the behavior diverges from that of a triangle or a square. Still there is enough similarity between triangles, squares, and other polygons that it makes sense to define a new interface class just for them: IPolygon. The IPolygon interface is also rather simple, as shown here: package com.example.programmingas3.geometricshapes { public interface IPolygon extends IGeometricShape { function getPerimeter():Number; function getSumOfAngles():Number; } } This interface defines two methods common to all polygons: the getPerimeter() method that measures the combined distance of all the sides, and the getSumOfAngles() method that adds up all the interior angles. The IPolygon interface extends the IGeometricShape interface, which means that any class that implements the IPolygon interface must declare all four methods—the two from the IGeometricShape interface, and the two from the IPolygon interface. Defining the shape classes Once you have a good idea about the methods common to each type of shape, you can define the shape classes themselves. In terms of how many methods you need to implement, the simplest shape is the Circle class, shown here: package com.example.programmingas3.geometricshapes { public class Circle implements IGeometricShape { public var diameter:Number;
  • 184. 184 Object-oriented programming in ActionScript public function Circle(diam:Number = 100):void { this.diameter = diam; } public function getArea():Number { // The formula is Pi * radius * radius. var radius:Number = diameter / 2; return Math.PI * radius * radius; } public function getCircumference():Number { // The formula is Pi * diameter. return Math.PI * diameter; } public function describe():String { var desc:String = "This shape is a Circle.n"; desc += "Its diameter is " + diameter + " pixels.n"; desc += "Its area is " + getArea() + ".n"; desc += "Its circumference is " + getCircumference() + ".n"; return desc; } } } The Circle class implements the IGeometricShape interface, so it must provide code for both the getArea() method and the describe() method. In addition, it defines the getCircumference() method, which is unique to the Circle class. The Circle class also declares a property, diameter, which won’t be found in the other polygon classes. The other two types of shapes, squares and equilateral triangles, have some other things in common: they each have sides of equal length, and there are common formulas you can use to calculate the perimeter and sum of interior angles for both. In fact, those common formulas will apply to any other regular polygons that you need to define in the future as well. The RegularPolygon class will be the superclass for both the Square class and the EquilateralTriangle class. A superclass lets you define common methods in one place, so you don’t have to define them separately in each subclass. Here is the code for the RegularPolygon class: package com.example.programmingas3.geometricshapes { public class RegularPolygon implements IPolygon { public var numSides:int;
  • 185. Example: GeometricShapes 185 public var sideLength:Number; public function RegularPolygon(len:Number = 100, sides:int = 3):void { this.sideLength = len; this.numSides = sides; } public function getArea():Number { // This method should be overridden in subclasses. return 0; } public function getPerimeter():Number { return sideLength * numSides; } public function getSumOfAngles():Number { if (numSides >= 3) { return ((numSides - 2) * 180); } else { return 0; } } public function describe():String { var desc:String = "Each side is " + sideLength + " pixels long.n"; desc += "Its area is " + getArea() + " pixels square.n"; desc += "Its perimeter is " + getPerimeter() + " pixels long.n"; desc += "The sum of all interior angles in this shape is " + getSumOfAngles() + " degrees.n"; return desc; } } } First, the RegularPolygon class declares two properties that are common to all regular polygons: the length of each side (the sideLength property) and the number of sides (the numSides property). The RegularPolygon class implements the IPolygon interface and declares all four of the IPolygon interface methods. It implements two of these—the getPerimeter() and getSumOfAngles() methods—using common formulas.
  • 186. 186 Object-oriented programming in ActionScript Because the formula for the getArea() method will differ from shape to shape, the base class version of the method cannot include common logic that can be inherited by the subclass methods. Instead, it simply returns a 0 default value to indicate that the area was not calculated. To calculate the area of each shape correctly, the subclasses of the RegularPolygon class will have to override the getArea() method themselves. The following code for the EquilateralTriangle class show how the getArea() method is overridden: package com.example.programmingas3.geometricshapes { public class EquilateralTriangle extends RegularPolygon { public function EquilateralTriangle(len:Number = 100):void { super(len, 3); } public override function getArea():Number { // The formula is ((sideLength squared) * (square root of 3)) / 4. return ( (this.sideLength * this.sideLength) * Math.sqrt(3) ) / 4; } public override function describe():String { /* starts with the name of the shape, then delegates the rest of the description work to the RegularPolygon superclass */ var desc:String = "This shape is an equilateral Triangle.n"; desc += super.describe(); return desc; } } } The override keyword indicates that the EquilateralTriangle.getArea() method intentionally overrides the getArea() method from the RegularPolygon superclass. When the EquilateralTriangle.getArea() method is called, it calculates the area using the formula in the preceding code, and the code in the RegularPolygon.getArea() method never executes. In contrast, the EquilateralTriangle class doesn’t define its own version of the getPerimeter() method. When the EquilateralTriangle.getPerimeter() method is called, the call goes up the inheritance chain and executes the code in the getPerimeter() method of the RegularPolygon superclass.
  • 187. Example: GeometricShapes 187 The EquilateralTriangle() constructor uses the super() statement to explicitly invoke the RegularPolygon() constructor of its superclass. If both constructors had the same set of parameters, you could have omitted the EquilateralTriangle() constructor completely, and the RegularPolygon() constructor would be executed instead. However, the RegularPolygon() constructor needs an extra parameter, numSides. So the EquilateralTriangle() constructor calls super(len, 3), which passes along the len input parameter and the value 3 to indicate that the triangle will have 3 sides. The describe() method also uses the super() statement, but in a different way—to invoke the RegularPolygon superclass’ version of the describe() method. The EquilateralTriangle.describe() method first sets the desc string variable to a statement about the type of shape. Then it gets the results of the RegularPolygon.describe() method by calling super.describe(), and it appends that result to the desc string. The Square class won’t be described in detail here, but it is similar to the EquilateralTriangle class, providing a constructor and its own implementations of the getArea() and describe() methods. Polymorphism and the factory method A set of classes that make good use of interfaces and inheritance can be used in many interesting ways. For example, all of the shape classes described so far either implement the IGeometricShape interface or extend a superclass that does. So if you define a variable to be an instance of IGeometricShape, you don’t have to know whether it is actually an instance of the Circle or the Square class in order to call its describe() method. The following code shows how this works: var myShape:IGeometricShape = new Circle(100); trace(myShape.describe()); When myShape.describe() is called, it executes the method Circle.describe(), because even though the variable is defined as an instance of the IGeometricShape interface, Circle is its underlying class. This example shows the principle of polymorphism in action: the exact same method call results in different code being executed, depending on the class of the object whose method is being invoked. The GeometricShapes application applies this kind of interface-based polymorphism using a simplified version of a design pattern known as the factory method. The term factory method refers to a function that returns an object whose underlying data type or contents can differ depending on the context.
  • 188. 188 Object-oriented programming in ActionScript The GeometricShapeFactory class shown here defines a factory method named createShape(): package com.example.programmingas3.geometricshapes { public class GeometricShapeFactory { public static var currentShape:IGeometricShape; public static function createShape(shapeName:String, len:Number):IGeometricShape { switch (shapeName) { case "Triangle": return new EquilateralTriangle(len); case "Square": return new Square(len); case "Circle": return new Circle(len); } return null; } public static function describeShape(shapeType:String, shapeSize:Number):String { GeometricShapeFactory.currentShape = GeometricShapeFactory.createShape(shapeType, shapeSize); return GeometricShapeFactory.currentShape.describe(); } } } The createShape() factory method lets the shape subclass constructors define the details of the instances that they create, while returning the new objects as IGeometricShape instances so that they can be handled by the application in a more general way. The describeShape() method in the preceding example shows how an application can use the factory method to get a generic reference to a more specific object. The application can get the description for a newly created Circle object like this: GeometricShapeFactory.describeShape(“Circle”, 100);
  • 189. Example: GeometricShapes 189 The describeShape() method then calls the createShape() factory method with the same parameters, storing the new Circle object in a static variable named currentShape, which was typed as an IGeometricShape object. Next, the describe() method is called on the currentShape object, and that call is automatically resolved to execute the Circle.describe() method, returning a detailed description of the circle. Enhancing the sample application The real power of interfaces and inheritance becomes apparent when you enhance or change your application. Say that you wanted to add a new shape, a pentagon, to this sample application. You would create a new Pentagon class that extends the RegularPolygon class and defines its own versions of the getArea() and describe() methods. Then you would add a new Pentagon option to the combo box in the application’s user interface. But that’s it. The Pentagon class would automatically get the functionality of the getPerimeter() method and the getSumOfAngles() method from the RegularPolygon class by inheritance. Because it inherits from a class that implements the IGeometricShape interface, a Pentagon instance can be treated as an IGeometricShape instance too. That means you do not need to change any of the methods in the GeometricShapeFactory class, making it much easier to add new types of shapes when needed. You may want to add a Pentagon class to the Geometric Shapes example as an exercise, to see how interfaces and inheritance can ease the workload of adding new features to an application.
  • 191. 191 5 CHAPTER 5 Working with dates and times Timing might not be everything, but it's usually a key factor in software applications. ActionScript 3.0 provides powerful ways to manage calendar dates, times, and time intervals. Two main classes provide most of this timing functionality: the Date class and the new Timer class in the flash.utils package. Contents Basics of dates and times. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .191 Managing calendar dates and times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Controlling time intervals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Example: Simple analog clock. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Basics of dates and times Introduction to working with dates and times Dates and times are a common type of information used in ActionScript programs. For instance, you might need to know the current day of the week or to measure how much time a user spends on a particular screen, among many other possibilities. In ActionScript, you can use the Date class to represent a single moment in time, including date and time information. Within a Date instance are values for the individual date and time units, including year, month, date, day of the week, hour, minutes, seconds, milliseconds, and time zone. For more advanced uses, ActionScript also includes the Timer class, which you can use to perform actions after a certain delay or at repeated intervals.
  • 192. 192 Working with dates and times Common date and time tasks This chapter describes the following common tasks for working with date and time information: ■ Working with Date objects ■ Getting the current date and time ■ Accessing individual date and time units (days, years, hours, minutes, and so on) ■ Performing arithmetic with dates and times ■ Converting between time zones ■ Performing repeating actions ■ Performing actions after a set time interval Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ UTC time: Coordinated Universal Time—the “zero hour” reference time zone. All other time zones are defined as a number of hours relative to (ahead of or behind) UTC time. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because the code listings in this chapter deal primarily with Date objects, testing the examples will involve viewing the values of the variables used in the examples, either by writing values into a text field instance on the Stage, or by using the trace() function to print values to the Output panel. These techniques are described in detail in “Testing in-chapter example code listings” on page 63.
  • 193. Managing calendar dates and times 193 Managing calendar dates and times All of the calendar date and time management functions in ActionScript 3.0 are concentrated in the top-level Date class. The Date class contains methods and properties that let you handle dates and times in either Coordinated Universal Time (UTC) or in local time specific to a time zone. UTC is a standard time definition that is essentially the same as Greenwich Mean Time (GMT). Creating Date objects The Date class boasts one of the most versatile constructor methods of all the core classes. You can invoke it four different ways. First, if given no parameters, the Date() constructor returns a Date object containing the current date and time, in local time based on your time zone. Here’s an example: var now:Date = new Date(); Second, if given a single numeric parameter, the Date() constructor treats that as the number of milliseconds since January 1, 1970, and returns a corresponding Date object. Note that the millisecond value you pass in is treated as milliseconds since January 1, 1970, in UTC. However, the Date object shows values in your local time zone, unless you use the UTC- specific methods to retrieve and display them. If you create a new Date object using a single milliseconds parameter, make sure you account for the time zone difference between your local time and UTC. The following statements create a Date object set to midnight on the day of January 1, 1970, in UTC: var millisecondsPerDay:int = 1000 * 60 * 60 * 24; // gets a Date one day after the start date of 1/1/1970 var startTime:Date = new Date(millisecondsPerDay); Third, you can pass multiple numeric parameters to the Date() constructor. It treats those parameters as the year, month, day, hour, minute, second, and millisecond, respectively, and returns a corresponding Date object. Those input parameters are assumed to be in local time rather than UTC. The following statements get a Date object set to midnight at the start of January 1, 2000, in local time: var millenium:Date = new Date(2000, 0, 1, 0, 0, 0, 0);
  • 194. 194 Working with dates and times Fourth, you can pass a single string parameter to the Date() constructor. It will try to parse that string into date or time components and then return a corresponding Date object. If you use this approach, it’s a good idea to enclose the Date() constructor in a try..catch block to trap any parsing errors. The Date() constructor accepts a number of different string formats, as listed in the ActionScript 3.0 Language and Components Reference. The following statement initializes a new Date object using a string value: var nextDay:Date = new Date(“Mon May 1 2006 11:30:00 AM”); If the Date() constructor cannot successfully parse the string parameter, it will not raise an exception. However, the resulting Date object will contain an invalid date value. Getting time unit values You can extract the values for various units of time within a Date object using properties or methods of the Date class. Each of the following properties gives you the value of a time unit in the Date object: ■ The fullYear property ■ The month property, which is in a numeric format with 0 for January up to 11 for December ■ The date property, which is the calendar number of the day of the month, in the range of 1 to 31 ■ The day property, which is the day of the week in numeric format, with 0 standing for Sunday ■ The hours property, in the range of 0 to 23 ■ The minutes property ■ The seconds property ■ The milliseconds property In fact, the Date class gives you a number of ways to get each of these values. For example, you can get the month value of a Date object in four different ways: ■ The month property ■ The getMonth() method ■ The monthUTC property ■ The getMonthUTC() method All four ways are essentially equivalent in terms of efficiency, so you can use whichever approach suits your application best.
  • 195. Managing calendar dates and times 195 The properties just listed all represent components of the total date value. For example, the milliseconds property will never be greater than 999, since when it reaches 1000 the seconds value increases by 1 and the milliseconds property resets to 0. If you want to get the value of the Date object in terms of milliseconds since January 1, 1970 (UTC), you can use the getTime() method. Its counterpart, the setTime() method, lets you change the value of an existing Date object using milliseconds since January 1, 1970 (UTC). Performing date and time arithmetic You can perform addition and subtraction on dates and times with the Date class. Date values are kept internally in terms of milliseconds, so you should convert other values to milliseconds before adding them to or subtracting them from Date objects. If your application will perform a lot of date and time arithmetic, you might find it useful to create constants that hold common time unit values in terms of milliseconds, like the following: public static const millisecondsPerMinute:int = 1000 * 60; public static const millisecondsPerHour:int = 1000 * 60 * 60; public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24; Now it is easy to perform date arithmetic using standard time units. The following code sets a date value to one hour from the current time using the getTime() and setTime() methods: var oneHourFromNow:Date = new Date(); oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour); Another way to set a date value is to create a new Date object using a single milliseconds parameter. For example, the following code adds 30 days to one date to calculate another: // sets the invoice date to today’s date var invoiceDate:Date = new Date(); // adds 30 days to get the due date var dueDate:Date = new Date(invoiceDate.getTime() + (30 * millisecondsPerDay)); Next, the millisecondsPerDay constant is multiplied by 30 to represent 30 days’ time and the result is added to the invoiceDate value and used to set the dueDate value.
  • 196. 196 Working with dates and times Converting between time zones Date and time arithmetic comes in handy when you want to convert dates from one time zone to another. So does the getTimezoneOffset() method, which returns the value in minutes by which the Date object’s time zone differs from UTC. It returns a value in minutes because not all time zones are set to even-hour increments—some have half-hour offsets from neighboring zones. The following example uses the time zone offset to convert a date from local time to UTC. It does the conversion by first calculating the time zone value in milliseconds and then adjusting the Date value by that amount: // creates a Date in local time var nextDay:Date = new Date("Mon May 1 2006 11:30:00 AM"); // converts the Date to UTC by adding or subtracting the time zone offset var offsetMilliseconds:Number = nextDay.getTimezoneOffset() * 60 * 1000; nextDay.setTime(nextDay.getTime() + offsetMilliseconds); Controlling time intervals When you develop applications using Adobe Flash CS3 Professional, you have access to the timeline, which provides a steady, frame-by-frame progression through your application. In pure ActionScript projects, however, you must rely on other timing mechanisms. Loops versus timers In some programming languages, you must devise your own timing schemes using loop statements like for or do..while. Loop statements generally execute as fast as the local machine allows, which means that the application runs faster on some machines and slower on others. If your application needs a consistent timing interval, you need to tie it to an actual calendar or clock time. Many applications, such as games, animations, and real-time controllers, need regular, time-driven ticking mechanisms that are consistent from machine to machine. The ActionScript 3.0 Timer class provides a powerful solution. Using the ActionScript 3.0 event model, the Timer class dispatches timer events whenever a specified time interval is reached.
  • 197. Controlling time intervals 197 The Timer class The preferred way to handle timing functions in ActionScript 3.0 is to use the Timer class (flash.utils.Timer), which can be used to dispatch events whenever an interval is reached. To start a timer, you first create an instance of the Timer class, telling it how often to generate a timer event and how many times to do so before stopping. For example, the following code creates a Timer instance that dispatches an event every second and continues for 60 seconds: var oneMinuteTimer:Timer = new Timer(1000, 60); The Timer object dispatches a TimerEvent object each time the given interval is reached. A TimerEvent object’s event type is timer (defined by the constant TimerEvent.TIMER). A TimerEvent object contains the same properties as a standard Event object. If the Timer instance is set to a fixed number of intervals, it will also dispatch a timerComplete event (defined by the constant TimerEvent.TIMER_COMPLETE) when it reaches the final interval. Here is a small sample application showing the Timer class in action: package { import flash.display.Sprite; import flash.events.TimerEvent; import flash.utils.Timer; public class ShortTimer extends Sprite { public function ShortTimer() { // creates a new five-second Timer var minuteTimer:Timer = new Timer(1000, 5); // designates listeners for the interval and completion events minuteTimer.addEventListener(TimerEvent.TIMER, onTick); minuteTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete); // starts the timer ticking minuteTimer.start(); } public function onTick(event:TimerEvent):void { // displays the tick count so far // The target of this event is the Timer instance itself. trace("tick " + event.target.currentCount); }
  • 198. 198 Working with dates and times public function onTimerComplete(event:TimerEvent):void { trace("Time's Up!"); } } } When the ShortTimer class is created, it creates a Timer instance that will tick once per second for five seconds. Then it adds two listeners to the timer: one that listens to each tick, and one that listens for the timerComplete event. Next, it starts the timer ticking, and from that point forward, the onTick() method executes at one-second intervals. The onTick() method simply displays the current tick count. After five seconds have passed, the onTimerComplete() method executes, telling you that the time is up. When you run this sample, you should see the following lines appear in your console or trace window at the rate of one line per second: tick 1 tick 2 tick 3 tick 4 tick 5 Time's Up! Timing functions in the flash.utils package ActionScript 3.0 contains a number of timing functions similar to those that were available in ActionScript 2.0. These functions are provided as package-level functions in the flash.utils package, and they operate just as they did in ActionScript 2.0. Function Description clearInterval(id:uint):void Cancels a specified setInterval() call. clearTimeout(id:uint):void Cancels a specified setTimeout() call. getTimer():int Returns the number of milliseconds that have elapsed since Adobe Flash Player was initialized. setInterval(closure:Function, delay:Number, ... arguments):uint Runs a function at a specified interval (in milliseconds). setTimeout(closure:Function, delay:Number, ... arguments):uint Runs a specified function after a specified delay (in milliseconds).
  • 199. Example: Simple analog clock 199 These functions remain in ActionScript 3.0 for backward compatibility. Adobe does not recommend that you use them in new ActionScript 3.0 applications. In general, it is easier and more efficient to use the Timer class in your applications. Example: Simple analog clock A simple analog clock example illustrates two of the date and time concepts discussed in this chapter: ■ Getting the current date and time and extracting values for the hours, minutes, and seconds ■ Using a Timer to set the pace of an application To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The SimpleClock application files can be found in the folder Samples/SimpleClock. The application consists of the following files: Defining the SimpleClock class The clock example is simple, but it’s a good idea to organize even simple applications well so you could easily expand them in the future. To that end, the SimpleClock application uses the SimpleClock class to handle the startup and time-keeping tasks, and then uses another class named AnalogClockFace to actually display the time. Here is the code that defines and initializes the SimpleClock class (note that in the Flash version, SimpleClock extends the Sprite class instead): public class SimpleClock extends UIComponent { /** * The time display component. */ private var face:AnalogClockFace; File Description SimpleClockApp.mxml or SimpleClockApp.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/ simpleclock/SimpleClock.as The main application file. com/example/programmingas3/ simpleclock/AnalogClockFace.as Draws a round clock face and hour, minute, and seconds hands based on the time.
  • 200. 200 Working with dates and times /** * The Timer that acts like a heartbeat for the application. */ private var ticker:Timer; The class has two important properties: ■ The face property, which is an instance of the AnalogClockFace class ■ The ticker property, which is an instance of the Timer class The SimpleClock class uses a default constructor. The initClock() method takes care of the real setup work, creating the clock face and starting the Timer instance ticking. Creating the clock face The next lines in the SimpleClock code create the clock face that is used to display the time: /** * Sets up a SimpleClock instance. */ public function initClock(faceSize:Number = 200) { // creates the clock face and adds it to the display list face = new AnalogClockFace(Math.max(20, faceSize)); face.init(); addChild(face); // draws the initial clock display face.draw(); The size of the face can be passed in to the initClock() method. If no faceSize value is passed, a default size of 200 pixels is used. Next, the application initializes the face and then adds it to the display list using the addChild() method inherited from the DisplayObject class. Then it calls the AnalogClockFace.draw() method to display the clock face once, showing the current time. Starting the timer After creating the clock face, the initClock() method sets up a timer: // creates a Timer that fires an event once per second ticker = new Timer(1000); // designates the onTick() method to handle Timer events ticker.addEventListener(TimerEvent.TIMER, onTick); // starts the clock ticking ticker.start();
  • 201. Example: Simple analog clock 201 First this method instantiates a Timer instance that will dispatch an event once per second (every 1000 milliseconds). Since no second repeatCount parameter is passed to the Timer() constructor, the Timer will keep repeating indefinitely. The SimpleClock.onTick() method will execute once per second when the timer event is received: public function onTick(event:TimerEvent):void { // updates the clock display face.draw(); } The AnalogClockFace.draw() method simply draws the clock face and hands. Displaying the current time Most of the code in the AnalogClockFace class involves setting up the clock face’s display elements. When the AnalogClockFace is initialized, it draws a circular outline, places a numeric text label at each hour mark, and then creates three Shape objects, one each for the hour hand, the minute hand, and the second hand on the clock. Once the SimpleClock application is running, it calls the AnalogClockFace.draw() method each second, as follows: /** * Called by the parent container when the display is being drawn. */ public override function draw():void { // stores the current date and time in an instance variable currentTime = new Date(); showTime(currentTime); } This method saves the current time in a variable, so the time can’t change in the middle of drawing the clock hands. Then it calls the showTime() method to display the hands, as the following shows: /** * Displays the given Date/Time in that good old analog clock style. */ public function showTime(time:Date):void { // gets the time values var seconds:uint = time.getSeconds(); var minutes:uint = time.getMinutes(); var hours:uint = time.getHours();
  • 202. 202 Working with dates and times // multiplies by 6 to get degrees this.secondHand.rotation = 180 + (seconds * 6); this.minuteHand.rotation = 180 + (minutes * 6); // Multiply by 30 to get basic degrees, then // add up to 29.5 degrees (59 * 0.5) // to account for the minutes. this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5); } First, this method extracts the values for the hours, minutes, and seconds of the current time. Then it uses these values to calculate the angle for each hand. Since the second hand makes a full rotation in 60 seconds, it rotates 6 degrees each second (360/60). The minute hand rotates the same amount each minute. The hour hand updates every minute, too, so it can show some progress as the minutes tick by. It rotates 30 degrees each hour (360/12), but it also rotates half a degree each minute (30 degrees divided by 60 minutes).
  • 203. 203 6 CHAPTER 6 Working with strings The String class contains methods that let you work with text strings. Strings are important in working with many objects. The methods described in this chapter are useful in working with strings used in objects such as TextField, StaticText, XML, ContextMenu, and FileReference objects. Strings are sequences of characters. ActionScript 3.0 supports ASCII and Unicode characters. Contents Basics of strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 Creating strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 The length property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .207 Working with characters in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Comparing strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Obtaining string representations of other objects. . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Concatenating strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 Finding substrings and patterns in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 Converting strings between uppercase and lowercase . . . . . . . . . . . . . . . . . . . . . . . 215 Example: ASCII art . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
  • 204. 204 Working with strings Basics of strings Introduction to working with strings In programming parlance, a string is a text value—a sequence of letters, numbers, or other characters strung together into a single value. For instance, this line of code creates a variable with the data type String and assigns a literal string value to that variable: var albumName:String = "Three for the money"; As this example shows, in ActionScript you can denote a string value by surrounding text with double or single quotation marks. Here are several more examples of strings: "Hello" "555-7649" "http://www.adobe.com/" Any time you manipulate a piece of text in ActionScript, you are working with a string value. The ActionScript String class is the data type you can use to work with text values. String instances are frequently used for properties, method parameters, and so forth in many other ActionScript classes. Common tasks for working with strings The following are common string-related tasks that are explored in this chapter: ■ Creating String objects ■ Working with special characters such as carriage-return, tab, and non-keyboard characters ■ Measuring string length ■ Isolating individual characters in a string ■ Joining strings ■ Comparing strings ■ Finding, extracting, and replacing portions of a string ■ Making strings uppercase or lowercase
  • 205. Creating strings 205 Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ ASCII: A system for representing text characters and symbols in computer programs. The ASCII system supports the 26-letter English alphabet, plus a limited set of additional characters. ■ Character: The smallest unit of text data (a single letter or symbol). ■ Concatenation: Joining multiple string values together by adding one to the end of the other, creating a new string value. ■ Empty string: A string that contains no text, white space, or other characters, written as "". An empty string value is different from a String variable with a null value—a null String variable is a variable that does not have a String instance assigned to it, whereas an empty string has an instance with a value that contains no characters. ■ String: A textual value (sequence of characters). ■ String literal (or “literal string”): A string value written explicitly in code, written as a text value surrounded by double quotation marks or single quotation marks. ■ Substring: A string that is a portion of another string. ■ Unicode: A standard system for representing text characters and symbols in computer programs. The Unicode system allows for the use of any character in any writing system. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because the code listings in this chapter deal primarily with manipulating text, testing the examples will involve viewing the values of the variables used in the examples, either by writing values into a text field instance on the Stage or by using the trace() function to print values to the Output panel. These techniques are described in detail in “Testing in-chapter example code listings” on page 63. Creating strings The String class is used to represent string (textual) data in ActionScript 3.0. ActionScript strings support both ASCII and Unicode characters. The simplest way to create a string is to use a string literal. To declare a string literal, use straight double quotation mark (") or single quotation mark (') characters. For example, the following two strings are equivalent: var str1:String = "hello"; var str2:String = 'hello';
  • 206. 206 Working with strings You can also declare a string by using the new operator, as follows: var str1:String = new String("hello"); var str2:String = new String(str1); var str3:String = new String(); // str3 == "" The following two strings are equivalent: var str1:String = "hello"; var str2:String = new String("hello"); To use single quotation marks (') within a string literal defined with single quotation mark (') delimiters, use the backslash escape character (). Similarly, to use double quotation marks (") within a string literal defined with double quotation marks (") delimiters, use the backslash escape character (). The following two strings are equivalent: var str1:String = "That's "A-OK""; var str2:String = 'That's "A-OK"'; You may choose to use single quotation marks or double quotation marks based on any single or double quotation marks that exist in a string literal, as in the following: var str1:String = "ActionScript <span class='heavy'>3.0</span>"; var str2:String = '<item id="155">banana</item>'; Keep in mind that ActionScript distinguishes between a straight single quotation mark (') and a left or right single quotation mark (‘ or ’). The same is true for double quotation marks. Use straight quotation marks to delineate string literals. When pasting text from another source into ActionScript, be sure to use the correct characters.
  • 207. The length property 207 As the following table shows, you can use the backslash escape character () to define other characters in string literals: The length property Every string has a length property, which is equal to the number of characters in the string: var str:String = "Adobe"; trace(str.length); // output: 5 An empty string and a null string both have a length of 0, as the following example shows: var str1:String = new String(); trace(str1.length); // output: 0 str2:String = ''; trace(str2.length); // output: 0 Escape sequence Character b Backspace f Form feed n Newline r Carriage return t Tab unnnn The Unicode character with the character code specified by the hexadecimal number nnnn; for example, u263a is the smiley character. xnn The ASCII character with the character code specified by the hexadecimal number nn ' Single quotation mark " Double quotation mark Single backslash character
  • 208. 208 Working with strings Working with characters in strings Every character in a string has an index position in the string (an integer). The index position of the first character is 0. For example, in the following string, the character y is in position 0 and the character w is in position 5: "yellow" You can examine individual characters in various positions in a string using the charAt() method and the charCodeAt() method, as in this example: var str:String = "hello world!"; for (var:i = 0; i < str.length; i++) { trace(str.charAt(i), "-", str.charCodeAt(i)); } When you run this code, the following output is produced: h - 104 e - 101 l - 108 l - 108 o - 111 - 32 w - 119 o - 111 r - 114 l - 108 d - 100 ! - 33 You can also use character codes to define a string using the fromCharCode() method, as the following example shows: var myStr:String = String.fromCharCode(104,101,108,108,111,32,119,111,114,108,100,33); // Sets myStr to "hello world!" Comparing strings You can use the following operators to compare strings: <, <=, !=, ==, =>, and >. These operators can be used with conditional statements, such as if and while, as the following example shows: var str1:String = "Apple"; var str2:String = "apple"; if (str1 < str2) { trace("A < a, B < b, C < c, ...");
  • 209. Concatenating strings 209 } When using these operators with strings, ActionScript considers the character code value of each character in the string, comparing characters from left to right, as in the following: trace("A" < "B"); // true trace("A" < "a"); // true trace("Ab" < "az"); // true trace("abc" < "abza"); // true Use the == and != operators to compare strings with each other and to compare strings with other types of objects, as the following example shows: var str1:String = "1"; var str1b:String = "1"; var str2:String = "2"; trace(str1 == str1b); // true trace(str1 == str2); // false var total:uint = 1; trace(str1 == total); // true Obtaining string representations of other objects You can obtain a String representation for any kind of object. All objects have a toString() method for this purpose: var n:Number = 99.47; var str:String = n.toString(); // str == "99.47" When using the + concatenation operator with a combination of String objects and objects that are not strings, you do not need to use the toString() method. For details on concatenation, see the next section. The String() global function returns the same value for a given object as the value returned by the object calling the toString() method. Concatenating strings Concatenation of strings means taking two strings and joining them sequentially into one. For example, you can use the + operator to concatenate two strings: var str1:String = "green"; var str2:String = "ish"; var str3:String = str1 + str2; // str3 == "greenish"
  • 210. 210 Working with strings You can also use the += operator to the produce the same result, as the following example shows: var str:String = "green"; str += "ish"; // str == "greenish" Additionally, the String class includes a concat() method, which can be used as follows: var str1:String = "Bonjour"; var str2:String = "from"; var str3:String = "Paris"; var str4:String = str1.concat(" ", str2, " ", str3); // str4 == "Bonjour from Paris" If you use the + operator (or the += operator) with a String object and an object that is not a string, ActionScript automatically converts the nonstring object to a String object in order to evaluate the expression, as shown in this example: var str:String = "Area = "; var area:Number = Math.PI * Math.pow(3, 2); str = str + area; // str == "Area = 28.274333882308138" However, you can use parentheses for grouping to provide context for the + operator, as the following example shows: trace("Total: $" + 4.55 + 1.45); // output: Total: $4.551.45 trace("Total: $" + (4.55 + 1.45)); // output: Total: $6 Finding substrings and patterns in strings Substrings are sequential characters within a string. For example, the string "abc" has the following substrings: "", "a", "ab", "abc", "b", "bc", "c". You can use ActionScript methods to locate substrings of a string. Patterns are defined in ActionScript by strings or by regular expressions. For example, the following regular expression defines a specific pattern—the letters A, B, and C followed by a digit character (the forward slashes are regular expression delimiters): /ABCd/ ActionScript includes methods for finding patterns in strings and for replacing found matches with replacement substrings. These methods are described in the following sections. Regular expressions can define intricate patterns. For more information, see Chapter 9, “Using regular expressions,” on page 285.
  • 211. Finding substrings and patterns in strings 211 Finding a substring by character position The substr() and substring() methods are similar. Both return a substring of a string. Both take two parameters. In both methods, the first parameter is the position of the starting character in the given string. However, in the substr() method, the second parameter is the length of the substring to return, and in the substring() method, the second parameter is the position of the character at the end of the substring (which is not included in the returned string). This example shows the difference between these two methods: var str:String = "Hello from Paris, Texas!!!"; trace(str.substr(11,15)); // output: Paris, Texas!!! trace(str.substring(11,15)); // output: Pari The slice() method functions similarly to the substring() method. When given two non- negative integers as parameters, it works exactly the same. However, the slice() method can take negative integers as parameters, in which case the character position is taken from the end of the string, as shown in the following example: var str:String = "Hello from Paris, Texas!!!"; trace(str.slice(11,15)); // output: Pari trace(str.slice(-3,-1)); // output: !! trace(str.slice(-3,26)); // output: !!! trace(str.slice(-3,str.length)); // output: !!! trace(str.slice(-8,-3)); // output: Texas You can combine non-negative and negative integers as the parameters of the slice() method. Finding the character position of a matching substring You can use the indexOf() and lastIndexOf() methods to locate matching substrings within a string, as the following example shows: var str:String = "The moon, the stars, the sea, the land"; trace(str.indexOf("the")); // output: 10 Notice that the indexOf() method is case-sensitive. You can specify a second parameter to indicate the index position in the string from which to start the search, as follows: var str:String = "The moon, the stars, the sea, the land" trace(str.indexOf("the", 11)); // output: 21 The lastIndexOf() method finds the last occurrence of a substring in the string: var str:String = "The moon, the stars, the sea, the land" trace(str.lastIndexOf("the")); // output: 30
  • 212. 212 Working with strings If you include a second parameter with the lastIndexOf() method, the search is conducted from that index position in the string working backward (from right to left): var str:String = "The moon, the stars, the sea, the land" trace(str.lastIndexOf("the", 29)); // output: 21 Creating an array of substrings segmented by a delimiter You can use the split() method to create an array of substrings, which is divided based on a delimiter. For example, you can segment a comma-delimited or tab-delimited string into multiple strings. The following example shows how to split an array into substrings with the ampersand (&) character as the delimiter: var queryStr:String = "first=joe&last=cheng&title=manager&StartDate=3/6/ 65"; var params:Array = queryStr.split("&", 2); // params == ["first=joe","last=cheng"] The second parameter of the split() method, which is optional, defines the maximum size of the array that is returned. You can also use a regular expression as the delimiter character: var str:String = "Give met5." var a:Array = str.split(/s+/); // a == ["Give","me","5."] For more information, see Chapter 9, “Using regular expressions,” on page 285 and the ActionScript 3.0 Language and Components Reference. Finding patterns in strings and replacing substrings The String class includes the following methods for working with patterns in strings: ■ Use the match() and search() methods to locate substrings that match a pattern. ■ Use the replace() method to find substrings that match a pattern and replace them with a specified substring. These methods are described in the following sections. You can use strings or regular expressions to define patterns used in these methods. For more information on regular expressions, see Chapter 9, “Using regular expressions,” on page 285.
  • 213. Finding substrings and patterns in strings 213 Finding matching substrings The search() method returns the index position of the first substring that matches a given pattern, as shown in this example: var str:String = "The more the merrier."; // (This search is case-sensitive.) trace(str.search("the")); // output: 9 You can also use regular expressions to define the pattern to match, as this example shows: var pattern:RegExp = /the/i; var str:String = "The more the merrier."; trace(str.search(pattern)); // 0 The output of the trace() method is 0, because the first character in the string is index position 0. The i flag is set in the regular expression, so the search is not case-sensitive. The search() method finds only one match and returns its starting index position, even if the g (global) flag is set in the regular expression. The following example shows a more intricate regular expression, one that matches a string in double quotation marks: var pattern:RegExp = /"[^"]*"/; var str:String = "The "more" the merrier."; trace(str.search(pattern)); // output: 4 str = "The "more the merrier."; trace(str.search(pattern)); // output: -1 // (Indicates no match, since there is no closing double quotation mark.) The match() method works similarly. It searches for a matching substring. However, when you use the global flag in a regular expression pattern, as in the following example, match() returns an array of matching substrings: var str:String = "[email protected], [email protected]"; var pattern:RegExp = /w*@w*.[org|com]+/g; var results:Array = str.match(pattern); The results array is set to the following: ["[email protected]","[email protected]"] For more information on regular expressions, see Chapter 9, “Using regular expressions,” on page 285.
  • 214. 214 Working with strings Replacing matched substrings You can use the replace() method to search for a specified pattern in a string and replace matches with the specified replacement string, as the following example shows: var str:String = "She sells seashells by the seashore."; var pattern:RegExp = /sh/gi; trace(str.replace(pattern, "sch")); //sche sells seaschells by the seaschore. Note that in this example, the matched strings are not case-sensitive because the i (ignoreCase) flag is set in the regular expression, and multiple matches are replaced because the g (global) flag is set. For more information, see Chapter 9, “Using regular expressions,” on page 285. You can include the following $ replacement codes in the replacement string. The replacement text shown in the following table is inserted in place of the $ replacement code: For example, the following shows the use of the $2 and $1 replacement codes, which represent the first and second capturing group matched: var str:String = "flip-flop"; var pattern:RegExp = /(w+)-(w+)/g; trace(str.replace(pattern, "$2-$1")); // flop-flip You can also use a function as the second parameter of the replace() method. The matching text is replaced by the returned value of the function. var str:String = "Now only $9.95!"; var price:RegExp = /$([d,]+.d+)+/i; trace(str.replace(price, usdToEuro)); $ Code Replacement Text $$ $ $& The matched substring. $` The portion of the string that precedes the matched substring. This code uses the straight left single quotation mark character (`), not the straight single quotation mark (') or the left curly single quotation mark (‘). $' The portion of the string that follows the matched substring. This code uses the straight single quotation mark (’). $n The nth captured parenthetical group match, where n is a single digit, 1- 9, and $n is not followed by a decimal digit. $nn The nnth captured parenthetical group match, where nn is a two-digit decimal number, 01–99. If the nnth capture is undefined, the replacement text is an empty string.
  • 215. Converting strings between uppercase and lowercase 215 function usdToEuro(matchedSubstring:String, capturedMatch1:String, index:int, str:String):String { var usd:String = capturedMatch1; usd = usd.replace(",", ""); var exchangeRate:Number = 0.853690; var euro:Number = usd * exchangeRate; const euroSymbol:String = String.fromCharCode(8364); return euro.toFixed(2) + " " + euroSymbol; } When you use a function as the second parameter of the replace() method, the following arguments are passed to the function: ■ The matching portion of the string. ■ Any capturing parenthetical group matches. The number of arguments passed this way will vary depending on the number of parenthetical matches. You can determine the number of parenthetical matches by checking arguments.length - 3 within the function code. ■ The index position in the string where the match begins. ■ The complete string. Converting strings between uppercase and lowercase As the following example shows, the toLowerCase() method and the toUpperCase() method convert alphabetical characters in the string to lowercase and uppercase, respectively: var str:String = "Dr. Bob Roberts, #9." trace(str.toLowerCase()); // dr. bob roberts, #9. trace(str.toUpperCase()); // DR. BOB ROBERTS, #9. After these methods are executed, the source string remains unchanged. To transform the source string, use the following code: str = str.toUpperCase(); These methods work with extended characters, not simply a–z and A–Z: var str:String = "José Barça"; trace(str.toUpperCase(), str.toLowerCase()); // JOSÉ BARÇA josé barça
  • 216. 216 Working with strings Example: ASCII art This ASCII Art example shows a number of features of working with the String class in ActionScript 3.0, including the following: ■ The split() method of the String class is used to extract values from a character- delimited string (image information in a tab-delimited text file). ■ Several string-manipulation techniques, including split(), concatenation, and extracting a portion of the string using substring() and substr(), are used to capitalize the first letter of each word in the image titles. ■ The getCharAt() method is used to get a single character from a string (to determine the ASCII character corresponding to a grayscale bitmap value). ■ String concatenation is used to build up the ASCII art representation of an image one character at a time. The term ASCII art refers to a text representations of an image, in which a grid of monospaced font characters, such as Courier New characters, plots the image. The following image shows an example of ASCII art produced by the application: The ASCII art version of the graphic is shown on the right.
  • 217. Example: ASCII art 217 To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The ASCIIArt application files can be found in the folder Samples/AsciiArt. The application consists of the following files: Extracting tab-delimited values This example uses the common practice of storing application data separate from the application itself; that way, if the data changes (for example, if another image is added or an image’s title changes), there is no need to recreate the SWF file. In this case, the image metadata, including the image title, the URL of the actual image file, and some values that are used to manipulate the image, are stored in a text file (the txt/ImageData.txt file in the project). The contents of the text file are as follows: FILENAMETITLEWHITE_THRESHHOLDBLACK_THRESHHOLD FruitBasket.jpgPear, apple, orange, and bananad810 Banana.jpgA picture of a bananaC820 Orange.jpgorangeFF20 Apple.jpgpicture of an apple6E10 File Description AsciiArtApp.mxml or AsciiArtApp.fla The main application file in Flash (FLA) or Flex (MXML) com/example/programmingas3/asciiArt/ AsciiArtBuilder.as The class that provides the main functionality of the application, including extracting image metadata from a text file, loading the images, and managing the image-to-text conversion process. com/example/programmingas3/asciiArt/ BitmapToAsciiConverter.as A class that provides the parseBitmapData() method for converting image data into a String version. com/example/programmingas3/asciiArt/ Image.as A class which represents a loaded bitmap image. com/example/programmingas3/asciiArt/ ImageInfo.as A class representing metadata for an ASCII art image (such as title, image file URL, and so on). image/ A folder containing images used by the application. txt/ImageData.txt A tab-delimited text file, containing information on the images to be loaded by the application.
  • 218. 218 Working with strings The file uses a specific tab-delimited format. The first line (row) is a heading row. The remaining lines contain the following data for each bitmap to be loaded: ■ The filename of the bitmap. ■ The display name of the bitmap. ■ The white-threshold and black-threshold values for the bitmaps. These are hex values above which and below which a pixel is to be considered completely white or completely black. As soon as the application starts, the AsciiArtBuilder class loads and parses the contents of the text file in order to create the “stack” of images that it will display, using the following code from the AsciiArtBuilder class’s parseImageInfo() method: var lines:Array = _imageInfoLoader.data.split("n"); var numLines:uint = lines.length; for (var i:uint = 1; i < numLines; i++) { var imageInfoRaw:String = lines[i]; ... if (imageInfoRaw.length > 0) { // Create a new image info record and add it to the array of image info. var imageInfo:ImageInfo = new ImageInfo(); // Split the current line into values (separated by tab (t) // characters) and extract the individual properties: var imageProperties:Array = imageInfoRaw.split("t"); imageInfo.fileName = imageProperties[0]; imageInfo.title = normalizeTitle(imageProperties[1]); imageInfo.whiteThreshold = parseInt(imageProperties[2], 16); imageInfo.blackThreshold = parseInt(imageProperties[3], 16); result.push(imageInfo); } } The entire contents of the text file are contained in a single String instance, the _imageInfoLoader.data property. Using the split() method with the newline character ("n") as a parameter, the String instance is divided into an Array (lines) whose elements are the individual lines of the text file. Next, the code uses a loop to work with each of the lines (except the first, because it contains only headers rather than actual content). Inside the loop, the split() method is used once again to divide the contents of the single line into a set of values (the Array object named imageProperties). The parameter used with the split() method in this case is the tab ("t") character, because the values in each line are delineated by tab characters.
  • 219. Example: ASCII art 219 Using String methods to normalize image titles One of the design decisions for this application is that all the image titles are displayed using a standard format, with the first letter of each word capitalized (except for a few words that are commonly not capitalized in English titles). Rather than assume that the text file contains properly formatted titles, the application formats the titles while they’re being extracted from the text file. In the previous code listing, as part of extracting individual image metadata values, the following line of code is used: imageInfo.title = normalizeTitle(imageProperties[1]); In that code, the image’s title from the text file is passed through the normalizeTitle() method before it is stored in the ImageInfo object: private function normalizeTitle(title:String):String { var words:Array = title.split(" "); var len:uint = words.length; for (var i:uint; i < len; i++) { words[i] = capitalizeFirstLetter(words[i]); } return words.join(" "); } This method uses the split() method to divide the title into individual words (separated by the space character), passes each word through the capitalizeFirstLetter() method, and then uses the Array class’s join() method to combine the words back into a single string again. As its name suggests, the capitalizeFirstLetter() method actually does the work of capitalizing the first letter of each word: /** * Capitalizes the first letter of a single word, unless it's one of * a set of words that are normally not capitalized in English. */ private function capitalizeFirstLetter(word:String):String { switch (word) {
  • 220. 220 Working with strings case "and": case "the": case "in": case "an": case "or": case "at": case "of": case "a": // Don't do anything to these words. break; default: // For any other word, capitalize the first character. var firstLetter:String = word.substr(0, 1); firstLetter = firstLetter.toUpperCase(); var otherLetters:String = word.substring(1); word = firstLetter + otherLetters; } return word; } In English, the initial character of each word in a title is not capitalized if it is one of the following words: “and,” “the,” “in,” “an,” “or,” “at,” “of,” or “a.” (This is a simplified version of the rules.) To execute this logic, the code first uses a switch statement to check if the word is one of the words that should not be capitalized. If so, the code simply jumps out of the switch statement. On the other hand, if the word should be capitalized, that is done in several steps, as follows: 1. The first letter of the word is extracted using substr(0, 1), which extracts a substring starting with the character at index 0 (the first letter in the string, as indicated by the first parameter 0). The substring will be one character in length (indicated by the second parameter 1). 2. That character is capitalized using the toUpperCase() method. 3. The remaining characters of the original word are extracted using substring(1), which extracts a substring starting at index 1 (the second letter) through the end of the string (indicated by leaving off the second parameter of the substring() method). 4. The final word is created by combining the newly capitalized first letter with the remaining letters using string concatenation: firstLetter + otherLetters.
  • 221. Example: ASCII art 221 Generating the ASCII art text The BitmapToAsciiConverter class provides the functionality of converting a bitmap image to its ASCII text representation. This process is performed by the parseBitmapData() method, which is partially shown here: var result:String = ""; // Loop through the rows of pixels top to bottom: for (var y:uint = 0; y < _data.height; y += verticalResolution) { // Within each row, loop through pixels left to right: for (var x:uint = 0; x < _data.width; x += horizontalResolution) { ... // Convert the gray value in the 0-255 range to a value // in the 0-64 range (since that's the number of "shades of // gray" in the set of available characters): index = Math.floor(grayVal / 4); result += palette.charAt(index); } result += "n"; } return result; This code first defines a String instance named result that will be used to build up the ASCII art version of the bitmap image. Next, it loops through individual pixels of the source bitmap image. Using several color-manipulation techniques (omitted here for brevity), it converts the red, green, and blue color values of an individual pixel to a single grayscale value (a number from 0 to 255). The code then divides that value by 4 (as shown) to convert it to a value in the 0-63 scale, which is stored in the variable index. (The 0-63 scale is used because the “palette” of available ASCII characters used by this application contains 64 values.) The palette of characters is defined as a String instance in the BitmapToAsciiConverter class: // The characters are in order from darkest to lightest, so that their // position (index) in the string corresponds to a relative color value // (0 = black). private static const palette:String = "@#$%&8BMW*mwqpdbkhaoQ0OZXYUJCLtfjzxnuvcr[]{}1()|/?Il!i><+_~-;,. "; Since the index variable defines which ASCII character in the palette corresponds to the current pixel in the bitmap image, that character is retrieved from the palette String using the charAt() method. It is then appended to the result String instance using the concatenation assignment operator (+=). In addition, at the end of each row of pixels, a newline character is concatenated to the end of the result String, forcing the line to wrap to create a new row of character “pixels.”
  • 222. 222 Working with strings
  • 223. 223 7 CHAPTER 7 Working with arrays Arrays allow you to store multiple values in a single data structure. You can use simple indexed arrays that store values using fixed ordinal integer indexes or complex associative arrays that store values using arbitrary keys. Arrays can also be multidimensional, containing elements that are themselves arrays. This chapter discusses how to create and manipulate various types of arrays. Contents Basics of arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .223 Indexed arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .225 Associative arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .234 Multidimensional arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .239 Cloning arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 Example: PlayList. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .247 Basics of arrays Introduction to working with arrays Often in programming you’ll need to work with a set of items rather than a single object; for example, in a music player application, you might want to have a list of songs waiting to be played. You wouldn’t want to have to create a separate variable for each song on that list—it would be preferable to have all the Song objects together in a bundle, and be able to work with them as a group.
  • 224. 224 Working with arrays An array is a programming element that acts as a container for a set of items, such as a list of songs. Most commonly all the items in an array will be instances of the same class, but that is not a requirement in ActionScript. The individual items in an array are known as the array’s elements. You might think of an array as a file drawer for variables. Variables can be added as elements in the array, which is like putting a folder into the file drawer. Once several files are in the drawer, you can work with the array as a single variable (like carrying the whole drawer to a different location); you can work with the variables as a group (like flipping through the folders one by one searching for a piece of information); or you can access them individually (like opening the drawer and selecting a single folder). For example, imagine you’re creating a music player application where a user can select multiple songs and add them to a playlist. In your ActionScript code, you might have a method named addSongsToPlaylist(), which accepts a single array as a parameter. No matter how many songs are to be added to the list (a few, a lot, or even only one), you have to call the addSongsToPlaylist() method only one time, passing it the array containing the Song objects. Inside the addSongsToPlaylist() method, you can use a loop to go through the array’s elements (the songs) one by one and actually add them to the playlist. The most common type of ActionScript array is an indexed array, which is an array where each item is stored in a numbered slot (known as an index), and items are accessed using the number, like an address. The Array class is used to represent an indexed array. Indexed arrays work well for most programming needs. A special use of an indexed array is a multidimensional array, which is an indexed array whose elements are indexed arrays (which in turn contain other elements). Another type of array is an associative array, which uses a string key instead of a numeric index to identify individual elements. Finally, for advanced users, ActionScript 3.0 also includes the Dictionary class, which represents a dictionary—an array that allows you to use any type of object as a key to distinguish between elements. Common array tasks The following common activities for working with arrays are described in this chapter: ■ Creating indexed arrays ■ Adding and removing array elements ■ Sorting array elements ■ Extracting portions of an array ■ Working with associative arrays and dictionaries ■ Working with multidimensional arrays ■ Copying array elements ■ Creating an array subclass
  • 225. Indexed arrays 225 Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Array: An object that serves as a container to group multiple objects together. ■ Associative array: An array that uses string keys to identify individual elements. ■ Dictionary: An array whose items consist of pairs of objects, known as the key and the value. The key is used instead of a numeric index to identify a single element. ■ Element: A single item in an array. ■ Index: The numeric “address” used to identify a single element in an indexed array. ■ Indexed array: The standard type of array that stores each element in a numbered element, and uses the number (index) to identify individual elements. ■ Key: The string or object used to identify a single element in an associative array or a dictionary. ■ Multidimensional array: An array containing items that are arrays rather than single values. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Essentially all the code listings in this chapter include the appropriate trace() function call. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the trace() function in the Output panel. This and other techniques for testing example code listings are described in detail in “Testing in-chapter example code listings” on page 63. Indexed arrays Indexed arrays store a series of one or more values organized such that each value can be accessed using an unsigned integer value. The first index is always the number 0, and the index increments by 1 for each subsequent element added to the array. As the following code shows, you can create an indexed array by calling the Array class constructor or by initializing the array with an array literal:
  • 226. 226 Working with arrays // Use Array constructor. var myArray:Array = new Array(); myArray.push("one"); myArray.push("two"); myArray.push("three"); trace(myArray); // output: one,two,three // Use Array literal. var myArray:Array = ["one", "two", "three"]; trace(myArray); // output: one,two,three The Array class also contains properties and methods that allow you to modify indexed arrays. These properties and methods apply almost exclusively to indexed arrays rather than associative arrays. Indexed arrays use an unsigned 32-bit integer for the index number. The maximum size of an indexed array is 232 - 1 or 4,294,967,295. An attempt to create an array that is larger than the maximum size results in a run-time error. An array element can hold a value of any data type. ActionScript 3.0 does not support the concept of typed arrays, which means that you cannot specify that all the elements of an array belong to a specific data type. This section explains how to create and modify indexed arrays using the Array class, starting with how to create an array. The methods that modify arrays are grouped into three categories that cover how to insert elements, remove elements, and sort arrays. Methods in a final group treat an existing array as read-only; these methods merely query arrays. Rather than modifying an existing array, the query methods all return a new array. The section concludes with a discussion about extending the Array class. Creating arrays The Array constructor function can be used in three ways. First, if you call the constructor with no arguments, you get an empty array. You can use the length property of the Array class to verify that the array has no elements. For example, the following code calls the Array constructor with no arguments: var names:Array = new Array(); trace(names.length); // output: 0 Second, if you use a number as the only parameter to the Array constructor, an array of that length is created, with each element’s value set to undefined. The argument must be an unsigned integer between the values 0 and 4,294,967,295. For example, the following code calls the Array constructor with a single numeric argument:
  • 227. Indexed arrays 227 var names:Array = new Array(3); trace(names.length); // output: 3 trace(names[0]); // output: undefined trace(names[1]); // output: undefined trace(names[2]); // output: undefined Third, if you call the constructor and pass a list of elements as parameters, an array is created, with elements corresponding to each of the parameters. The following code passes three arguments to the Array constructor: var names:Array = new Array("John", "Jane", "David"); trace(names.length); // output: 3 trace(names[0]); // output: John trace(names[1]); // output: Jane trace(names[2]); // output: David You can also create arrays with array literals or object literals. An array literal can be assigned directly to an array variable, as shown in the following example: var names:Array = ["John", "Jane", "David"]; Inserting array elements Three of the Array class methods—push(), unshift(), and splice()—allow you to insert elements into an array. The push() method appends one or more elements to the end of an array. In other words, the last element inserted into the array using the push() method will have the highest index number. The unshift() method inserts one or more elements at the beginning of an array, which is always at index number 0. The splice() method will insert any number of items at a specified index in the array. The following example demonstrates all three methods. An array named planets is created to store the names of the planets in order of proximity to the Sun. First, the push() method is called to add the initial item, Mars. Second, the unshift() method is called to insert the item that belongs at the front of the array, Mercury. Finally, the splice() method is called to insert the items Venus and Earth after Mercury, but before Mars. The first argument sent to splice(), the integer 1, directs the insertion to begin at index 1. The second argument sent to splice(), the integer 0, indicates that no items should be deleted. Finally, the third and fourth arguments sent to splice(), Venus and Earth, are the items to be inserted. var planets:Array = new Array(); planets.push("Mars"); // array contents: Mars planets.unshift("Mercury"); // array contents: Mercury,Mars planets.splice(1, 0, "Venus", "Earth"); trace(planets); // array contents: Mercury,Venus,Earth,Mars
  • 228. 228 Working with arrays The push() and unshift() methods both return an unsigned integer that represents the length of the modified array. The splice() method returns an empty array when used to insert elements, which may seem strange, but makes more sense in light of the splice() method’s versatility. You can use the splice() method not only to insert elements into an array, but also to remove elements from an array. When used to remove elements, the splice() method returns an array containing the elements removed. Removing array elements Three methods of the Array class—pop(), shift(), and splice()—allow you to remove elements from an array. The pop() method removes an element from the end of the array. In other words, it removes the element at the highest index number. The shift() method removes an element from the beginning of the array, which means that it always removes the element at index number 0. The splice() method, which can also be used to insert elements, removes an arbitrary number of elements starting at the index number specified by the first argument sent to the method. The following example uses all three methods to remove elements from an array. An array named oceans is created to store the names of large bodies of water. Some of the names in the array are lakes rather than oceans, so they need to be removed. First, the splice() method is used to remove the items Aral and Superior, and insert the items Atlantic and Indian. The first argument sent to splice(), the integer 2, indicates that the operation should start with the third item in the list, which is at index 2. The second argument, 2, indicates that two items should be removed. The remaining arguments, Atlantic and Indian, are values to be inserted at index 2. Second, the pop() method is used to remove last element in the array, Huron. And third, the shift() method is used to remove the first item in the array, Victoria. var oceans:Array = ["Victoria", "Pacific", "Aral", "Superior", "Indian", "Huron"]; oceans.splice(2, 2, "Arctic", "Atlantic"); // replaces Aral and Superior oceans.pop(); // removes Huron oceans.shift(); // removes Victoria trace(oceans); // output: Pacific,Arctic,Atlantic,Indian The pop() and shift() methods both return the item that was removed. The data type of the return value is Object because arrays can hold values of any data type. The splice() method returns an array containing the values removed. You can change the oceans array example so that the call to splice() assigns the array to a new array variable, as shown in the following example: var lakes:Array = oceans.splice(2, 2, "Arctic", "Atlantic"); trace(lakes); // output: Aral,Superior
  • 229. Indexed arrays 229 You may come across code that uses the delete operator on an array element. The delete operator sets the value of an array element to undefined, but it does not remove the element from the array. For example, the following code uses the delete operator on the third element in the oceans array, but the length of the array remains 5: var oceans:Array = ["Arctic", "Pacific", "Victoria", "Indian", "Atlantic"]; delete oceans[2]; trace(oceans); // output: Arctic,Pacific,,Indian,Atlantic trace(oceans[2]); // output: undefined trace(oceans.length); // output: 5 You can truncate an array using an array’s length property. If you set the length property of an array to a length that is less than the current length of the array, the array is truncated, removing any elements stored at index numbers higher than the new value of length minus 1. For example, if the oceans array were sorted such that all valid entries were at the beginning of the array, you could use the length property to remove the entries at the end of the array, as shown in the following code: var oceans:Array = ["Arctic", "Pacific", "Victoria", "Aral", "Superior"]; oceans.length = 2; trace(oceans); // output: Arctic,Pacific Sorting an array There are three methods—reverse(), sort(), and sortOn()—that allow you to change the order of an array, either by sorting or reversing the order. All of these methods modify the existing array. The reverse() method changes the order of the array such that the last element becomes the first element, the penultimate element becomes the second element, and so on. The sort() method allows you to sort an array in a variety of predefined ways, and even allows you to create custom sorting algorithms. The sortOn() method allows you to sort an indexed array of objects that have one or more common properties that can be used as sort keys. The reverse() method takes no parameters and does not return a value, but allows you to toggle the order of your array from its current state to the reverse order. The following example reverses the order of the oceans listed in the oceans array: var oceans:Array = ["Arctic", "Atlantic", "Indian", "Pacific"]; oceans.reverse(); trace(oceans); // output: Pacific,Indian,Atlantic,Arctic
  • 230. 230 Working with arrays The sort() method rearranges the elements in an array using the default sort order. The default sort order has the following characteristics: ■ The sort is case-sensitive, which means that uppercase characters precede lowercase characters. For example, the letter D precedes the letter b. ■ The sort is ascending, which means that lower character codes (such as A) precede higher character codes (such as B). ■ The sort places identical values adjacent to each other but in no particular order. ■ The sort is string-based, which means that elements are converted to strings before they are compared (for example, 10 precedes 3 because the string "1" has a lower character code than the string "3" has). You may find that you need to sort your array without regard to case, or in descending order, or perhaps your array contains numbers that you want to sort numerically instead of alphabetically. The sort() method has an options parameter that allows you to alter each characteristic of the default sort order. The options are defined by a set of static constants in the Array class, as shown in the following list: ■ Array.CASEINSENSITIVE: This option makes the sort disregard case. For example, the lowercase letter b precedes the uppercase letter D. ■ Array.DESCENDING: This reverses the default ascending sort. For example, the letter B precedes the letter A. ■ Array.UNIQUESORT: This causes the sort to abort if two identical values are found. ■ Array.NUMERIC: This causes numerical sorting, so that 3 precedes 10. The following example highlights some of these options. An array named poets is created that is sorted using several different options. var poets:Array = ["Blake", "cummings", "Angelou", "Dante"]; poets.sort(); // default sort trace(poets); // output: Angelou,Blake,Dante,cummings poets.sort(Array.CASEINSENSITIVE); trace(poets); // output: Angelou,Blake,cummings,Dante poets.sort(Array.DESCENDING); trace(poets); // output: cummings,Dante,Blake,Angelou poets.sort(Array.DESCENDING | Array.CASEINSENSITIVE); // use two options trace(poets); // output: Dante,cummings,Blake,Angelou
  • 231. Indexed arrays 231 You can also write your own custom sort function, which you can pass as a parameter to the sort() method. For example, if you have a list of names in which each list element contains a person’s full name, but you want to sort the list by last name, you must use a custom sort function to parse each element and use the last name in the sort function. The following code shows how this can be done with a custom function that is used as a parameter to the Array.sort() method: var names:Array = new Array("John Q. Smith", "Jane Doe", "Mike Jones"); function orderLastName(a, b):int { var lastName:RegExp = /bS+$/; var name1 = a.match(lastName); var name2 = b.match(lastName); if (name1 < name2) { return -1; } else if (name1 > name2) { return 1; } else { return 0; } } trace(names); // output: John Q. Smith,Jane Doe,Mike Jones names.sort(orderLastName); trace(names); // output: Jane Doe,Mike Jones,John Q. Smith The custom sort function orderLastName() uses a regular expression to extract the last name from each element to use for the comparison operation. The function identifier orderLastName is used as the sole parameter when calling the sort() method on the names array. The sort function accepts two parameters, a and b, because it works on two array elements at a time. The sort function’s return value indicates how the elements should be sorted: ■ A return value of -1 indicates that the first parameter, a, precedes the second parameter, b. ■ A return value of 1 indicates that the second parameter, b, precedes the first, a. ■ A return value of 0 indicates that the elements have equal sorting precedence. The sortOn() method is designed for indexed arrays with elements that contain objects. These objects are expected to have at least one common property that can be used as the sort key. The use of the sortOn() method for arrays of any other type yields unexpected results. The following example revises the poets array so that each element is an object instead of a string. Each object holds both the poet’s last name and year of birth.
  • 232. 232 Working with arrays var poets:Array = new Array(); poets.push({name:"Angelou", born:"1928"}); poets.push({name:"Blake", born:"1757"}); poets.push({name:"cummings", born:"1894"}); poets.push({name:"Dante", born:"1265"}); poets.push({name:"Wang", born:"701"}); You can use the sortOn() method to sort the array by the born property. The sortOn() method defines two parameters, fieldName and options. The fieldName argument must be specified as a string. In the following example, sortOn() is called with two arguments, "born" and Array.NUMERIC. The Array.NUMERIC argument is used to ensure that the sort is done numerically instead of alphabetically. This is a good practice even when all the numbers have the same number of digits because it ensures that the sort will continue to behave as expected if a number with fewer or more digits is later added to the array. poets.sortOn("born", Array.NUMERIC); for (var i:int = 0; i < poets.length; ++i) { trace(poets[i].name, poets[i].born); } /* output: Wang 701 Dante 1265 Blake 1757 cummings 1894 Angelou 1928 */ Generally, the sort() and sortOn() methods modify an array. If you wish to sort an array without modifying the existing array, pass the Array.RETURNINDEXEDARRAY constant as part of the options parameter. This option directs the methods to return a new array that reflects the sort and to leave the original array unmodified. The array returned by the methods is a simple array of index numbers that reflects the new sort order and does not contain any elements from the original array. For example, to sort the poets array by birth year without modifying the array, include the Array.RETURNINDEXEDARRAY constant as part of the argument passed for the options parameter. The following example stores the returned index information in an array named indices and uses the indices array in conjunction with the unmodified poets array to output the poets in order of birth year: var indices:Array; indices = poets.sortOn("born", Array.NUMERIC | Array.RETURNINDEXEDARRAY); for (var i:int = 0; i < indices.length; ++i) { var index:int = indices[i]; trace(poets[index].name, poets[index].born); }
  • 233. Indexed arrays 233 /* output: Wang 701 Dante 1265 Blake 1757 cummings 1894 Angelou 1928 */ Querying an array The remaining four methods of the Array class—concat(), join(), slice(), toString()—all query the array for information, but do not modify the array. The concat() and slice() methods both return new arrays, while the join() and toString() methods both return strings. The concat() method takes a new array or list of elements as arguments and combines it with the existing array to create a new array. The slice() method has two parameters, aptly named startIndex and an endIndex, and returns a new array containing a copy of the elements “sliced” from the existing array. The slice begins with the element at startIndex and ends with the element just before endIndex. That bears repeating: the element at endIndex is not included in the return value. The following example uses concat() and slice() to create new arrays using elements of other arrays: var array1:Array = ["alpha", "beta"]; var array2:Array = array1.concat("gamma", "delta"); trace(array2); // output: alpha,beta,gamma,delta var array3:Array = array1.concat(array2); trace(array3); // output: alpha,beta,alpha,beta,gamma,delta var array4:Array = array3.slice(2,5); trace(array4); // output: alpha,beta,gamma You can use the join() and toString() methods to query the array and return its contents as a string. If no parameters are used for the join() method, the two methods behave identically—they return a string containing a comma-delimited list of all elements in the array. The join() method, unlike the toString() method, accepts a parameter named delimiter, which allows you to choose the symbol to use as a separator between each element in the returned string. The following example creates an array called rivers and calls both join() and toString() to return the values in the array as a string. The toString() method is used to return comma-separated values (riverCSV), while the join() method is used to return values separated by the + character.
  • 234. 234 Working with arrays var rivers:Array = ["Nile", "Amazon", "Yangtze", "Mississippi"]; var riverCSV:String = rivers.toString(); trace(riverCSV); // output: Nile,Amazon,Yangtze,Mississippi var riverPSV:String = rivers.join("+"); trace(riverPSV); // output: Nile+Amazon+Yangtze+Mississippi One issue to be aware of with the join() method is that any nested arrays are always returned with comma-separated values, no matter what separator you specify for the main array elements, as the following example shows: var nested:Array = ["b","c","d"]; var letters:Array = ["a",nested,"e"]; var joined:String = letters.join("+"); trace(joined); // output: a+b,c,d+e Associative arrays An associative array, sometimes called a hash or map, uses keys instead of a numeric index to organize stored values. Each key in an associative array is a unique string that is used to access a stored value. An associative array is an instance of the Object class, which means that each key corresponds to a property name. Associative arrays are unordered collections of key and value pairs. Your code should not expect the keys of an associative array to be in a specific order. ActionScript 3.0 introduces an advanced type of associative array called a dictionary. Dictionaries, which are instances of the Dictionary class in the flash.utils package, use keys that can be of any data type but are usually instances of the Object class. In other words, dictionary keys are not limited to values of type String. This section describes how to create associative arrays that use strings for keys and how to use the Dictionary class. Associative arrays with string keys There are two ways to create associative arrays in ActionScript 3.0. The first way is to use the Object constructor, which has the advantage of allowing you to initialize your array with an object literal. An instance of the Object class, also called a generic object, is functionally identical to an associative array. Each property name of the generic object serves as the key that provides access to a stored value.
  • 235. Associative arrays 235 The following example creates an associative array named monitorInfo, using an object literal to initialize the array with two key and value pairs: var monitorInfo:Object = {type:"Flat Panel", resolution:"1600 x 1200"}; trace(monitorInfo["type"], monitorInfo["resolution"]); // output: Flat Panel 1600 x 1200 If you do not need to initialize the array at declaration time, you can use the Object constructor to create the array, as follows: var monitorInfo:Object = new Object(); After the array is created using either an object literal or the Object class constructor, you can add new values to the array using either the bracket operator ([]) or the dot operator (.). The following example adds two new values to monitorArray: monitorInfo["aspect ratio"] = "16:10"; // bad form, do not use spaces monitorInfo.colors = "16.7 million"; trace(monitorInfo["aspect ratio"], monitorInfo.colors); // output: 16:10 16.7 million Note that the key named aspect ratio contains a space character. This is possible with the bracket operator, but generates an error if attempted with the dot operator. Using spaces in your key names is not recommended. The second way to create an associative array is to use the Array constructor (or the constructor of any dynamic class) and then use either the bracket operator ([]) or the dot operator (.) to add key and value pairs to the array. If you declare your associative array to be of type Array, you cannot use an object literal to initialize the array. The following example creates an associative array named monitorInfo using the Array constructor and adds a key called type and a key called resolution, along with their values: var monitorInfo:Array = new Array(); monitorInfo["type"] = "Flat Panel"; monitorInfo["resolution"] = "1600 x 1200"; trace(monitorInfo["type"], monitorInfo["resolution"]); // output: Flat Panel 1600 x 1200 There is no advantage in using the Array constructor to create an associative array. You cannot use the Array.length property or any of the methods of the Array class with associative arrays, even if you use the Array constructor or the Array data type. The use of the Array constructor is best left for the creation of indexed arrays.
  • 236. 236 Working with arrays Associative arrays with object keys You can use the Dictionary class to create an associative array that uses objects for keys rather than strings. Such arrays are sometimes called dictionaries, hashes, or maps. For example, consider an application that determines the location of a Sprite object based on its association with a specific container. You can use a Dictionary object to map each Sprite object to a container. The following code creates three instances of the Sprite class that serve as keys for the Dictionary object. Each key is assigned a value of either GroupA or GroupB. The values can be of any data type, but in this example both GroupA and GroupB are instances of the Object class. Subsequently, you can access the value associated with each key with the property access ([]) operator, as shown in the following code: import flash.display.Sprite; import flash.utils.Dictionary; var groupMap:Dictionary = new Dictionary(); // objects to use as keys var spr1:Sprite = new Sprite(); var spr2:Sprite = new Sprite(); var spr3:Sprite = new Sprite(); // objects to use as values var groupA:Object = new Object(); var groupB:Object = new Object(); // Create new key-value pairs in dictionary. groupMap[spr1] = groupA; groupMap[spr2] = groupB; groupMap[spr3] = groupB; if (groupMap[spr1] == groupA) { trace("spr1 is in groupA"); } if (groupMap[spr2] == groupB) { trace("spr2 is in groupB"); } if (groupMap[spr3] == groupB) { trace("spr3 is in groupB"); }
  • 237. Associative arrays 237 Iterating with object keys You can iterate through the contents of a Dictionary object with either a for..in loop or a for each..in loop. A for..in loop allows you to iterate based on the keys, whereas a for each..in loop allows you to iterate based on the values associated with each key. Use the for..in loop for direct access to the object keys of a Dictionary object. You can also access the values of the Dictionary object with the property access ([]) operator. The following code uses the previous example of the groupMap dictionary to show how to iterate through a Dictionary object with the for..in loop: for (var key:Object in groupMap) { trace(key, groupMap[key]); } /* output: [object Sprite] [object Object] [object Sprite] [object Object] [object Sprite] [object Object] */ Use the for each..in loop for direct access to the values of a Dictionary object. The following code also uses the groupMap dictionary to show how to iterate through a Dictionary object with the for each..in loop: for each (var item:Object in groupMap) { trace(item); } /* output: [object Object] [object Object] [object Object] */ Object keys and memory management Adobe Flash Player uses a garbage collection system to recover memory that is no longer used. When an object has no references pointing to it, the object becomes eligible for garbage collection, and the memory is recovered the next time the garbage collection system executes. For example, the following code creates a new object and assigns a reference to the object to the variable myObject: var myObject:Object = new Object();
  • 238. 238 Working with arrays As long as any reference to the object exists, the garbage collection system will not recover the memory that the object occupies. If the value of myObject is changed such that it points to a different object or is set to the value null, the memory occupied by the original object becomes eligible for garbage collection, but only if there are no other references to the original object. If you use myObject as a key in a Dictionary object, you are creating another reference to the original object. For example, the following code creates two references to an object—the myObject variable, and the key in the myMap object: import flash.utils.Dictionary; var myObject:Object = new Object(); var myMap:Dictionary = new Dictionary(); myMap[myObject] = "foo"; To make the object referenced by myObject eligible for garbage collection, you must remove all references to it. In this case, you must change the value of myObject and delete the myObject key from myMap, as shown in the following code: myObject = null; delete myMap[myObject]; Alternatively, you can use the useWeakReference parameter of the Dictionary constructor to make all of the dictionary keys weak references. The garbage collection system ignores weak references, which means that an object that has only weak references is eligible for garbage collection. For example, in the following code, you do not need to delete the myObject key from myMap in order to make the object eligible for garbage collection: import flash.utils.Dictionary; var myObject:Object = new Object(); var myMap:Dictionary = new Dictionary(true); myMap[myObject] = "foo"; myObject = null; // Make object eligible for garbage collection.
  • 239. Multidimensional arrays 239 Multidimensional arrays Multidimensional arrays contain other arrays as elements. For example, consider a list of tasks that is stored as an indexed array of strings: var tasks:Array = ["wash dishes", "take out trash"]; If you want to store a separate list of tasks for each day of the week, you can create a multidimensional array with one element for each day of the week. Each element contains an indexed array, similar to the tasks array, that stores the list of tasks. You can use any combination of indexed or associative arrays in multidimensional arrays. The examples in the following sections use either two indexed arrays or an associative array of indexed arrays. You might want to try the other combinations as exercises. Two indexed arrays When you use two indexed arrays, you can visualize the result as a table or spreadsheet. The elements of the first array represent the rows of the table, while the elements of the second array represent the columns. For example, the following multidimensional array uses two indexed arrays to track task lists for each day of the week. The first array, masterTaskList, is created using the Array class constructor. Each element of the array represents a day of the week, with index 0 representing Monday, and index 6 representing Sunday. These elements can be thought of as the rows in the table. You can create each day’s task list by assigning an array literal to each of the seven elements that you create in the masterTaskList array. The array literals represent the columns in the table. var masterTaskList:Array = new Array(); masterTaskList[0] = ["wash dishes", "take out trash"]; masterTaskList[1] = ["wash dishes", "pay bills"]; masterTaskList[2] = ["wash dishes", "dentist", "wash dog"]; masterTaskList[3] = ["wash dishes"]; masterTaskList[4] = ["wash dishes", "clean house"]; masterTaskList[5] = ["wash dishes", "wash car", "pay rent"]; masterTaskList[6] = ["mow lawn", "fix chair"]; You can access individual items on any of the task lists using bracket notation. The first set of brackets represents the day of the week, and the second set of brackets represents the task list for that day. For example, to retrieve the second task from Wednesday’s list, first use index 2 for Wednesday, and then use index 1 for the second task in the list. trace(masterTaskList[2][1]); // output: dentist
  • 240. 240 Working with arrays To retrieve the first task from Sunday’s list, use index 6 for Sunday and index 0 for the first task on the list. trace(masterTaskList[6][0]); // output: mow lawn Associative array with an indexed array To make the individual arrays easier to access, you can use an associative array for the days of the week and an indexed array for the task lists. Using an associative array allows you to use dot syntax when referring to a particular day of the week, but at the cost of extra run-time processing to access each element of the associative array. The following example uses an associative array as the basis of a task list, with a key and value pair for each day of the week: var masterTaskList:Object = new Object(); masterTaskList["Monday"] = ["wash dishes", "take out trash"]; masterTaskList["Tuesday"] = ["wash dishes", "pay bills"]; masterTaskList["Wednesday"] = ["wash dishes", "dentist", "wash dog"]; masterTaskList["Thursday"] = ["wash dishes"]; masterTaskList["Friday"] = ["wash dishes", "clean house"]; masterTaskList["Saturday"] = ["wash dishes", "wash car", "pay rent"]; masterTaskList["Sunday"] = ["mow lawn", "fix chair"]; Dot syntax makes the code more readable by making it possible to avoid multiple sets of brackets. trace(masterTaskList.Wednesday[1]); // output: dentist trace(masterTaskList.Sunday[0]); // output: mow lawn You can iterate through the task list using a for..in loop, but you must use bracket notation instead of dot syntax to access the value associated with each key. Because masterTaskList is an associative array, the elements are not necessarily retrieved in the order that you may expect, as the following example shows: for (var day:String in masterTaskList) { trace(day + ": " + masterTaskList[day]) } /* output: Sunday: mow lawn,fix chair Wednesday: wash dishes,dentist,wash dog Friday: wash dishes,clean house Thursday: wash dishes Monday: wash dishes,take out trash Saturday: wash dishes,wash car,pay rent Tuesday: wash dishes,pay bills */
  • 241. Advanced topics 241 Cloning arrays The Array class has no built-in method for making copies of arrays. You can create a shallow copy of an array by calling either the concat() or slice() methods with no arguments. In a shallow copy, if the original array has elements that are objects, only the references to the objects are copied rather than the objects themselves. The copy points to the same objects as the original does. Any changes made to the objects are reflected in both arrays. In a deep copy, any objects found in the original array are also copied so that the new array does not point to the same objects as does the original array. Deep copying requires more than one line of code, which usually calls for the creation of a function. Such a function could be created as a global utility function or as a method of an Array subclass. The following example defines a function named clone() that does deep copying. The algorithm is borrowed from a common Java programming technique. The function creates a deep copy by serializing the array into an instance of the ByteArray class, and then reading the array back into a new array. This function accepts an object so that it can be used with both indexed arrays and associative arrays, as shown in the following code: import flash.utils.ByteArray; function clone(source:Object):* { var myBA:ByteArray = new ByteArray(); myBA.writeObject(source); myBA.position = 0; return(myBA.readObject()); } Advanced topics Extending the Array class The Array class is one of the few core classes that is not final, which means that you can create your own subclass of Array. This section provides an example of how to create a subclass of Array and discusses some of the issues that can arise during the process.
  • 242. 242 Working with arrays As mentioned previously, arrays in ActionScript are not typed, but you can create a subclass of Array that accepts elements of only a specific data type. The example in the following sections defines an Array subclass named TypedArray that limits its elements to values of the data type specified in the first parameter. The TypedArray class is presented merely as an example of how to extend the Array class and may not be suitable for production purposes for several reasons. First, type checking occurs at run time rather than at compile time. Second, when a TypedArray method encounters a mismatch, the mismatch is ignored and no exception is thrown, although the methods can be easily modified to throw exceptions. Third, the class cannot prevent the use of the array access operator to insert values of any type into the array. Fourth, the coding style favors simplicity over performance optimization. Declaring the subclass Use the extends keyword to indicate that a class is a subclass of Array. A subclass of Array should use the dynamic attribute, just as the Array class does. Otherwise, your subclass will not function properly. The following code shows the definition of the TypedArray class, which contains a constant to hold the data type, a constructor method, and the four methods that are capable of adding elements to the array. The code for each method is omitted in this example, but is delineated and explained fully in the sections that follow: public dynamic class TypedArray extends Array { private const dataType:Class; public function TypedArray(...args) {} AS3 override function concat(...args):Array {} AS3 override function push(...args):uint {} AS3 override function splice(...args) {} AS3 override function unshift(...args):uint {} }
  • 243. Advanced topics 243 The four overridden methods all use the AS3 namespace instead of the public attribute because this example assumes that the compiler option -as3 is set to true and the compiler option -es is set to false. These are the default settings for Adobe Flex Builder 2 and for Adobe Flash CS3 Professional. For more information, see “The AS3 namespace” on page 179. TypedArray constructor The subclass constructor poses an interesting challenge because the constructor must accept a list of arguments of arbitrary length. The challenge is how to pass the arguments on to the superconstructor to create the array. If you pass the list of arguments as an array, the superconstructor considers it a single argument of type Array and the resulting array is always 1 element long. The traditional way to handle pass-through argument lists is to use the Function.apply() method, which takes an array of arguments as its second parameter but converts it to a list of arguments when executing the function. Unfortunately, the Function.apply() method cannot be used with constructors. The only option left is to recreate the logic of the Array constructor in the TypedArray constructor. The following code shows the algorithm used in the Array class constructor, which you can reuse in your Array subclass constructor: public dynamic class Array { public function Array(...args) { var n:uint = args.length if (n == 1 && (args[0] is Number)) { var dlen:Number = args[0]; var ulen:uint = dlen; if (ulen != dlen) { throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")"); } length = ulen; } else { length = n; for (var i:int=0; i < n; i++) TIP If you are an advanced developer who prefers to use prototype inheritance, you can make two minor changes to the TypedArray class to make it compile with the compiler option -es set to true. First, remove all occurrences of the override attribute and replace the AS3 namespace with the public attribute. Second, substitute Array.prototype for all four occurrences of super.
  • 244. 244 Working with arrays { this[i] = args[i] } } } } The TypedArray constructor shares most of the code from the Array constructor, with only four changes to the code. First, the parameter list includes a new required parameter of type Class that allows specification of the array’s data type. Second, the data type passed to the constructor is assigned to the dataType variable. Third, in the else statement, the value of the length property is assigned after the for loop so that length includes only arguments that are the proper type. Fourth, the body of the for loop uses the overridden version of the push() method so that only arguments of the correct data type are added to the array. The following example shows the TypedArray constructor function: public dynamic class TypedArray extends Array { private var dataType:Class; public function TypedArray(typeParam:Class, ...args) { dataType = typeParam; var n:uint = args.length if (n == 1 && (args[0] is Number)) { var dlen:Number = args[0]; var ulen:uint = dlen if (ulen != dlen) { throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")") } length = ulen; } else { for (var i:int=0; i < n; i++) { // type check done in push() this.push(args[i]) } length = this.length; } } }
  • 245. Advanced topics 245 TypedArray overridden methods The TypedArray class overrides the four methods of the Array class that are capable of adding elements to an array. In each case, the overridden method adds a type check that prevents the addition of elements that are not the correct data type. Subsequently, each method calls the superclass version of itself. The push() method iterates through the list of arguments with a for..in loop and does a type check on each argument. Any argument that is not the correct type is removed from the args array with the splice() method. After the for..in loop ends, the args array contains values only of type dataType. The superclass version of push() is then called with the updated args array, as the following code shows: AS3 override function push(...args):uint { for (var i:* in args) { if (!(args[i] is dataType)) { args.splice(i,1); } } return (super.push.apply(this, args)); } The concat() method creates a temporary TypedArray named passArgs to store the arguments that pass the type check. This allows the reuse of the type check code that exists in the push() method. A for..in loop iterates through the args array, and calls push() on each argument. Because passArgs is typed as TypedArray, the TypedArray version of push() is executed. The concat() method then calls its own superclass version, as the following code shows: AS3 override function concat(...args):Array { var passArgs:TypedArray = new TypedArray(dataType); for (var i:* in args) { // type check done in push() passArgs.push(args[i]); } return (super.concat.apply(this, passArgs)); }
  • 246. 246 Working with arrays The splice() method takes an arbitrary list of arguments, but the first two arguments always refer to an index number and the number of elements to delete. This is why the overridden splice() method does type checking only for args array elements in index positions 2 or higher. One point of interest in the code is that there appears to be a recursive call to splice() inside the for loop, but this is not a recursive call because args is of type Array rather than TypedArray, which means that the call to args.splice() is a call to the superclass version of the method. After the for..in loop concludes, the args array contains only values of the correct type in index positions 2 or higher, and splice() calls its own superclass version, as shown in the following code: AS3 override function splice(...args):* { if (args.length > 2) { for (var i:int=2; i< args.length; i++) { if (!(args[i] is dataType)) { args.splice(i,1); } } } return (super.splice.apply(this, args)); } The unshift() method, which adds elements to the beginning of an array, also accepts an arbitrary list of arguments. The overridden unshift() method uses an algorithm very similar to that used by the push() method, as shown in the following example code: AS3 override function unshift(...args):uint { for (var i:* in args) { if (!(args[i] is dataType)) { args.splice(i,1); } } return (super.unshift.apply(this, args)); } }
  • 247. Example: PlayList 247 Example: PlayList The PlayList example demonstrates techniques for working with arrays, in the context of a music playlist application that manages a list of songs. These techniques are: ■ Creating an indexed array ■ Adding items to an indexed array ■ Sorting an array of objects by different properties, using different sorting options ■ Converting an array to a character-delimited string To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The PlayList application files can be found in the Samples/PlayList folder. The application consists of the following files: PlayList class overview The PlayList class manages a set of Song objects. It has public methods with functionality for adding a song to the playlist (the addSong() method) and sorting the songs in the list (the sortList() method). In addition, the class includes a read-only accessor property, songList, which provides access to the actual set of songs in the playlist. Internally, the PlayList class keeps track of its songs using a private Array variable: public class PlayList { private var _songs:Array; private var _currentSort:SortProperty = null; private var _needToSort:Boolean = false; ... } File Description PlayList.mxml or PlayList.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/playlist/ Song.as A value object representing information about a single song. The items that are managed by the PlayList class are Song instances. com/example/programmingas3/playlist/ SortProperty.as A pseudo-enumeration whose available values represent the properties of the Song class by which a list of Song objects can be sorted.
  • 248. 248 Working with arrays In addition to the _songs Array variable, which is used by the PlayList class to keep track of its list of songs, two other private variables keep track of whether the list needs to be sorted (_needToSort) and which property the song list is sorted by at a given time (_currentSort). As with all objects, declaring an Array instance is only half the job of creating an Array. Before accessing an Array instance’s properties or methods, it must be instantiated, which is done in the PlayList class’s constructor. public function PlayList() { this._songs = new Array(); // Set the initial sorting. this.sortList(SortProperty.TITLE); } The first line of the constructor instantiates the _songs variable, so that it is ready to be used. In addition, the sortList() method is called to set the initial sort-by property. Adding a song to the list When a user enters a new song into the application, the code in the data entry form calls the PlayList class’s addSong() method. /** * Adds a song to the playlist. */ public function addSong(song:Song):void { this._songs.push(song); this._needToSort = true; } Inside addSong(), the _songs array’s push() method is called, adding the Song object that was passed to addSong() as a new element in that array. With the push() method, the new element is added to the end of the array, regardless of any sorting that might have been applied previously. This means that after the push() method has been called, the list of songs is likely to no longer be sorted correctly, so the _needToSort variable is set to true. In theory, the sortList() method could be called immediately, removing the need to keep track of whether the list is sorted or not at a given time. In practice, however, there is no need for the list of songs to be sorted until immediately before it is retrieved. By deferring the sorting operation, the application doesn’t perform sorting that is unnecessary if, for example, several songs are added to the list before it is retrieved.
  • 249. Example: PlayList 249 Sorting the list of songs Because the Song instances that are managed by the playlist are complex objects, users of the application may wish to sort the playlist according to different properties, such as song title or year of publication. In the PlayList application, the task of sorting the list of songs has three parts: identifying the property by which the list should be sorted, indicating what sorting options need to be used when sorting by that property, and performing the actual sort operation. Properties for sorting A Song object keeps track of several properties, including song title, artist, publication year, filename, and a user-selected set of genres in which the song belongs. Of these, only the first three are practical for sorting. As a matter of convenience for developers, the example includes the SortProperty class, which acts as an enumeration with values representing the properties available for sorting. public static const TITLE:SortProperty = new SortProperty("title"); public static const ARTIST:SortProperty = new SortProperty("artist"); public static const YEAR:SortProperty = new SortProperty("year"); The SortProperty class contain three constants, TITLE, ARTIST, and YEAR, each of which stores a String containing the actual name of the associated Song class property that can be used for sorting. Throughout the rest of the code, whenever a sort property is indicated, it is done using the enumeration member. For instance, in the PlayList constructor, the list is sorted initially by calling the sortList() method, as follows: // Set the initial sorting. this.sortList(SortProperty.TITLE); Because the property for sorting is specified as SortProperty.TITLE, the songs are sorted according to their title.
  • 250. 250 Working with arrays Sorting by property and specifying sort options The work of actually sorting the list of songs is performed by the PlayList class in the sortList() method, as follows: /** * Sorts the list of songs according to the specified property. */ public function sortList(sortProperty:SortProperty):void { ... var sortOptions:uint; switch (sortProperty) { case SortProperty.TITLE: sortOptions = Array.CASEINSENSITIVE; break; case SortProperty.ARTIST: sortOptions = Array.CASEINSENSITIVE; break; case SortProperty.YEAR: sortOptions = Array.NUMERIC; break; } // Perform the actual sorting of the data. this._songs.sortOn(sortProperty.propertyName, sortOptions); // Save the current sort property. this._currentSort = sortProperty; // Record that the list is sorted. this._needToSort = false; } When sorting by title or artist, it makes sense to sort alphabetically, but when sorting by year, it’s most logical to perform a numeric sort. The switch statement is used to define the appropriate sorting option, stored in the variable sortOptions, according to the value specified in the sortProperty parameter. Here again the named enumeration members are used to distinguish between properties, rather than hard-coded values. With the sort property and sort options determined, the _songs array is actually sorted by calling its sortOn() method, passing those two values as parameters. The current sort property is recorded, as is the fact that the song list is currently sorted.
  • 251. Example: PlayList 251 Combining array elements into a character-delimited string In addition to using an array to maintain the song list in the PlayList class, in this example arrays are also used in the Song class to help manage the list of genres to which a given song belongs. Consider this snippet from the Song class’s definition: private var _genres:String; public function Song(title:String, artist:String, year:uint, filename:String, genres:Array) { ... // Genres are passed in as an array // but stored as a semicolon-separated string. this._genres = genres.join(";"); } When creating a new Song instance, the genres parameter that is used to specify the genre (or genres) the song belongs to is defined as an Array instance. This makes it convenient to group multiple genres together into a single variable that can be passed to the constructor. However, internally the Song class maintains the genres in the private _genres variable as a semicolon-separated String instance. The Array parameter is converted into a semicolon- separated string by calling its join() method with the literal string value ";" as the specified delimiter. By the same token, the genres accessors allow genres to be set or retrieved as an Array: public function get genres():Array { // Genres are stored as a semicolon-separated String, // so they need to be transformed into an Array to pass them back out. return this._genres.split(";"); } public function set genres(value:Array):void { // Genres are passed in as an array, // but stored as a semicolon-separated string. this._genres = value.join(";"); } The genres set accessor behaves exactly the same as the constructor; it accepts an Array and calls the join() method to convert it to a semicolon-separated String. The get accessor performs the opposite operation: the _genres variable’s split() method is called, splitting the String into an array of values using the specified delimiter (the literal string value ";" as before).
  • 252. 252 Working with arrays
  • 253. 253 8 CHAPTER 8 Handling errors To “handle” an error means you build logic into your application that responds to, or fixes, an error, generated either when an application is compiled or when a compiled application is running. When your application handles errors, something occurs as a response when the error is encountered, as opposed to no response and whatever process created the error failing silently. Used correctly, error handling helps shield your application and its users from otherwise unexpected behavior. However, error handling is a broad category that includes responding to many kinds of errors that are thrown during compilation or at run time. This chapter focuses on how to handle run-time errors, the different types of errors that can be generated, and the advantages of the new error-handling system in ActionScript 3.0. This chapter also explains how to implement your own custom error-handling strategies for your applications. Contents Basics of error handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Types of errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .257 Error handling in ActionScript 3.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Working with the debugger version of Flash Player . . . . . . . . . . . . . . . . . . . . . . . . . . 261 Handling synchronous errors in an application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .262 Creating custom error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .267 Responding to error events and status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .268 Comparing the Error classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .272 Example: CustomErrors application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .278
  • 254. 254 Handling errors Basics of error handling Introduction to error handling A run-time error is something that goes wrong in your ActionScript code that stops the ActionScript content from running in Adobe Flash Player. To ensure that your ActionScript code runs smoothly for users, you must write code in your application that handles the error—that fixes it, works around it, or at least lets the user know that it’s happened. This process is called error handling. Error handling is a broad category that includes responding to many kinds of errors that are thrown during compilation or at run time. Errors that happen at compile time are often easier to identify—you must fix them in order to complete the process of creating a SWF file. This chapter doesn’t discuss compile-time errors; for more information on writing code that doesn’t contain compile-time errors, see Chapter 3, “ActionScript language and syntax,” on page 67 and Chapter 4, “Object-oriented programming in ActionScript,” on page 137. This chapter focuses on run-time errors. Run-time errors can be more difficult to detect, because in order for them to occur the erroneous code must actually be run. If a segment of your program has several branches of code, like an if..then..else statement, you must test every possible condition, with all the possible input values that real users might use, in order to confirm that your code is error-free. Run-time errors can be divided into two categories: program errors are mistakes in your ActionScript code, such as specifying the wrong data type for a method parameter; logical errors are mistakes in the logic (the data checking and value manipulation) of your program, such as using the wrong formula to calculate interest rates in a banking application. Again, both of these types of errors can often be detected and corrected ahead of time by diligently testing your application. Ideally, you’ll want to identify and remove all errors from your application before it is released to end users. However, not all errors can be foreseen or prevented. For example, suppose your ActionScript application loads information from a particular website that is outside of your control. If at some point that website isn’t available, the part of your application that depends on that external data won’t behave correctly. The most important aspect of error handling involves preparing for these unknown cases and handling them gracefully so that users can continue to use your application, or at least get a friendly error message explaining why it isn’t working.
  • 255. Basics of error handling 255 Run-time errors are represented in two ways in ActionScript: ■ Error classes: Many errors have an error class associated with them. When an error occurs, Flash Player creates an instance of the specific error class that is associated with that particular error. Your code can use the information contained in that error object to make an appropriate response to the error. ■ Error events: Sometimes an error occurs when Flash Player would normally trigger an event. In those cases, Flash Player triggers an error event instead. Like other events, each error event has a class associated with it, and Flash Player passes an instance of that class to the methods that are subscribed to the error event. To determine whether a particular method can trigger an error or error event, see the method’s entry in the ActionScript 3.0 Language and Components Reference. Common error-handling tasks These are common error-related tasks you might need to perform with your code: ■ Writing code to handle errors ■ Testing for, catching, and re-throwing errors ■ Defining your own error class ■ Responding to error and status events Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Asynchronous: A program command such as a method call that doesn’t provide an immediate result; instead it gives a result (or error) in the form of an event. ■ Catch: When an exception (a run-time error) occurs and your code becomes aware of the exception, that code is said to catch the exception. Once an exception is caught, Flash Player stops notifying other ActionScript code of the exception. ■ Debugger version: A special version of Flash Player that contains code for notifying users of run-time errors. In the standard version of Flash Player (the one that most users have), errors that aren’t handled by your ActionScript code are ignored by Flash Player. In the debugger version (which is included with Adobe Flash CS3 Professional and Adobe Flex), a warning message appears when an unhandled error happens. ■ Exception: An error that happens while a program is running and that the run-time environment (that is, Flash Player) can’t resolve on its own.
  • 256. 256 Handling errors ■ Re-throw: When your code catches an exception, Flash Player no longer notifies other objects of the exception. If it’s important for other objects to be notified of the exception, your code must re-throw the exception to start the notification process again. ■ Synchronous: A program command, such as a method call, that provides an immediate result (or immediately throws an error), meaning the response can be used within the same code block. ■ Throw: The act of notifying Flash Player (and consequently, notifying other objects and ActionScript code) that an error has occurred is known as throwing an error. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Essentially all the code listings in this chapter include the appropriate trace() function call. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the code listing’s trace() functions in the Output panel. Some of the later code listings are more complex and are written as a class. To test these examples: 1. Create an empty Flash document and save it to your computer. 2. Create a new ActionScript file and save it in the same directory as the Flash document. The file’s name should match the name of the class in the code listing. For instance, if the code listing defines a class named ErrorTest, use the name ErrorTest.as to save the ActionScript file. 3. Copy the code listing into the ActionScript file and save the file. 4. In the Flash document, click a blank part of the Stage or work space to activate the document Property inspector. 5. In the Property inspector, in the Document Class field, enter the name of the ActionScript class you copied from the text. 6. Run the program using Control > Test Movie You will see the results of the example in the Output panel (if the example uses the trace() function) or in a text field created by the example code.
  • 257. Types of errors 257 These techniques for testing example code listings are described in more detail in “Testing in- chapter example code listings” on page 63. Types of errors When you develop and run applications, you encounter different types of errors and error terminology. The following list introduces the major error types and terms: ■ Compile-time errors are raised by the ActionScript compiler during code compilation. Compile-time errors occur when syntactical problems in your code prevent your application from being built. ■ Run-time errors occur when you run your application after you compile it. Run-time errors represent errors that are caused while a SWF file plays in Adobe Flash Player 9. In most cases, you will be able to handle run-time errors as they occur, reporting them to the user and taking steps to keep your application running. If the error is a fatal error, such as not being able to connect to a remote website or load required data, you can use error handling to allow your application to finish gracefully. ■ Synchronous errors are run-time errors that occur at the time a function is invoked—for example, when you try to use a specific method and the argument you pass to the method is invalid, so Flash Player throws an exception. Most errors occur synchronously—at the time the statement executes—and the flow of control passes immediately to the most applicable catch statement. For example, the following code excerpt throws a run-time error because the browse() method is not called before the program attempts to upload a file: var fileRef:FileReference = new FileReference(); try { fileRef.upload("http://www.yourdomain.com/fileupload.cfm"); } catch (error:IllegalOperationError) { trace(error); // Error #2037: Functions called in incorrect sequence, or earlier // call was unsuccessful. }
  • 258. 258 Handling errors In this case, a run-time error is thrown synchronously because Flash Player determined that the browse() method was not called before the file upload was attempted. For detailed information on synchronous error handling, see “Handling synchronous errors in an application” on page 262. ■ Asynchronous errors are run-time errors that occur at various points during run time; they generate events and are caught by event listeners. An asynchronous operation is one in which a function initiates an operation, but doesn’t wait for it to complete. You can create an error event listener to wait for the application or user to try some operation, and if the operation fails, you catch the error with an event listener and respond to the error event. Then, the event listener calls an event handler function to respond to the error event in a useful manner. For example, the event handler could launch a dialog box that prompts the user to resolve the error. Consider the file-upload synchronous error example presented earlier. If you successfully call the browse() method before beginning a file upload, Flash Player would dispatch several events. For example, when an upload starts, the open event is dispatched. When the file upload operation completes successfully, the complete event is dispatched. Because event handling is asynchronous (that is, it does not occur at specific, known, predesignated times), you need to use the addEventListener() method to listen for these specific events, as the following code shows: var fileRef:FileReference = new FileReference(); fileRef.addEventListener(Event.SELECT, selectHandler); fileRef.addEventListener(Event.OPEN, openHandler); fileRef.addEventListener(Event.COMPLETE, completeHandler); fileRef.browse(); function selectHandler(event:Event):void { trace("...select..."); var request:URLRequest = new URLRequest("http://www.yourdomain.com/ fileupload.cfm"); request.method = URLRequestMethod.POST; event.target.upload(request.url); } function openHandler(event:Event):void { trace("...open..."); } function completeHandler(event:Event):void { trace("...complete..."); }
  • 259. Error handling in ActionScript 3.0 259 For detailed information on asynchronous error handling, see “Responding to error events and status” on page 268. ■ Uncaught exceptions are errors thrown with no corresponding logic (like a catch statement) to respond to them. If your application throws an error, and no appropriate catch statement or event handler can be found at the current or higher level to handle the error, the error is considered an uncaught exception. At run time, Flash Player ignores, by design, uncaught errors and tries to continue playing if the error doesn’t stop the current SWF file, because users can’t necessarily resolve an error themselves. The process of ignoring an uncaught error is called “failing silently” and can complicate debugging applications. The debugger version of Flash Player responds to an uncaught error by terminating the current script and displaying the uncaught error in trace statement output or writing the error message to a log file. If the exception object is an instance of the Error class or one of its subclasses, the getStackTrace() method is invoked, and the stack trace information will also be displayed in trace statement output or in a log file. For more information about using the debugger version of Flash Player, see “Working with the debugger version of Flash Player” on page 261. Error handling in ActionScript 3.0 Since many applications can run without building the logic to handle errors, developers are tempted to postpone building error handling into their applications. However, without error handling, an application may easily stall or frustrate the user if something doesn’t work as expected. ActionScript 2.0 has an Error class that allows you to build logic into custom functions to throw an exception with a specific message. Because error handling is critical for making a user-friendly application, ActionScript 3.0 includes an expanded architecture for catching errors. NOTE While the ActionScript 3.0 Language and Components Reference documents the exceptions thrown by many methods, it may not include all possible exceptions for each method. A method may throw an exception for syntax errors or other problems that are not noted explicitly in the method description, even when the description does list some of the exceptions a method throws.
  • 260. 260 Handling errors ActionScript 3.0 error-handling elements ActionScript 3.0 includes many tools for error handling, including: ■ Error classes. In compliance with the ECMAScript (ECMA-262) edition 4 draft language specification, ActionScript 3.0 includes a broad range of Error classes to expand the scope of situations that may produce error objects. Each Error class helps applications handle and respond to specific error conditions, whether they are related to system errors (like a MemoryError condition), coding errors (like an ArgumentError condition), networking and communication errors (like a URIError condition), or other situations. For more information on each class, see “Comparing the Error classes” on page 272. ■ Fewer silent failures. In earlier versions of Flash Player, errors were generated and reported only if you explicitly used the throw statement. For Flash Player 9, native ActionScript methods and properties throw run-time errors that allow you to handle these exceptions more effectively when they occur, and then individually react to each exception. ■ Clear error messages displayed during debugging. When you are using the debugger version of Flash Player, problematic code or situations will generate robust error messages, which help you easily identify reasons why a particular block of code fails. This makes fixing errors more efficient. For more information, see “Working with the debugger version of Flash Player” on page 261. ■ Precise errors allow for clear error messages displayed to users at run time. In previous versions of Flash Player, the FileReference.upload() method returned a Boolean value of false if the upload() call was unsuccessful, indicating one of five possible errors. If an error occurs when you call the upload() method in ActionScript 3.0, you can throw one of four specific errors, which helps you display more accurate error messages to end users. ■ Refined error handling. Distinct errors are thrown for many common situations. For example, in ActionScript 2.0, before a FileReference object has been populated, the name property has the value null (so, before you can use or display the name property, you need to ensure that the value is set and not null). In ActionScript 3.0, if you attempt to access the name property before it has been populated, Flash Player throws an IllegalOperationError, which informs you that the value has not been set, and you can use try..catch..finally blocks to handle the error. For more information see “Using try..catch..finally statements” on page 262. ■ No significant performance drawbacks. Using try..catch..finally blocks to handle errors takes little or no additional resources compared to previous versions of ActionScript. ■ An ErrorEvent class that allows you to build listeners for specific asynchronous error events. For more information see “Responding to error events and status” on page 268.
  • 261. Working with the debugger version of Flash Player 261 Error-handling strategies As long as your application doesn’t encounter a problematic condition, it may still run successfully if you don’t build error-handling logic into your code. However, if you don’t actively handle errors and your application does encounter a problem, your users will never know why your application fails when it does. There are different ways you can approach error handling in your application. The following list summarizes the three major options for handling errors: ■ Use try..catch..finally statements. These will catch synchronous errors as they occur. You can nest your statements into a hierarchy to catch exceptions at various levels of code execution. For more information, see “Using try..catch..finally statements” on page 262. ■ Create your own custom error objects. You can use the Error class to create your own custom error objects to track specific operations in your application that are not covered by built-in error types. Then you can use try..catch..finally statements on your custom error objects. For more information see “Creating custom error classes” on page 267. ■ Write event listeners and handlers to respond to error events. By using this strategy, you can create global error handlers that let you handle similar events without duplicating a lot of code in try..catch..finally blocks. You are also more likely to catch asynchronous errors using this approach. For more information, see “Responding to error events and status” on page 268. Working with the debugger version of Flash Player Adobe provides developers with a special edition of the Flash Player to assist debugging efforts. You obtain a copy of the debugger version of Flash Player when you install Adobe Flash CS3 Professional or Adobe Flex Builder 2. There is a notable difference in how the debugger version and the release version of Flash Player indicate errors. The debugger version shows the error type (such as a generic Error, IOError, or EOFError), error number, and a human-readable error message. The release version shows only the error type and error number. For example, consider the following code: try { tf.text = myByteArray.readBoolean(); }
  • 262. 262 Handling errors catch (error:EOFError) { tf.text = error.toString(); } If the readBoolean() method threw an EOFError in the debugger version of Flash Player, the following message would be displayed in the tf text field: “EOFError: Error #2030: End of file was encountered.” The same code in a release version of Flash Player would display the following text: “EOFError: Error #2030.” In order to keep Flash Player’s resources and size to a minimum in the release version, error message strings are not present. You can look up the error number in the documentation (the appendixes of the ActionScript 3.0 Language and Components Reference) to correlate to an error message. Alternatively, you can reproduce the error using the debugger version of Flash Player to see the full message. Handling synchronous errors in an application The most common error handling is synchronous error-handling logic, where you insert statements into your code to catch synchronous errors at run time. This type of error handling lets your application notice and recover from run-time errors when functions fail. The logic for catching a synchronous error includes try..catch..finally statements, which literally try an operation, catch any error response from Flash Player, and finally execute some other operation to handle the failed operation. Using try..catch..finally statements When you work with synchronous run-time errors, use the try..catch..finally statements to catch errors. When a run-time error occurs, Flash Player throws an exception, which means that Flash Player suspends normal execution and creates a special object of type Error. The Error object is then thrown to the first available catch block. The try statement encloses statements that have the potential to create errors. You always use the catch statement with a try statement. If an error is detected in one of the statements in the try statement block, the catch statements that are attached to that try statement will execute.
  • 263. Handling synchronous errors in an application 263 The finally statement encloses statements that will execute whether or not an error occurs in the try block. If there is no error, the statements within the finally block execute after the try block statements complete. If there is an error, the appropriate catch statement executes first, followed by the statements in the finally block. The following code demonstrates the syntax for using the try..catch..finally statements: try { // some code that could throw an error } catch (err:Error) { // code to react to the error } finally { // Code that runs whether or not an error was thrown. This code can clean // up after the error, or take steps to keep the application running. } Each catch statement identifies a specific type of exception that it handles. The catch statement can specify only error classes that are subclasses of the Error class. Each catch statement is checked in order. Only the first catch statement that matches the type of error thrown will execute. In other words, if you first check the higher-level Error class and then a subclass of the Error class, only the higher-level Error class will match. The following code illustrates this point: try { throw new ArgumentError("I am an ArgumentError"); } catch (error:Error) { trace("<Error> " + error.message); } catch (error:ArgumentError) { trace("<ArgumentError> " + error.message); } The previous code displays the following output: <Error> I am an ArgumentError In order to correctly catch the ArgumentError, you need to make sure that the most specific error types are listed first and the more generic error types are listed later, as the following code shows: try {
  • 264. 264 Handling errors throw new ArgumentError("I am an ArgumentError"); } catch (error:ArgumentError) { trace("<ArgumentError> " + error.message); } catch (error:Error) { trace("<Error> " + error.message); } Several methods and properties in the Flash Player API throw run-time errors if they encounter errors while they execute. For example, the close() method in the Sound class throws an IOError if the method is unable to close the audio stream, as demonstrated in the following code: var mySound:Sound = new Sound(); try { mySound.close(); } catch (error:IOError) { // Error #2029: This URLStream object does not have an open stream. } As you become more familiar with the ActionScript 3.0 Language and Components Reference, you’ll notice which methods throw exceptions, as detailed in each method’s description. The throw statement Flash Player throws exceptions when it encounters errors in your application at run time. In addition, you can explicitly throw exceptions yourself using the throw statement. When explicitly throwing errors, Adobe recommends that you throw instances of the Error class or its subclasses. The following code demonstrates a throw statement that throws an instance of the Error class, MyErr, and eventually calls a function, myFunction(), to respond after the error is thrown: var MyError:Error = new Error("Encountered an error with the numUsers value", 99); var numUsers:uint = 0; try { if (numUsers == 0) { trace("numUsers equals 0"); } }
  • 265. Handling synchronous errors in an application 265 catch (error:uint) { throw MyError; // Catch unsigned integer errors. } catch (error:int) { throw MyError; // Catch integer errors. } catch (error:Number) { throw MyError; // Catch number errors. } catch (error:*) { throw MyError; // Catch any other error. } finally { myFunction(); // Perform any necessary cleanup here. } Notice that the catch statements are ordered so that the most specific data types are listed first. If the catch statement for the Number data type was listed first, neither the catch statement for the uint data type nor the catch statement for the int data type would ever get executed. Displaying a simple error message One of the biggest benefits of the new exception and error event model is that it allows you to tell users when and why an action has failed. Your part is to write the code to display the message and offer options in response. The following code shows a simple try..catch statement to display the error in a text field: package { import flash.display.Sprite; import flash.text.TextField; public class SimpleError extends Sprite { public var employee:XML = <EmpCode> <costCenter>1234</costCenter> NOTE In the Java programming language, each function that can throw an exception must declare this fact, listing the exception classes it can throw in a throws clause attached to the function declaration. ActionScript does not require you to declare the exceptions that can be thrown by a function.
  • 266. 266 Handling errors <costCenter>1-234</costCenter> </EmpCode>; public function SimpleError() { try { if (employee.costCenter.length() != 1) { throw new Error("Error, employee must have exactly one cost center assigned."); } } catch (error:Error) { var errorMessage:TextField = new TextField(); errorMessage.autoSize = TextFieldAutoSize.LEFT; errorMessage.textColor = 0xFF0000; errorMessage.text = error.message; addChild(errorMessage); } } } } Using a wider range of error classes and built-in compiler errors, ActionScript 3.0 offers more information than previous versions of ActionScript about why something has failed. This enables you to build more stable applications with better error handling. Rethrowing errors When you build applications, there are several occasions in which you may need to rethrow an error if you are unable to handle the error properly. For example, the following code shows a nested try..catch block, which rethrows a custom ApplicationError if the nested catch block is unable to handle the error: try { try { trace("<< try >>"); throw new ArgumentError("some error which will be rethrown"); } catch (error:ApplicationError) { trace("<< catch >> " + error); trace("<< throw >>"); throw error;
  • 267. Creating custom error classes 267 } catch (error:Error) { trace("<< Error >> " + error); } } catch (error:ApplicationError) { trace("<< catch >> " + error); } The output from the previous snippet would be the following: << try >> << catch >> ApplicationError: some error which will be rethrown << throw >> << catch >> ApplicationError: some error which will be rethrown The nested try block throws a custom ApplicationError error that is caught by the subsequent catch block. This nested catch block can try to handle the error, and if unsuccessful, throw the ApplicationError object to the enclosing try..catch block. Creating custom error classes You can extend one of the standard Error classes to create your own specialized error classes in ActionScript. There are a number of reasons to create your own error classes: ■ To identify specific errors or groups of errors that are unique to your application. For example, you may want to take different actions for errors thrown by your own code, in addition to those trapped by Flash Player. You can create a subclass of the Error class to track the new error data type in try..catch blocks. ■ To provide unique error display capabilities for errors generated by your application. For example, you can create a new toString() method that formats your error messages in a certain way. You can also define a lookupErrorString() method that takes an error code and retrieves the proper message based on the user’s language preference. A specialized error class must extend the core ActionScript Error class. Here is an example of a specialized AppError class that extends the Error class: public class AppError extends Error { public function AppError(message:String, errorID:int) { super(message, errorID); } }
  • 268. 268 Handling errors The following shows an example of using AppError in your project: try { throw new AppError("Encountered Custom AppError", 29); } catch (error:AppError) { trace(error.errorID + ": " + error.message) } Responding to error events and status One of the most noticeable improvements to error handling in ActionScript 3.0 is the support for error event handling for responding to asynchronous run-time errors. (For a definition of asynchronous errors, see “Types of errors” on page 257.) You can create event listeners and event handlers to respond to the error events. Many classes dispatch error events the same way they dispatch other events. For example, an instance of the XMLSocket class normally dispatches three types of events: Event.CLOSE, Event.CONNECT, and DataEvent.DATA. However, when a problem occurs, the XMLSocket class can dispatch the IOErrorEvent.IOError or the SecurityErrorEvent.SECURITY_ERROR. For more information about event listeners and event handlers, see Chapter 10, “Handling events,” on page 313. Error events fit into one of two categories: ■ Error events that extend the ErrorEvent class The flash.events.ErrorEvent class contains the properties and methods for managing Flash Player run-time errors related to networking and communication operations. The AsyncErrorEvent, IOErrorEvent, and SecurityErrorEvent classes extend the ErrorEvent class. If you’re using the debugger version of Flash Player, a dialog box will inform you at run-time of any error events without listener functions that the player encounters. NOTE If you want to override the Error.toString() method in your subclass, you need to give it one ...(rest) parameter. The ECMAScript (ECMA-262) edition 3 language specification defines the Error.toString() method that way, and ActionScript 3.0 defines it the same way for backward compatibility with that specification. Therefore, when you override the Error.toString() method, you must match the parameters exactly. You will not want to pass any parameters to your toString() method at run time, because those parameters are ignored.
  • 269. Responding to error events and status 269 ■ Status-based error events The status-based error events are related to the netStatus and status properties of the networking and communication classes. If Flash Player encounters a problem when reading or writing data, the value of the netStatus.info.level or status.level properties (depending on the class object you’re using) is set to the value "error". You respond to this error by checking if the level property contains the value "error" in your event handler function. Working with error events The ErrorEvent class and its subclasses contain error types for handling errors dispatched by Flash Player as it tries to read or write data. The following example uses both a try..catch statement and error event handlers to display any errors detected while trying to read a local file. You can add more sophisticated handling code to provide a user with options or otherwise handle the error automatically in the places indicated by the comment “your error-handling code here”: package { import flash.display.Sprite; import flash.errors.IOError; import flash.events.IOErrorEvent; import flash.events.TextEvent; import flash.media.Sound; import flash.media.SoundChannel; import flash.net.URLRequest; import flash.text.TextField; public class LinkEventExample extends Sprite { private var myMP3:Sound; public function LinkEventExample() { myMP3 = new Sound(); var list:TextField = new TextField(); list.autoSize = TextFieldAutoSize.LEFT; list.multiline = true; list.htmlText = "<a href="event:track1.mp3">Track 1</a><br>"; list.htmlText += "<a href="event:track2.mp3">Track 2</a><br>"; addEventListener(TextEvent.LINK, linkHandler); addChild(list); } private function playMP3(mp3:String):void { try
  • 270. 270 Handling errors { myMP3.load(new URLRequest(mp3)); myMP3.play(); } catch (err:Error) { trace(err.message); // your error-handling code here } myMP3.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); } private function linkHandler(linkEvent:TextEvent):void { playMP3(linkEvent.text); // your error-handling code here } private function errorHandler(errorEvent:IOErrorEvent):void { trace(errorEvent.text); // your error-handling code here } } } Working with status change events Flash Player dynamically changes the value of the netStatus.info.level or status.level properties for the classes that support the level property. The classes that have the netStatus.info.level property are NetConnection, NetStream, and SharedObject. The classes that have the status.level property are HTTPStatusEvent, Camera, Microphone, and LocalConnection. You can write a handler function to respond to the change in level value and track communication errors. The following example uses a netStatusHandler() function to test the value of the level property. If the level property indicates that an error has been encountered, the code traces the message “Video stream failed”. package { import flash.display.Sprite; import flash.events.NetStatusEvent; import flash.events.SecurityErrorEvent; import flash.media.Video; import flash.net.NetConnection; import flash.net.NetStream;
  • 271. Responding to error events and status 271 public class VideoExample extends Sprite { private var videoUrl:String = "Video.flv"; private var connection:NetConnection; private var stream:NetStream; public function VideoExample() { connection = new NetConnection(); connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); connection.connect(null); } private function netStatusHandler(event:NetStatusEvent):void { if (event.info.level = "error") { trace("Video stream failed") } else { connectStream(); } } private function securityErrorHandler(event:SecurityErrorEvent):void { trace("securityErrorHandler: " + event); } private function connectStream():void { var stream:NetStream = new NetStream(connection); var video:Video = new Video(); video.attachNetStream(stream); stream.play(videoUrl); addChild(video); } } }
  • 272. 272 Handling errors Comparing the Error classes ActionScript provides a number of predefined Error classes. Many of these classes are used by Flash Player, but you can also use the same Error classes in your own code. There are two main types of Error classes in ActionScript 3.0: ActionScript core Error classes and flash.error package Error classes. The core Error classes are prescribed by the ECMAScript (ECMA-262) edition 4 draft language specification. The flash.error package contents are additional classes introduced to aid ActionScript 3.0 application development and debugging. ECMAScript core Error classes The ECMAScript core error classes include the Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError classes. Each of these classes are located in the top-level namespace. Class name Description Notes Error The Error class can be used for throwing exceptions, and is the base class for the other exception classes defined in ECMAScript: EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError. The Error class serves as the base class for all run-time errors thrown by Flash Player, and is the recommended base class for any custom error classes. EvalError An EvalError exception is thrown if any parameters are passed to the Function class’s constructor or if user code calls the eval() function. In ActionScript 3.0, support for the eval() function has been removed and attempts to use the function cause an error to be thrown. Earlier versions of Flash Player used the eval() function to access variables, properties, objects, or movie clips by name. RangeError A RangeError exception is thrown if a numeric value falls outside of an acceptable range. For example, a RangeError would be thrown by the Timer class if a delay was either negative or was not finite. A RangeError could also be thrown if you attempted to add a display object at an invalid depth.
  • 273. Comparing the Error classes 273 ReferenceError A ReferenceError exception is thrown when a reference to an undefined property is attempted on a sealed (nondynamic) object. Versions of the ActionScript compiler before ActionScript 3.0 did not throw an error when access was attempted to a property that was undefined. However, because the new ECMAScript specification specifies that an error should be thrown in this condition, ActionScript 3.0 throws the ReferenceError exception. Exceptions for undefined variables point to potential bugs, helping you improve software quality. However, if you are not used to having to initialize your variables, this new ActionScript behavior may require some changes in your coding habits. SyntaxError A SyntaxError exception is thrown when a parsing error occurs in your ActionScript code. For more information, see Section 15.11.6.4 of the ECMAScript (ECMA-262) edition 3 (until edition 4 is available) language specification at www.ecma-international.org/ publications/standards/Ecma- 262.htm, as well as Section 10.3.1 of the ECMAScript for XML (E4X) specification (ECMA-357 edition 2) at www.ecma-international.org/ publications/standards/Ecma- 357.htm. A SyntaxError can be thrown under the following circumstances: • ActionScript throws SyntaxError exceptions when an invalid regular expression is parsed by the RegExp class. • ActionScript throws SyntaxError exceptions when invalid XML is parsed by the XMLDocument class. Class name Description Notes
  • 274. 274 Handling errors TypeError The TypeError exception is thrown when the actual type of an operand is different from the expected type. For more information, see Section 15.11.6.5 of the ECMAScript specification at www.ecma-international.org/ publications/standards/Ecma- 262.htm, as well as Section 10.3 of the E4X specification at www.ecma-international.org/ publications/standards/Ecma- 357.htm. A TypeError can be thrown under the following circumstances: • An actual parameter of a function or method could not be coerced to the formal parameter type. • A value is assigned to a variable and cannot be coerced to the variable’s type. • The right side of the is or instanceof operator is not a valid type. • The super keyword is used illegally. • A property lookup results in more than one binding, and is therefore ambiguous. • A method is invoked on an incompatible object. For example, a TypeError exception is thrown if a method in the RegExp class is “grafted” onto a generic object and then invoked. URIError The URIError exception is thrown when one of the global URI handling functions is used in a way that is incompatible with its definition. For more information, see Section 15.11.6.6 of the ECMAScript specification at www.ecma-international.org/ publications/standards/Ecma- 262.htm. A URIError can be thrown under the following circumstances: An invalid URI is specified for a Flash Player API function that expects a valid URI, such as Socket.connect(). Class name Description Notes
  • 275. Comparing the Error classes 275 ActionScript core Error classes In addition to the core ECMAScript Error classes, ActionScript adds several classes of its own for ActionScript-specific error conditions and error-handling functionality. Because these classes are ActionScript language extensions to ECMAScript edition 4 draft language specification that could potentially be interesting additions to the draft specification, they are kept at the top level instead of being placed in a package like flash.error. Class name Description Notes ArgumentError The ArgumentError class represents an error that occurs when the parameter values supplied during a function call do not match the parameters defined for that function. Some examples of argument errors include the following: • Too few or too many arguments are supplied to a method. • An argument was expected to be a member of an enumeration and was not. SecurityError The SecurityError exception is thrown when a security violation takes place and access is denied. Some examples of security errors include the following: • An unauthorized property access or method call is made across a security sandbox boundary. • An attempt was made to access a URL not permitted by the security sandbox. • A socket connection was attempted to an unauthorized port number—for example, a port below 1024—without a policy file present. • An attempt was made to access the user's camera or microphone, and the request to access the device was denied by the user. VerifyError A VerifyError exception is thrown when a malformed or corrupted SWF file is encountered. When a SWF file loads another SWF file, the parent SWF can catch a VerifyError generated by the loaded SWF.
  • 276. 276 Handling errors flash.error package Error classes The flash.error package contains Error classes that are considered part of the Flash Player API. In contrast to the Error classes just described, the flash.error package communicates errors events that are specific to Flash Player. Class name Description Notes EOFError An EOFError exception is thrown when you attempt to read past the end of the available data. For example, an EOFError is thrown when one of the read methods in the IDataInput interface is called and there is insufficient data to satisfy the read request. IllegalOperationError An IllegalOperationError exception is thrown when a method is not implemented or the implementation doesn't cover the current usage. Examples of illegal operation error exceptions include the following: • A base class, such as DisplayObjectContainer, provides more functionality than the Stage can support. For example, if you attempt to get or set a mask on the Stage (using stage.mask), Flash Player will throw an IllegalOperationError with the message “The Stage class does not implement this property or method.” • A subclass inherits a method it does not require and does not want to support. • Certain accessibility methods are called when Flash Player is compiled without accessibility support. • Authoring-only features are invoked from a run-time version of Flash Player. • You attempt to set the name of an object placed on the timeline. IOError An IOError exception is thrown when some type of I/O exception occurs. You get this error, for example, when a read-write operation is attempted on a socket that is not connected or that has become disconnected.
  • 277. Comparing the Error classes 277 MemoryError A MemoryError exception is thrown when a memory allocation request fails. By default, ActionScript Virtual Machine 2 does not impose a limit on how much memory an ActionScript program may allocate. On a desktop PC, memory allocation failures are infrequent. You see an error thrown when the system is unable to allocate the memory required for an operation. So, on a desktop PC, this exception is rare unless an allocation request is extremely large; for example, a request for 3 billion bytes is impossible because a 32-bit Microsoft® Windows® program can access only 2 GB of address space. ScriptTimeoutError A ScriptTimeoutError exception is thrown when a script timeout interval of 15 seconds is reached. By catching a ScriptTimeoutError exception, you can handle the script timeout more gracefully. If there is no exception handler, the uncaught exception handler will display a dialog box with an error message. To prevent a malicious developer from catching the exception and staying in an infinite loop, only the first ScriptTimeoutError exception thrown in the course of a particular script can be caught. A subsequent ScriptTimeoutError exception cannot be caught by your code and will immediately go to the uncaught exception handler. StackOverflowError The StackOverflowError exception is thrown when the stack available to the script has been exhausted. A StackOverflowError exception might indicate that infinite recursion has occurred. Class name Description Notes
  • 278. 278 Handling errors Example: CustomErrors application The CustomErrors application demonstrates techniques for working with custom errors when building an application. These techniques are: ■ Validating an XML packet ■ Writing a custom error ■ Throwing custom errors ■ Notifying users when an error is thrown To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The CustomErrors application files can be found in the Samples/CustomError folder. The application consists of the following files: CustomErrors application overview The CustomErrors.mxml file contains the user interface and some logic for the custom error application. Once the application’s creationComplete event is dispatched, the initApp() method is invoked. This method defines a sample XML packet that will be verified by the Validator class. The following code shows the initApp() method: private function initApp():void { employeeXML = <employee id="12345"> <firstName>John</firstName> File Description CustomErrors.mxml or CustomErrors.fla The main application file in Flash (FLA) or Flex (MXML) com/example/programmingas3/errors/ ApplicationError.as A class that serves as the base error class for both the FatalError and WarningError classes. com/example/programmingas3/errors/ FatalError.as A class that defines a FatalError error that can be thrown by the application. This class extends the custom ApplicationError class. com/example/programmingas3/errors/ Validator.as A class that defines a single method that validates a user-supplied employee XML packet. com/example/programmingas3/errors/ WarningError.as A class that defines a WarningError error that can be thrown by the application. This class extends the custom ApplicationError class.
  • 279. Example: CustomErrors application 279 <lastName>Doe</lastName> <costCenter>12345</costCenter> <costCenter>67890</costCenter> </employee>; } The XML packet is later displayed in a TextArea component instance on the Stage. This allows you to modify the XML packet before attempting to revalidate it. When the user clicks the Validate button, the validateData() method is called. This method validates the employee XML packet using the validateEmployeeXML() method in the Validator class. The following code shows the validateData() method: public function validateData():void { try { var tempXML:XML = XML(xmlText.text); Validator.validateEmployeeXML(tempXML); status.text = "The XML was successfully validated."; } catch (error:FatalError) { showFatalError(error); } catch (error:WarningError) { showWarningError(error); } catch (error:Error) { showGenericError(error); } } First, a temporary XML object is created using the contents of the TextArea component instance xmlText. Next, the validateEmployeeXML() method in the custom Validator class (com.example.programmingas3/errors/Validator.as) is invoked and passes the temporary XML object as a parameter. If the XML packet is valid, the status Label component instance displays a success message and the application exits. If the validateEmployeeXML() method threw a custom error (that is, a FatalError, WarningError, or a generic Error occurred), the appropriate catch statement executes and calls either the showFatalError(), showWarningError(), or showGenericError() methods. Each of these methods displays an appropriate message in an Alert component to notify the user of the specific error that occurred. Each method also updates the status Label component instance with a specific message.
  • 280. 280 Handling errors If a fatal error occurs during an attempt to validate the employee XML packet, the error message is displayed in an Alert component, and the xmlText TextArea component instance and validateBtn Button component instance are disabled, as the following code shows: public function showFatalError(error:FatalError):void { var message:String = error.message + "nn" + "Click OK to end."; var title:String = error.getTitle(); Alert.show(message, title); status.text = "This application has ended."; this.xmlText.enabled = false; this.validateBtn.enabled = false; } If a warning error instead of a fatal error occurs, the error message is displayed in an Alert component instance, but the TextField and Button component instances aren’t disabled. The showWarningError() method displays the custom error message in the Alert component instance. The message also asks the user to decide if they want to proceed with validating the XML or abort the script. The following excerpt shows the showWarningError() method: public function showWarningError(error:WarningError):void { var message:String = error.message + "nn" + "Do you want to exit this application?"; var title:String = error.getTitle(); Alert.show(message, title, Alert.YES | Alert.NO, null, closeHandler); status.text = message; } When the user closes the Alert component instance by using either the Yes or No button, the closeHandler() method is invoked. The following excerpt shows the closeHandler() method: private function closeHandler(event:CloseEvent):void { switch (event.detail) { case Alert.YES: showFatalError(new FatalError(9999)); break; case Alert.NO: break; } } If the user chooses to abort the script by clicking Yes in the warning error Alert dialog, a FatalError is thrown, causing the application to terminate.
  • 281. Example: CustomErrors application 281 Building a custom validator The custom Validator class contains a single method, validateEmployeeXML(). The validateEmployeeXML() method takes a single argument, employee, which is the XML packet that you wish to validate. The validateEmployeeXML() method is as follows: public static function validateEmployeeXML(employee:XML):void { // checks for the integrity of items in the XML if (employee.costCenter.length() < 1) { throw new FatalError(9000); } if (employee.costCenter.length() > 1) { throw new WarningError(9001); } if (employee.ssn.length() != 1) { throw new FatalError(9002); } } To be validated, an employee must belong to one (and only one) cost center. If the employee doesn’t belong to any cost centers, the method throws a FatalError, which bubbles up to the validateData() method in the main application file. If the employee belongs to more than one cost center, a WarningError is thrown. The final check in the XML validator is that the user has exactly one social security number defined (the ssn node in the XML packet). If there is not exactly one ssn node, a FatalError error is thrown. You can add additional checks to the validateEmployeeXML() method—for instance, to ensure that the ssn node contains a valid number, or that the employee has at least one phone number and e-mail address defined, and that both values are valid. You can also modify the XML so that each employee has a unique ID and specifies the ID of their manager. Defining the ApplicationError class The ApplicationError class serves as the base class for both the FatalError and WarningError classes. The ApplicationError class extends the Error class, and defines its own custom methods and properties, including defining an error ID, severity, and an XML object that contains the custom error codes and messages. This class also defines two static constants that are used to define the severity of each error type.
  • 282. 282 Handling errors The ApplicationError class’s constructor method is as follows: public function ApplicationError() { messages = <errors> <error code="9000"> <![CDATA[Employee must be assigned to a cost center.]]> </error> <error code="9001"> <![CDATA[Employee must be assigned to only one cost center.]]> </error> <error code="9002"> <![CDATA[Employee must have one and only one SSN.]]> </error> <error code="9999"> <![CDATA[The application has been stopped.]]> </error> </errors>; } Each error node in the XML object contains a unique numeric code and an error message. Error messages can be easily looked up by their error code using E4X, as seen in the following getMessageText() method: public function getMessageText(id:int):String { var message:XMLList = messages.error.(@code == id); return message[0].text(); } The getMessageText() method takes a single integer argument, id, and returns a string. The id argument is the error code for the error to look up. For example, passing an id of 9001 retrieves the error saying that employees must be assigned to only one cost center. If more than one error has the same error code, ActionScript returns the error message only for the first result found (message[0] in the returned XMLList object). The next method in this class, getTitle(), doesn’t take any parameters and returns a string value that contains the error ID for this specific error. This value is used in the Alert component’s title to help you easily identify the exact error that occurred during validation of the XML packet. The following excerpt shows the getTitle() method: public function getTitle():String { return "Error #" + id; }
  • 283. Example: CustomErrors application 283 The final method in the ApplicationError class is toString(). This method overrides the function defined in the Error class so that you can customize the presentation of the error message. The method returns a string that identifies the specific error number and message that occurred. public override function toString():String { return "[APPLICATION ERROR #" + id + "] " + message; } Defining the FatalError class The FatalError class extends the custom ApplicationError class and defines three methods: the FatalError constructor, getTitle(), and toString(). The first method, the FatalError constructor, takes a single integer argument, errorID, and sets the error’s severity using the static constant values defined in the ApplicationError class, and gets the specific error’s error message by calling the getMessageText() method in the ApplicationError class. The FatalError constructor is as follows: public function FatalError(errorID:int) { id = errorID; severity = ApplicationError.FATAL; message = getMessageText(errorID); } The next method in the FatalError class, getTitle(), overrides the getTitle() method defined earlier in the ApplicationError class, and appends the text “-- FATAL” in the title to inform the user that a fatal error has occurred. The getTitle() method is as follows: public override function getTitle():String { return "Error #" + id + " -- FATAL"; } The final method in this class, toString(), overrides the toString() method defined in the ApplicationError class. The toString() method is public override function toString():String { return "[FATAL ERROR #" + id + "] " + message; }
  • 284. 284 Handling errors Defining the WarningError class The WarningError class extends the ApplicationError class and is nearly identical to the FatalError class, except for a couple minor string changes and sets the error severity to ApplicationError.WARNING instead of ApplicationError.FATAL, as seen in the following code: public function WarningError(errorID:int) { id = errorID; severity = ApplicationError.WARNING; message = super.getMessageText(errorID); }
  • 285. 285 9 CHAPTER 9 Using regular expressions A regular expression describes a pattern that is used to find and manipulate matching text in strings. Regular expressions resemble strings, but they can include special codes to describe patterns and repetition. For example, the following regular expression matches a string that starts with the character A followed by one or more sequential digits: /Ad+/ This chapter describes the basic syntax for constructing regular expressions. However, regular expressions can have many complexities and nuances. You can find detailed resources on regular expressions on the web and in bookstores. Keep in mind that different programming environments implement regular expressions in different ways. ActionScript 3.0 implements regular expressions as defined in the ECMAScript edition 3 language specification (ECMA- 262). Contents Basics of regular expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .286 Regular expression syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .288 Methods for using regular expressions with strings . . . . . . . . . . . . . . . . . . . . . . . . . 305 Example: A Wiki parser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
  • 286. 286 Using regular expressions Basics of regular expressions Introduction to using regular expressions A regular expression describes a pattern of characters. Regular expressions are typically used to verify that a text value conforms to a particular pattern (such as verifying that a user-entered phone number has the proper number of digits) or to replace portions of a text value that matches a particular pattern. Regular expressions can be simple. For example, suppose you wanted to confirm that a particular string matches “ABC,” or wanted to replace every occurrence of “ABC” in a string with some other text. In that case, you could use the following regular expression, which defines the pattern consisting of the letters A, B, and C in sequence: /ABC/ Note that the regular expression literal is delineated with the forward slash (/) character. Regular expression patterns can also be complex, and sometimes cryptic in appearance, such as the following expression to match a valid e-mail address: /([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}/ Most commonly you will use regular expressions to search for patterns in strings and to replace characters. In those cases, you will create a regular expression object and use it as a parameter for one of several String class methods. The following methods of the String class take regular expressions as parameters: match(), replace(), search(), and split(). For more information on these methods, see “Finding patterns in strings and replacing substrings” on page 212. The RegExp class includes the following methods: test() and exec(). For more information, see “Methods for using regular expressions with strings” on page 305. Common regular expression tasks There are several common uses for regular expressions, which are described in detail in this chapter: ■ Creating a regular expression pattern ■ Using special characters in patterns ■ Identifying sequences of multiple characters (such as “a two-digit number” or “between seven and ten letters”) ■ Identifying any character in a range of letters or numbers (such as “any letter from a to m”) ■ Identifying a character in a set of possible characters
  • 287. Basics of regular expressions 287 ■ Identifying subsequences (segments within a pattern) ■ Matching and replacing text based on patterns Important concepts and terms The following reference list contains important terms used in this chapter: ■ Escape character: A character indicating that the character that follows should be treated as a metacharacter rather than a literal character. In regular expression syntax, the backslash character () is the escape character, so a backslash followed by another character is a special code rather than just the character itself. ■ Flag: A character that specifies some option about how the regular expression pattern should be used, such as whether to distinguish between uppercase and lowercase characters. ■ Metacharacter: A character that has special meaning in a regular expression pattern, as opposed to literally representing that character in the pattern. ■ Quantifier: A character (or several characters) indicating how many times a part of the pattern should repeat. For example, a quantifier would be used to designate that a United States postal code should contain five or nine numbers. ■ Regular expression: A program statement defining a pattern of characters that can be used to confirm whether other strings match that pattern or to replace portions of a string. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because the code listings in this chapter consist primarily of regular expression patterns, testing the examples involves a few steps: 1. Create a new Flash document. 2. Select a keyframe and open the Actions panel. 3. Create a RegExp (regular expression) variable such as this one: var pattern:RegExp = /ABC/; 4. Copy the pattern from the example and assign it as the value of your RegExp variable. For instance, in the previous line of code, the pattern is the part of the code to the right of the equals sign, not including the semicolon (/ABC/). 5. Create one or more String variables containing strings appropriate for testing your regular expression. For instance, if you are creating a regular expression to test for valid e-mail addresses, create a few String variables containing valid and bad e-mail addresses:
  • 288. 288 Using regular expressions var goodEmail:String = "[email protected]"; var badEmail:String = "5@$2.99"; 6. Add lines of code to test the String variables to determine whether they match the regular expression pattern. These will be the values that you’ll want to output to the screen using the trace() function or by writing them to a text field on the Stage. trace(goodEmail, " is valid:", pattern.test(goodEmail)); trace(badEmail, " is valid:", pattern.test(badEmail)); For instance, assuming pattern defines the regular expression pattern for a valid e-mail address, the preceding lines of code writes this text to the Output panel: [email protected] is valid: true 5@$2.99 is valid: false For more information about testing values by writing the values into a text field instance on the Stage or by using the trace() function to print the values to the Output panel, see “Testing in-chapter example code listings” on page 63. Regular expression syntax This section describes all of the elements of ActionScript regular expression syntax. As you’ll see, regular expressions can have many complexities and nuances. You can find detailed resources on regular expressions on the web and in bookstores. Keep in mind that different programming environments implement regular expressions in different ways. ActionScript 3.0 implements regular expressions as defined in the ECMAScript edition 3 language specification (ECMA-262). Generally, you use regular expressions that match more complicated patterns than a simple string of characters. For example, the following regular expression defines the pattern consisting of the letters A, B, and C in sequence followed by any digit: /ABCd/ The d code represents “any digit.” The backslash () character is called the escape character, and combined with the character that follows it (in this case the letter d), it has special meaning in the regular expression. This chapter describes these escape character sequences and other regular expression syntax features. The following regular expression defines the pattern of the letters ABC followed by any number of digits (note the asterisk): /ABCd*/
  • 289. Regular expression syntax 289 The asterisk character (*) is a metacharacter. A metacharacter is a character that has special meaning in regular expressions. The asterisk is a specific type of metacharacter called a quantifier, which is used to quantify the amount of repetition of a character or group of characters. For more information, see “Quantifiers” on page 295. In addition to its pattern, a regular expression can contain flags, which specify how the regular expression is to be matched. For example, the following regular expression uses the i flag, which specifies that the regular expression ignores case sensitivity in matching strings: /ABCd*/i For more information, see “Flags and properties” on page 301. You can use regular expressions with the following methods of the String class: match(), replace(), and search(). For more information on these methods, see “Finding patterns in strings and replacing substrings” on page 212. Creating an instance of a regular expression There are two ways to create a regular expression instance. One way uses forward slash characters (/) to delineate the regular expression; the other uses the new constructor. For example, the following regular expressions are equivalent: var pattern1:RegExp = /bob/i; var pattern2:RegExp = new RegExp("bob", "i"); Forward slashes delineate a regular expression literal in the same way as quotation marks delineate a string literal. The part of the regular expression within the forward slashes defines the pattern. The regular expression can also include flags after the final delineating slash. These flags are considered to be part of the regular expression, but they are separate from its pattern. When using the new constructor, you use two strings to define the regular expression. The first string defines the pattern, and the second string defines the flags, as in the following example: var pattern2:RegExp = new RegExp("bob", "i"); When including a forward slash within a regular expression that is defined by using the forward slash delineators, you must precede the forward slash with the backslash () escape character. For example, the following regular expression matches the pattern 1/2: var pattern:RegExp = /1/2/; To include quotation marks within a regular expression that is defined with the new constructor, you must add backslash () escape character before the quotation marks (just as you would when defining any String literal). For example, the following regular expressions match the pattern eat at "joe's":
  • 290. 290 Using regular expressions var pattern1:RegExp = new RegExp("eat at "joe's"", ""); var pattern2:RegExp = new RegExp('eat at "joe's"', ""); Do not use the backslash escape character with quotation marks in regular expressions that are defined by using the forward slash delineators. Similarly, do not use the escape character with forward slashes in regular expressions that are defined with the new constructor. The following regular expressions are equivalent, and they define the pattern 1/2 "joe's": var pattern1:RegExp = /1/2 "joe's"/; var pattern2:RegExp = new RegExp("1/2 "joe's"", ""); var pattern3:RegExp = new RegExp('1/2 "joe's"', ''); Also, in a regular expression that is defined with the new constructor, to use a metasequence that begins with the backslash () character, such as d (which matches any digit), type the backslash character twice: var pattern:RegExp = new RegExp("d+", ""); // matches one or more digits You must type the backlash character twice in this case, because the first parameter of the RegExp() constructor method is a string, and in a string literal you must type a backslash character twice to have it recognized as a single backslash character. The sections that follow describe syntax for defining regular expression patterns. For more information on flags, see “Flags and properties” on page 301. Characters, metacharacters, and metasequences The simplest regular expression is one that matches a sequence of characters, as in the following example: var pattern:RegExp = /hello/; However, the following characters, known as metacharacters, have special meanings in regular expressions: ^ $ . * + ? ( ) [ ] { } | For example, the following regular expression matches the letter A followed by zero or more instances of the letter B (the asterisk metacharacter indicates this repetition), followed by the letter C: /AB*C/ To include a metacharacter without its special meaning in a regular expression pattern, you must use the backslash () escape character. For example, the following regular expression matches the letter A followed by the letter B, followed by an asterisk, followed by the letter C: var pattern:RegExp = /AB*C/;
  • 291. Regular expression syntax 291 A metasequence, like a metacharacter, has special meaning in a regular expression. A metasequence is made up of more than one character. The following sections provide details on using metacharacters and metasequences. About metacharacters The following table summarizes the metacharacters that you can use in regular expressions: Metacharacter Description ^ (caret) Matches at the start of the string. With the m (multiline) flag set, the caret matches the start of a line as well (see “The m (multiline) flag” on page 302). Note that when used at the start of a character class, the caret indicates negation, not the start of a string. For more information, see“Character classes” on page 293. $ (dollar sign) Matches at the end of the string. With the m (multiline) flag set, $ matches the position before a newline (n) character as well. For more information, see “The m (multiline) flag” on page 302. (backslash) Escapes the special metacharacter meaning of special characters. Also, use the backslash character if you want to use a forward slash character in a regular expression literal, as in /1/2/ (to match the character 1, followed by the forward slash character, followed by the character 2). . (dot) Matches any single character. A dot matches a newline character (n) only if the s (dotall) flag is set. For more information, see “The s (dotall) flag” on page 303. * (star) Matches the previous item repeated zero or more times. For more information, see “Quantifiers” on page 295. + (plus) Matches the previous item repeated one or more times. For more information, see “Quantifiers” on page 295. ? (question mark) Matches the previous item repeated zero times or one time. For more information, see “Quantifiers” on page 295. ( and ) Defines groups within the regular expression. Use groups for the following: • To confine the scope of the | alternator: /(a|b|c)d/ • To define the scope of a quantifier: /(walla.){1,2}/ • In backreferences. For example, the 1 in the following regular expression matches whatever matched the first parenthetical group of the pattern: /(w*) is repeated: 1/ For more information, see “Groups” on page 297.
  • 292. 292 Using regular expressions About metasequences Metasequences are sequences of characters that have special meaning in a regular expression pattern. The following table describes these metasequences: [ and ] Defines a character class, which defines possible matches for a single character: /[aeiou]/ matches any one of the specified characters. Within character classes, use the hyphen (-) to designate a range of characters: /[A-Z0-9]/ matches uppercase A through Z or 0 through 9. Within character classes, insert a backslash to escape the ] and - characters: /[+-]d+/ matches either + or - before one or more digits. Within character classes, other characters, which are normally metacharacters, are treated as normal characters (not metacharacters), without the need for a backslash: /[$£]/ matches either $ or £. For more information, see “Character classes” on page 293. | (pipe) Used for alternation, to match either the part on the left side or the part on the right side: /abc|xyz/ matches either abc or xyz. Metasequence Description {n} {n,} and {n,n} Specifies a numeric quantifier or quantifier range for the previous item: /A{27}/ matches the character A repeated 27 times. /A{3,}/ matches the character A repeated 3 or more times. /A{3,5}/ matches the character A repeated 3 to 5 times. For more information, see “Quantifiers” on page 295. b Matches at the position between a word character and a nonword character. If the first or last character in the string is a word character, also matches the start or end of the string. B Matches at the position between two word characters. Also matches the position between two nonword characters. d Matches a decimal digit. D Matches any character other than a digit. f Matches a form feed character. n Matches the newline character. r Matches the return character. Metacharacter Description
  • 293. Regular expression syntax 293 Character classes You use character classes to specify a list of characters to match one position in the regular expression. You define character classes with square brackets ( [ and ] ). For example, the following regular expression defines a character class that matches bag, beg, big, bog, or bug: /b[aeiou]g/ Escape sequences in character classes Most metacharacters and metasequences that normally have special meanings in a regular expression do not have those same meanings inside a character class. For example, in a regular expression, the asterisk is used for repetition, but this is not the case when the asterisk appears in a character class. The following character class matches the asterisk literally, along with any of the other characters listed: /[abc*123]/ s Matches any white-space character (a space, tab, newline, or return character). S Matches any character other than a white-space character. t Matches the tab character. unnnn Matches the Unicode character with the character code specified by the hexadecimal number nnnn. For example, u263a is the smiley character. v Matches a vertical feed character. w Matches a word character (A–Z, a–z, 0-9, or _). Note that w does not match non-English characters, such as é, ñ, or ç. W Matches any character other than a word character. xnn Matches the character with the specified ASCII value, as defined by the hexadecimal number nn. Metasequence Description
  • 294. 294 Using regular expressions However, the three characters listed in the following table do function as metacharacters, with special meaning, in character classes: For any of these characters to be recognized as literal characters (without the special metacharacter meaning), you must precede the character with the backslash escape character. For example, the following regular expression includes a character class that matches any one of four symbols ($, , ], or -): /[$]-]/ In addition to the metacharacters that retain their special meanings, the following metasequences function as metasequences within character classes: Other regular expression metasequences and metacharacters are treated as normal characters within a character class. Ranges of characters in character classes Use the hyphen to specify a range of characters, such as A-Z, a-z, or 0-9. These characters must constitute a valid range in the character set. For example, the following character class matches any one of the characters in the range a-z or any digit: /[a-z0-9]/ Metacharacter Meaning in character classes ] Defines the end of the character class. - Defines a range of characters (see “Ranges of characters in character classes” on page 294). Defines metasequences and undoes the special meaning of metacharacters. Metasequence Meaning in character classes n Matches a newline character. r Matches a return character. t Matches a tab character. unnnn Matches the character with the specified Unicode code point value (as defined by the hexadecimal number nnnn). xnn Matches the character with the specified ASCII value (as defined by the hexadecimal number nn).
  • 295. Regular expression syntax 295 You can also use the xnn ASCII character code to specify a range by ASCII value. For example, the following character class matches any character from a set of extended ASCII characters (such as é and ê): /[x80-x9A]/ Negated character classes When you use a caret (^) character at the beginning of a character class, it negates that class— any character not listed is considered a match. The following character class matches any character except for a lowercase letter (a–z) or a digit: /[^a-z0-9]/ You must type the caret (^) character at the beginning of a character class to indicate negation. Otherwise, you are simply adding the caret character to the characters in the character class. For example, the following character class matches any one of a number of symbol characters, including the caret: /[!.,#+*%$&^]/ Quantifiers You use quantifiers to specify repetitions of characters or sequences in patterns, as follows: You can apply a quantifier to a single character, to a character class, or to a group: ■ /a+/ matches the character a repeated one or more times. ■ /d+/ matches one or more digits. ■ /[abc]+/ matches a repetition of one or more character, each of which is either a, b, or c. ■ /(very, )*/ matches the word very followed by a comma and a space repeated zero or more times. Quantifier metacharacter Description * (star) Matches the previous item repeated zero or more times. + (plus) Matches the previous item repeated one or more times. ? (question mark) Matches the previous item repeated zero times or one time. {n} {n,} and {n,n} Specifies a numeric quantifier or quantifier range for the previous item: /A{27}/ matches the character A repeated 27 times. /A{3,}/ matches the character A repeated 3 or more times. /A{3,5}/ matches the character A repeated 3 to 5 times.
  • 296. 296 Using regular expressions You can use quantifiers within parenthetical groupings that have quantifiers applied to them. For example, the following quantifier matches strings such as word and word-word-word: /w+(-w+)*/ By default, regular expressions perform what is known as greedy matching. Any subpattern in the regular expression (such as .*) tries to match as many characters in the string as possible before moving forward to the next part of the regular expression. For example, consider the following regular expression and string: var pattern:RegExp = /<p>.*</p>/; str:String = "<p>Paragraph 1</p> <p>Paragraph 2</p>"; The regular expression matches the entire string: <p>Paragraph 1</p> <p>Paragraph 2</p> Suppose, however, that you want to match only one <p>...</p> grouping. You can do this with the following: <p>Paragraph 1</p> Add a question mark (?) after any quantifier to change it to what is known as a lazy quantifier. For example, the following regular expression, which uses the lazy *? quantifier, matches <p> followed by the minimum number of characters possible (lazy), followed by </p>: /<p>.*?</p>/ Keep in mind the following points about quantifiers: ■ The quantifiers {0} and {0,0} do not exclude an item from a match. ■ Do not combine multiple quantifiers, as in /abc+*/. ■ The dot (.) does not span lines unless the s (dotall) flag is set, even if it is followed by a * quantifier. For example, consider the following code: var str:String = "<p>Testn"; str += "Multiline</p>"; var re:RegExp = /<p>.*</p>/; trace(str.match(re)); // null; re = /<p>.*</p>/s; trace(str.match(re)); // output: <p>Test // Multiline</p> For more information, see “The s (dotall) flag” on page 303.
  • 297. Regular expression syntax 297 Alternation Use the | (pipe) character in a regular expression to have the regular expression engine consider alternatives for a match. For example, the following regular expression matches any one of the words cat, dog, pig, rat: var pattern:RegExp = /cat|dog|pig|rat/; You can use parentheses to define groups to restrict the scope of the | alternator. The following regular expression matches cat followed by nap or nip: var pattern:RegExp = /cat(nap|nip)/; For more information, see “Groups” on page 297. The following two regular expressions, one using the | alternator, the other using a character class (defined with [ and ] ), are equivalent: /1|3|5|7|9/ /[13579]/ For more information, see “Character classes” on page 293. Groups You can specify a group in a regular expression by using parentheses, as follows: /class-(d*)/ A group is a subsection of a pattern. You can use groups to do the following things: ■ Apply a quantifier to more than one character. ■ Delineate subpatterns to be applied with alternation (by using the | character). ■ Capture substring matches (for example, by using 1 in a regular expression to match a previously matched group, or by using $1 similarly in the replace() method of the String class). The following sections provide details on these uses of groups. Using groups with quantifiers If you do not use a group, a quantifier applies to the character or character class that precedes it, as the following shows: var pattern:RegExp = /ab*/ ; // matches the character a followed by // zero or more occurrences of the character b pattern = /ad+/; // matches the character a followed by // one or more digits
  • 298. 298 Using regular expressions pattern = /a[123]{1,3}/; // matches the character a followed by // one to three occurrences of either 1, 2, or 3 However, you can use a group to apply a quantifier to more than one character or character class: var pattern:RegExp = /(ab)*/; // matches zero or more occurrences of the character a // followed by the character b, such as ababab pattern = /(ad)+/; // matches one or more occurrences of the character a followed by // a digit, such as a1a5a8a3 pattern = /(spam ){1,3}/; // matches 1 to 3 occurrences of the word spam followed by a space For more information on quantifiers, see “Quantifiers” on page 295. Using groups with the alternator (|) character You can use groups to define the group of characters to which you want to apply an alternator (|) character, as follows: var pattern:RegExp = /cat|dog/; // matches cat or dog pattern = /ca(t|d)og/; // matches catog or cadog Using groups to capture substring matches When you define a standard parenthetical group in a pattern, you can later refer to it in the regular expression. This is known as a backreference, and these sorts of groups are known as capturing groups. For example, in the following regular expression, the sequence 1 matches whatever substring matched the capturing parenthetical group: var pattern:RegExp = /(d+)-by-1/; // matches the following: 48-by-48 You can specify up to 99 of these backreferences in a regular expression by typing 1, 2, ... , 99.
  • 299. Regular expression syntax 299 Similarly, in the replace() method of the String class, you can use $1–$99 to insert captured group substring matches in the replacement string: var pattern:RegExp = /Hi, (w+)./; var str:String = "Hi, Bob."; trace(str.replace(pattern, "$1, hello.")); // output: Bob, hello. Also, if you use capturing groups, the exec() method of the RegExp class and the match() method of the String class return substrings that match the capturing groups: var pattern:RegExp = /(w+)@(w+).(w+)/; var str:String = "[email protected]"; trace(pattern.exec(str)); // [email protected],bob,example,com Using noncapturing groups and lookahead groups A noncapturing group is one that is used for grouping only; it is not “collected,” and it does not match numbered backreferences. Use (?: and ) to define noncapturing groups, as follows: var pattern = /(?:com|org|net); For example, note the difference between putting (com|org) in a capturing versus a noncapturing group (the exec() method lists capturing groups after the complete match): var pattern:RegExp = /(w+)@(w+).(com|org)/; var str:String = "[email protected]"; trace(pattern.exec(str)); // [email protected],bob,example,com //noncapturing: var pattern:RegExp = /(w+)@(w+).(?:com|org)/; var str:String = "[email protected]"; trace(pattern.exec(str)); // [email protected],bob,example A special type of noncapturing group is the lookahead group, of which there are two types: the positive lookahead group and the negative lookahead group. Use (?= and ) to define a positive lookahead group, which specifies that the subpattern in the group must match at the position. However, the portion of the string that matches the positive lookahead group can match remaining patterns in the regular expression. For example, because (?=e) is a positive lookahead group in the following code, the character e that it matches can be matched by a subsequent part of the regular expression—in this case, the capturing group, w*):
  • 300. 300 Using regular expressions var pattern:RegExp = /sh(?=e)(w*)/i; var str:String = "Shelly sells seashells by the seashore"; trace(pattern.exec(str)); // Shelly,elly Use (?! and ) to define a negative lookahead group that specifies that the subpattern in the group must not match at the position. For example: var pattern:RegExp = /sh(?!e)(w*)/i; var str:String = "She sells seashells by the seashore"; trace(pattern.exec(str)); // shore,ore Using named groups A named group is a type of group in a regular expression that is given a named identifier. Use (?P<name> and ) to define the named group. For example, the following regular expression includes a named group with the identifier named digits: var pattern = /[a-z]+(?P<digits>d+)[a-z]+/; When you use the exec() method, a matching named group is added as a property of the result array: var myPattern:RegExp = /([a-z]+)(?P<digits>d+)[a-z]+/; var str:String = "a123bcd"; var result:Array = myPattern.exec(str); trace(result.digits); // 123 Here is another example, which uses two named groups, with the identifiers name and dom: var emailPattern:RegExp = /(?P<name>(w|[_.-])+)@(?P<dom>((w|-)+))+.w{2,4}+/; var address:String = "[email protected]"; var result:Array = emailPattern.exec(address); trace(result.name); // bob trace(result.dom); // example NOTE Named groups are not part of the ECMAScript language specification. They are an added feature in ActionScript 3.0.
  • 301. Regular expression syntax 301 Flags and properties The following table lists the five flags that you can set for regular expressions. Each flag can be accessed as a property of the regular expression object. Note that these properties are read-only. You can set the flags (g, i, m, s, x) when you set a regular expression variable, as follows: var re:RegExp = /abc/gimsx; However, you cannot directly set the named properties. For instance, the following code results in an error: var re:RegExp = /abc/; re.global = true; // This generates an error. By default, unless you specify them in the regular expression declaration, the flags are not set, and the corresponding properties are also set to false. Additionally, there are two other properties of a regular expression: ■ The lastIndex property specifies the index position in the string to use for the next call to the exec() or test() method of a regular expression. ■ The source property specifies the string that defines the pattern portion of the regular expression. The g (global) flag When the g (global) flag is not included, a regular expression matches no more than one match. For example, with the g flag not included in the regular expression, the String.match() method returns only one matching substring: var str:String = "she sells seashells by the seashore."; var pattern:RegExp = /shw*/; trace(str.match(pattern)) // output: she Flag Property Description g global Matches more than one match. i ignoreCase Case-insensitive matching. Applies to the A—Z and a—z characters, but not to extended characters such as É and é. m multiline With this flag set, $ and ^ can match the beginning of a line and end of a line, respectively. s dotall With this flag set, . (dot) can match the newline character (n). x extended Allows extended regular expressions. You can type spaces in the regular expression, which are ignored as part of the pattern. This lets you type regular expression code more legibly.
  • 302. 302 Using regular expressions When the g flag is set, the Sting.match() method returns multiple matches, as follows: var str:String = "she sells seashells by the seashore."; var pattern:RegExp = /shw*/g; // The same pattern, but this time the g flag IS set. trace(str.match(pattern)); // output: she,shells,shore The i (ignoreCase) flag By default, regular expression matches are case-sensitive. When you set the i (ignoreCase) flag, case sensitivity is ignored. For example, the lowercase s in the regular expression does not match the uppercase letter S, the first character of the string: var str:String = "She sells seashells by the seashore."; trace(str.search(/sh/)); // output: 13 -- Not the first character With the i flag set, however, the regular expression does match the capital letter S: var str:String = "She sells seashells by the seashore."; trace(str.search(/sh/i)); // output: 0 The i flag ignores case sensitivity only for the A–Z and a–z characters, but not for extended characters such as É and é. The m (multiline) flag If the m (multiline) flag is not set, the ^ matches the beginning of the string and the $ matches the end of the string. If the m flag is set, these characters match the beginning of a line and end of a line, respectively. Consider the following string, which includes a newline character: var str:String = "Testn"; str += "Multiline"; trace(str.match(/^w*/g)); // Match a word at the beginning of the string. Even though the g (global) flag is set in the regular expression, the match() method matches only one substring, since there is only one match for the ^—the beginning of the string. The output is: Test Here is the same code with the m flag set: var str:String = "Testn"; str += "Multiline"; trace(str.match(/^w*/gm)); // Match a word at the beginning of lines. This time, the output includes the words at the beginning of both lines: Test,Multiline
  • 303. Regular expression syntax 303 Note that only the n character signals the end of a line. The following characters do not: ■ Return (r) character ■ Unicode line-separator (u2028) character ■ Unicode paragraph-separator (u2029) character The s (dotall) flag If the s (dotall or “dot all”) flag is not set, a dot (.) in a regular expression pattern does not match a newline character (n). So for the following example, there is no match: var str:String = "<p>Testn"; str += "Multiline</p>"; var re:RegExp = /<p>.*?</p>/; trace(str.match(re)); However, if the s flag is set, the dot matches the newline character: var str:String = "<p>Testn"; str += "Multiline</p>"; var re:RegExp = /<p>.*?</p>/s; trace(str.match(re)); In this case, the match is the entire substring within the <p> tags, including the newline character: <p>Test Multiline</p> The x (extended) flag Regular expressions can be difficult to read, especially when they include a lot of metasymbols and metasequences. For example: /<p(>|(s*[^>]*>)).*?</p>/gi When you use the x (extended) flag in a regular expression, any blank spaces that you type in the pattern are ignored. For example, the following regular expression is identical to the previous example: / <p (> | (s* [^>]* >)) .*? </p> /gix If you have the x flag set and do want to match a blank space character, precede the blank space with a backslash. For example, the following two regular expressions are equivalent: /foo bar/ /foo bar/x
  • 304. 304 Using regular expressions The lastIndex property The lastIndex property specifies the index position in the string at which to start the next search. This property affects the exec() and test() methods called on a regular expression that has the g flag set to true. For example, consider the following code: var pattern:RegExp = /pw*/gi; var str:String = "Pedro Piper picked a peck of pickled peppers."; trace(pattern.lastIndex); var result:Object = pattern.exec(str); while (result != null) { trace(pattern.lastIndex); result = pattern.exec(str); } The lastIndex property is set to 0 by default (to start searches at the beginning of the string). After each match, it is set to the index position following the match. Therefore, the output for the preceding code is the following: 0 5 11 18 25 36 44 If the global flag is set to false, the exec() and test() methods do not use or set the lastIndex property. The match(), replace(), and search() methods of the String class start all searches from the beginning of the string, regardless of the setting of the lastIndex property of the regular expression used in the call to the method. (However, the match() method does set lastIndex to 0.) You can set the lastIndex property to adjust the starting position in the string for regular expression matching. The source property The source property specifies the string that defines the pattern portion of a regular expression. For example: var pattern:RegExp = /foo/gi; trace(pattern.source); // foo
  • 305. Methods for using regular expressions with strings 305 Methods for using regular expressions with strings The RegExp class includes two methods: exec() and test(). In addition to the exec() and test() methods of the RegExp class, the String class includes the following methods that let you match regular expressions in strings: match(), replace(), search(), and splice(). The test() method The test() method of the RegExp class simply checks the supplied string to see if it contains a match for the regular expression, as the following example shows: var pattern:RegExp = /Class-w/; var str = "Class-A"; trace(pattern.test(str)); // output: true The exec() method The exec() method of the RegExp class checks the supplied string for a match of the regular expression and returns an array with the following: ■ The matching substring ■ Substring matches for any parenthetical groups in the regular expression The array also includes an index property, indicating the index position of the start of the substring match. For example, consider the following code: var pattern:RegExp = /d{3}-d{3}-d{4}/; //U.S phone number var str:String = "phone: 415-555-1212"; var result:Array = pattern.exec(str); trace(result.index, " - ", result); // 7 - 415-555-1212 Use the exec() method multiple times to match multiple substrings when the g (global) flag is set for the regular expression: var pattern:RegExp = /w*shw*/gi; var str:String = "She sells seashells by the seashore"; var result:Array = pattern.exec(str); while (result != null) {
  • 306. 306 Using regular expressions trace(result.index, "t", pattern.lastIndex, "t", result); result = pattern.exec(str); } //output: // 0 3 She // 10 19 seashells // 27 35 seashore String methods that use RegExp parameters The following methods of the String class take regular expressions as parameters: match(), replace(), search(), and split(). For more information on these methods, see “Finding patterns in strings and replacing substrings” on page 212. Example: A Wiki parser This simple Wiki text conversion example illustrates a number of uses for regular expressions: ■ Converting lines of text that match a source Wiki pattern to the appropriate HTML output strings. ■ Using a regular expression to convert URL patterns to HTML <a> hyperlink tags. ■ Using a regular expression to convert U.S. dollar strings (such as "$9.95") to euro strings (such as "8.24 €"). To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The WikiEditor application files can be found in the folder Samples/WikiEditor. The application consists of the following files: File Description WikiEditor.mxml or WikiEditor.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/ regExpExamples/WikiParser.as A class that includes methods that use regular expressions to convert Wiki input text patterns to the equivalent HTML output. com/example/programmingas3/ regExpExamples/URLParser.as A class that includes methods that use regular expressions to convert URL strings to HTML <a> hyperlink tags. com/example/programmingas3/ regExpExamples/CurrencyConverter.as A class that includes methods that use regular expressions to convert U.S. dollar strings to euro strings.
  • 307. Example: A Wiki parser 307 Defining the WikiParser class The WikiParser class includes methods that convert Wiki input text into the equivalent HTML output. This is not a very robust Wiki conversion application, but it does illustrate some good uses of regular expressions for pattern matching and string conversion. The constructor function, along with the setWikiData() method, simply initializes a sample string of Wiki input text, as follows: public function WikiParser() { wikiData = setWikiData(); } When the user clicks the Test button in the sample application, the application invokes the parseWikiString() method of the WikiParser object. This method calls a number of other methods, which in turn assemble the resulting HTML string. public function parseWikiString(wikiString:String):String { var result:String = parseBold(wikiString); result = parseItalic(result); result = linesToParagraphs(result); result = parseBullets(result); return result; } Each of the methods called—parseBold(), parseItalic(), linesToParagraphs(), and parseBullets()—uses the replace() method of the string to replace matching patterns, defined by a regular expression, in order to transform the input Wiki text into HTML- formatted text. Converting boldface and italic patterns The parseBold() method looks for a Wiki boldface text pattern (such as '''foo''') and transforms it into its HTML equivalent (such as <b>foo</b>), as follows: private function parseBold(input:String):String { var pattern:RegExp = /'''(.*?)'''/g; return input.replace(pattern, "<b>$1</b>"); } Note that the (.?*) portion of the regular expression matches any number of characters (*) between the two defining ''' patterns. The ? quantifier makes the match nongreedy, so that for a string such as '''aaa''' bbb '''ccc''', the first matched string will be '''aaa''' and not the entire string (which starts and ends with the ''' pattern).
  • 308. 308 Using regular expressions The parentheses in the regular expression define a capturing group, and the replace() method refers to this group by using the $1 code in the replacement string. The g (global) flag in the regular expression ensures that the replace() method replaces all matches in the string (not simply the first one). The parseItalic() method works similarly to the parseBold() method, except that it checks for two apostrophes ('') as the delimiter for italic text (not three): private function parseItalic(input:String):String { var pattern:RegExp = /''(.*?)''/g; return input.replace(pattern, "<i>$1</i>"); } Converting bullet patterns As the following example shows, the parseBullet() method looks for the Wiki bullet line pattern (such as * foo) and transforms it into its HTML equivalent (such as <li>foo</li>): private function parseBullets(input:String):String { var pattern:RegExp = /^*(.*)/gm; return input.replace(pattern, "<li>$1</li>"); } The ^ symbol at the beginning of the regular expression matches the beginning of a line. The m (multiline) flag in the regular expression causes the regular expression to match the ^ symbol against the start of a line, not simply the start of the string. The * pattern matches an asterisk character (the backslash is used to signal a literal asterisk instead of a * quantifier). The parentheses in the regular expression define a capturing group, and the replace() method refers to this group by using the $1 code in the replacement string. The g (global) flag in the regular expression ensures that the replace() method replaces all matches in the string (not simply the first one). Converting paragraph Wiki patterns The linesToParagraphs() method converts each line in the input Wiki string to an HTML <p> paragraph tag. These lines in the method strip out empty lines from the input Wiki string: var pattern:RegExp = /^$/gm; var result:String = input.replace(pattern, "");
  • 309. Example: A Wiki parser 309 The ^ and $ symbols the regular expression match the beginning and end of a line. The m (multiline) flag in the regular expression causes the regular expression to match the ^ symbol against the start of a line, not simply the start of the string. The replace() method replaces all matching substrings (empty lines) with an empty string (""). The g (global) flag in the regular expression ensures that the replace() method replaces all matches in the string (not simply the first one). Converting URLs to HTML <a> tags When the user clicks the Test button in the sample application, if the user selected the urlToATag check box, the application calls the URLParser.urlToATag() static method to convert URL strings from the input Wiki string into HTML <a> tags. var protocol:String = "((?:http|ftp)://)"; var urlPart:String = "([a-z0-9_-]+.[a-z0-9_-]+)"; var optionalUrlPart:String = "(.[a-z0-9_-]*)"; var urlPattern:RegExp = new RegExp(protocol + urlPart + optionalUrlPart, "ig"); var result:String = input.replace(urlPattern, "<a href='$1$2$3'><u>$1$2$3</u></a>"); The RegExp() constructor function is used to assemble a regular expression (urlPattern) from a number of constituent parts. These constituent parts are each strings that define part of the regular expression pattern. The first part of the regular expression pattern, defined by the protocol string, defines an URL protocol: either http:// or ftp://. The parentheses define a noncapturing group, indicated by the ? symbol. This means that the parentheses are simply used to define a group for the | alternation pattern; the group will not match backreference codes ($1, $2, $3) in the replacement string of the replace() method. The other constituent parts of the regular expression each use capturing groups (indicated by parentheses in the pattern), which are then used in the backreference codes ($1, $2, $3) in the replacement string of the replace() method. The part of the pattern defined by the urlPart string matches at least one of the following characters: a-z, 0-9, _, or -. The + quantifier indicates that at least one character is matched. The . indicates a required dot (.) character. And the remainder matches another string of at least one of these characters: a-z, 0-9, _, or -. The part of the pattern defined by the optionalUrlPart string matches zero or more of the following: a dot (.) character followed by any number of alphanumeric characters (including _ and -). The * quantifier indicates that zero or more characters are matched.
  • 310. 310 Using regular expressions The call to the replace() method employs the regular expression and assembles the replacement HTML string, using backreferences. The urlToATag() method then calls the emailToATag() method, which uses similar techniques to replace e-mail patterns with HTML <a> hyperlink strings. The regular expressions used to match HTTP, FTP, and e-mail URLs in this sample file are fairly simple, for the purposes of exemplification; there are much more complicated regular expressions for matching such URLs more correctly. Converting U.S. dollar strings to euro strings When the user clicks the Test button in the sample application, if the user selected the dollarToEuro check box, the application calls the CurrencyConverter.usdToEuro() static method to convert U.S. dollar strings (such as "$9.95") to euro strings (such as "8.24 €"), as follows: var usdPrice:RegExp = /$([d,]+.d+)+/g; return input.replace(usdPrice, usdStrToEuroStr); The first line defines a simple pattern for matching U.S. dollar strings. Notice that the $ character is preceded with the backslash () escape character. The replace() method uses the regular expression as the pattern matcher, and it calls the usdStrToEuroStr() function to determine the replacement string (a value in euros). When a function name is used as the second parameter of the replace() method, the following are passed as parameters to the called function: ■ The matching portion of the string. ■ Any captured parenthetical group matches. The number of arguments passed this way varies depending on the number of captured parenthetical group matches. You can determine the number of captured parenthetical group matches by checking arguments.length - 3 within the function code. ■ The index position in the string where the match begins. ■ The complete string. The usdStrToEuroStr() method converts U.S. dollar string patterns to euro strings, as follows: private function usdToEuro(...args):String { var usd:String = args[1]; usd = usd.replace(",", ""); var exchangeRate:Number = 0.828017; var euro:Number = Number(usd) * exchangeRate; trace(usd, Number(usd), euro);
  • 311. Example: A Wiki parser 311 const euroSymbol:String = String.fromCharCode(8364); // € return euro.toFixed(2) + " " + euroSymbol; } Note that args[1] represents the captured parenthetical group matched by the usdPrice regular expression. This is the numerical portion of the U.S. dollar string: that is, the dollar amount without the $ sign. The method applies an exchange rate conversion and returns the resulting string (with a trailing € symbol instead of a leading $ symbol).
  • 312. 312 Using regular expressions
  • 313. 313 10 CHAPTER 10 Handling events An event-handling system allows programmers to respond to user input and system events in a convenient way. The ActionScript 3.0 event model is not only convenient, but also standards-compliant, and well integrated with the Adobe Flash Player 9 display list. Based on the Document Object Model (DOM) Level 3 Events Specification, an industry-standard event-handling architecture, the new event model provides a powerful yet intuitive event- handling tool for ActionScript programmers. This chapter is organized in five sections. The first two sections provide background information about event handling in ActionScript. The last three sections describe the main concepts behind the event model: the event flow, the event object, and event listeners. The ActionScript 3.0 event-handling system interacts closely with the display list, and this chapter assumes that you have a basic understanding of the display list. For more information, see “Display programming” on page 371. Contents Basics of handling events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 How ActionScript 3.0 event handling differs from earlier versions. . . . . . . . . . . . . 317 The event flow. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 Event objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .322 Event listeners. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327 Example: Alarm Clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .335
  • 314. 314 Handling events Basics of handling events Introduction to handling events You can think of events as occurrences of any kind in your SWF file that are of interest to you as a programmer. For example, most SWF files support user interaction of some sort— whether it's something as simple as responding to a mouse click or something more complex, such as accepting and processing data entered into a form. Any such user interaction with your SWF file is considered an event. Events can also occur without any direct user interaction, such as when data has finished loading from a server or when an attached camera has become active. In ActionScript 3.0, each event is represented by an event object, which is an instance of the Event class or one of its subclasses. An event object not only stores information about a specific event, but also contains methods that facilitate manipulation of the event object. For example, when Flash Player detects a mouse click, it creates an event object (an instance of the MouseEvent class) to represent that particular mouse click event. After creating an event object, Flash Player dispatches it, which means that the event object is passed to the object that is the target of the event. An object that serves as the destination for a dispatched event object is called an event target. For example, when an attached camera becomes active, Flash Player dispatches an event object directly to the event target, which in this case is the object that represents the camera. If the event target is on the display list, however, the event object is passed down through the display list hierarchy until it reaches the event target. In some cases, the event object then “bubbles” back up the display list hierarchy along the same route. This traversal of the display list hierarchy is called the event flow. You can “listen” for event objects in your code using event listeners. Event listeners are the functions or methods that you write to respond to specific events. To ensure that your program responds to events, you must add event listeners either to the event target or to any display list object that is part of an event object’s event flow. Any time you write event listener code, it follows this basic structure (elements in bold are placeholders you’d fill in for your specific case): function eventResponse(eventObject:EventType):void { // Actions performed in response to the event go here. } eventTarget.addEventListener(EventType.EVENT_NAME, eventResponse);
  • 315. Basics of handling events 315 This code does two things. First, it defines a function, which is the way to specify the actions that will be performed in response to the event. Next, it calls the addEventListener() method of the source object, in essence “subscribing” the function to the specified event so that when the event happens, the function’s actions are carried out. When the event actually happens, the event target checks its list of all the functions and methods that are registered as event listeners. It then calls each one in turn, passing the event object as a parameter. You need to alter four things in this code to create your own event listener. First, you must change the name of the function to the name you want to use (this must be changed in two places, where the code says eventResponse). Second, you must specify the appropriate class name of the event object that is dispatched by the event you want to listen for (EventType in the code), and you must specify the appropriate constant for the specific event (EVENT_NAME in the listing). Third, you must call the addEventListener() method on the object that will dispatch the event (eventTarget in this code). Optionally, you can change the name of the variable used as the function’s parameter (eventObject in this code). Common event-handling tasks The following are common event-handling tasks, each of which is described in this chapter: ■ Writing code to respond to events ■ Stopping code from responding to events ■ Working with event objects ■ Working with event flow: ■ Identifying event flow information ■ Stopping event flow ■ Preventing default behavior ■ Dispatching events from your classes ■ Creating a custom event type Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Default behavior: Some events include a behavior that normally happens along with the event, known as the default behavior. For example, when a user types text in a text field, a text input event is raised. The default behavior for that event is to actually display the character that was typed into the text field—but you can override that default behavior (if for some reason you don’t want the typed character to be displayed). ■ Dispatch: To notify event listeners that an event has occurred.
  • 316. 316 Handling events ■ Event: Something that happens to an object that the object can tell other objects about. ■ Event flow: When events happen to an object on the display list (an object displayed on the screen), all the objects that contain the object are notified of the event and notify their event listeners in turn. This process starts with the Stage and proceeds through the display list to the actual object where the event occurred, and then proceeds back to the Stage again. This process is known as the event flow. ■ Event object: An object that contains information about a particular event’s occurrence, which is sent to all listeners when an event is dispatched. ■ Event target: The object that actually dispatches an event. For example, if the user clicks a button that is inside a Sprite that is in turn inside the Stage, all those objects dispatch events, but the event target is the one where the event actually happened—in this case, the clicked button. ■ Listener: An object or function that has registered itself with an object, to indicate that it should be notified when a specific event takes place. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Essentially, all the code listings in this chapter include a trace() function call for testing the results of the code. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the code listing’s trace() functions in the Output panel. Some of the code listings are more complex and are written as a class. To test these examples: 1. Create an empty Flash document and save it to your computer. 2. Create a new ActionScript file and save it in the same directory as the Flash document. The file’s name should match the name of the class in the code listing. For instance, if the code listing defines a class named EventTest, use the name EventTest.as to save the ActionScript file. 3. Copy the code listing into the ActionScript file and save the file. 4. In the Flash document, click a blank part of the Stage or work space to activate the document Property inspector.
  • 317. How ActionScript 3.0 event handling differs from earlier versions 317 5. In the Property inspector, in the Document Class field, enter the name of the ActionScript class you copied from the text. 6. Run the program using Control > Test Movie You will see the results of the example in the Output panel. These techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63. How ActionScript 3.0 event handling differs from earlier versions The most noticeable difference between event handling in ActionScript 3.0 and event handling in previous versions of ActionScript is that in ActionScript 3.0 there is only one system for event handling, whereas in previous versions of ActionScript there are several different event-handling systems. This section begins with an overview of how event handling worked in previous versions of ActionScript, and then discusses how event handling has changed for ActionScript 3.0. Event handling in previous versions of ActionScript Versions of ActionScript before ActionScript 3.0 provided a number of different ways to handle events: ■ on() event handlers that can be placed directly on Button and MovieClip instances ■ onClipEvent() handlers that can be placed directly on MovieClip instances ■ Callback function properties, such as XML.onload and Camera.onActivity ■ Event listeners that you register using the addListener() method ■ The UIEventDispatcher class that partially implemented the DOM event model. Each of these mechanisms presents its own set of advantages and limitations. The on() and onClipEvent() handlers are easy to use, but make subsequent maintenance of projects more difficult because code placed directly on buttons and movie clips can be difficult to find. Callback functions are also simple to implement, but limit you to only one callback function for any given event. Event listeners are more difficult to implement—they require not only the creation of a listener object and function, but also the registration of the listener with the object that generates the event. This increased overhead, however, enables you to create several listener objects and register them all for the same event.
  • 318. 318 Handling events The development of components for ActionScript 2.0 engendered yet another event model. This new model, embodied in the UIEventDispatcher class, was based on a subset of the DOM Events Specification. Developers who are familiar with component event handling will find the transition to the new ActionScript 3.0 event model relatively painless. Unfortunately, the syntax used by the various event models overlap in various ways, and differ in others. For example, in ActionScript 2.0, some properties, such as TextField.onChanged, can be used as either a callback function or an event listener. However, the syntax for registering listener objects differs depending on whether you are using one of the six classes that support listeners or the UIEventDispatcher class. For the Key, Mouse, MovieClipLoader, Selection, Stage, and TextField classes, you use the addListener() method, but for components event handling, you use a method called addEventListener(). Another complexity introduced by the different event-handling models was that the scope of the event handler function varied widely depending on the mechanism used. In other words, the meaning of the this keyword was not consistent among the event-handling systems. Event handling in ActionScript 3.0 ActionScript 3.0 introduces a single event-handling model that replaces the many different event-handling mechanisms that existed in previous versions of the language. The new event model is based on the Document Object Model (DOM) Level 3 Events Specification. Although the SWF file format does not adhere specifically to the Document Object Model standard, there are sufficient similarities between the display list and the structure of the DOM to make implementation of the DOM event model possible. An object on the display list is analogous to a node in the DOM hierarchical structure, and the terms display list object and node are used interchangeably throughout this discussion. The Flash Player implementation of the DOM event model includes a concept named default behaviors. A default behavior is an action that Flash Player executes as the normal consequence of certain events. Default behaviors Developers are usually responsible for writing code that responds to events. In some cases, however, a behavior is so commonly associated with an event that Flash Player automatically executes the behavior unless the developer adds code to cancel it. Because Flash Player automatically exhibits the behavior, such behaviors are called default behaviors.
  • 319. How ActionScript 3.0 event handling differs from earlier versions 319 For example, when a user enters text into a TextField object, the expectation that the text will be displayed in that TextField object is so common that the behavior is built into Flash Player. If you do not want this default behavior to occur, you can cancel it using the new event- handling system. When a user inputs text into a TextField object, Flash Player creates an instance of the TextEvent class to represent that user input. To prevent Flash Player from displaying the text in the TextField object, you must access that specific TextEvent instance and call that instance’s preventDefault() method. Not all default behaviors can be prevented. For example, Flash Player generates a MouseEvent object when a user double-clicks a word in a TextField object. The default behavior, which cannot be prevented, is that the word under the cursor is highlighted. Many types of event objects do not have associated default behaviors. For example, Flash Player dispatches a connect event object when a network connection is established, but there is no default behavior associated with it. The API documentation for the Event class and its subclasses lists each type of event and describes any associated default behavior, and whether that behavior can be prevented. It is important to understand that default behaviors are associated only with event objects dispatched by Flash Player, and do not exist for event objects dispatched programmatically through ActionScript. For example, you can use the methods of the EventDispatcher class to dispatch an event object of type textInput, but that event object will not have a default behavior associated with it. In other words, Flash Player will not display a character in a TextField object as a result of a textInput event that you dispatched programmatically. What’s new for event listeners in ActionScript 3.0 For developers with experience using the ActionScript 2.0 addListener() method, it may be helpful to point out the differences between the ActionScript 2.0 event listener model and the ActionScript 3.0 event model. The following list describes a few major differences between the two event models: ■ To add event listeners in ActionScript 2.0, you use addListener() in some cases and addEventListener() in others, whereas in ActionScript 3.0, you use addEventListener() in all situations. ■ There is no event flow in ActionScript 2.0, which means that the addListener() method can be called only on the object that broadcasts the event, whereas in ActionScript 3.0, the addEventListener() method can be called on any object that is part of the event flow. ■ In ActionScript 2.0, event listeners can be either functions, methods, or objects, whereas in ActionScript 3.0, only functions or methods can be event listeners.
  • 320. 320 Handling events The event flow Flash Player dispatches event objects whenever an event occurs. If the event target is not on the display list, Flash Player dispatches the event object directly to the event target. For example, Flash Player dispatches the progress event object directly to a URLStream object. If the event target is on the display list, however, Flash Player dispatches the event object into the display list, and the event object travels through the display list to the event target. The event flow describes how an event object moves through the display list. The display list is organized in a hierarchy that can be described as a tree. At the top of the display list hierarchy is the Stage, which is a special display object container that serves as the root of the display list. The Stage is represented by the flash.display.Stage class and can only be accessed through a display object. Every display object has a property named stage that refers to the Stage for that application. When Flash Player dispatches an event object, that event object makes a roundtrip journey from the Stage to the target node. The DOM Events Specification defines the target node as the node representing the event target. In other words, the target node is the display list object where the event occurred. For example, if a user clicks on a display list object named child1, Flash Player will dispatch an event object using child1 as the target node. The event flow is conceptually divided into three parts. The first part is called the capture phase; this phase comprises all of the nodes from the Stage to the parent of the target node. The second part is called the target phase, which consists solely of the target node. The third part is called the bubbling phase. The bubbling phase comprises the nodes encountered on the return trip from the parent of the target node back to the Stage. The names of the phases make more sense if you conceive of the display list as a vertical hierarchy with the Stage at the top, as shown in the following diagram: Stage Parent Node Child1 Node Child2 Node
  • 321. The event flow 321 If a user clicks on Child1 Node, Flash Player dispatches an event object into the event flow. As the following image shows, the object’s journey starts at Stage, moves down to Parent Node, then moves to Child1 Node, and then “bubbles” back up to Stage, moving through Parent Node again on its journey back to Stage. In this example, the capture phase comprises Stage and Parent Node during the initial downward journey. The target phase comprises the time spent at Child1 Node. The bubbling phase comprises Parent Node and Stage as they are encountered during the upward journey back to the root node. The event flow contributes to a more powerful event-handling system than that previously available to ActionScript programmers. In previous versions of ActionScript, the event flow does not exist, which means that event listeners can be added only to the object that generates the event. In ActionScript 3.0, you can add event listeners not only to a target node, but also to any node along the event flow. The ability to add event listeners along the event flow is useful when a user interface component comprises more than one object. For example, a button object often contains a text object that serves as the button’s label. Without the ability to add a listener to the event flow, you would have to add a listener to both the button object and the text object to ensure that you receive notification about click events that occur anywhere on the button. The existence of the event flow, however, allows you to place a single event listener on the button object that handles click events that occur either on the text object or on the areas of the button object that are not obscured by the text object. Not every event object, however, participates in all three phases of the event flow. Some types of events, such as the enterFrame and init event types, are dispatched directly to the target node and participate in neither the capture phase nor the bubbling phase. Other events may target objects that are not on the display list, such as events dispatched to an instance of the Socket class. These event objects will also flow directly to the target object, without participating in the capture and bubbling phases. Stage Parent Node Child1 Node Child2 Node Capture Phase Bubbling Phase Target Phase
  • 322. 322 Handling events To find out how a particular event type behaves, you can either check the API documentation or examine the event object's properties. Examining the event object’s properties is described in the following section. Event objects Event objects serve two main purposes in the new event-handling system. First, event objects represent actual events by storing information about specific events in a set of properties. Second, event objects contain a set of methods that allow you to manipulate event objects and affect the behavior of the event-handling system. To facilitate access to these properties and methods, the Flash Player API defines an Event class that serves as the base class for all event objects. The Event class defines a fundamental set of properties and methods that are common to all event objects. This section begins with a discussion of the Event class properties, continues with a description of the Event class methods, and concludes with an explanation of why subclasses of the Event class exist. Understanding Event class properties The Event class defines a number of read-only properties and constants that provide important information about an event object.The following are especially important: ■ Event object types are represented by constants and stored in the Event.type property. ■ Whether an event’s default behavior can be prevented is represented by a Boolean value and stored in the Event.cancelable property. ■ Event flow information is contained in the remaining properties. Event object types Every event object has an associated event type. Event types are stored in the Event.type property as string values. It is useful to know the type of an event object so that your code can distinguish objects of different types from one another. For example, the following code specifies that the clickHandler() listener function should respond to any mouse click event objects that are passed to myDisplayObject: myDisplayObject.addEventListener(MouseEvent.CLICK, clickHandler); Some two dozen event types are associated with the Event class itself and are represented by Event class constants, some of which are shown in the following excerpt from the Event class definition:
  • 323. Event objects 323 package flash.events { public class Event { // class constants public static const ACTIVATE:String = "activate"; public static const ADDED:String = "added"; // remaining constants omitted for brevity } } These constants provide an easy way to refer to specific event types. You should use these constants instead of the strings they represent. If you misspell a constant name in your code, the compiler will catch the mistake, but if you instead use strings, a typographical error may not manifest at compile time and could lead to unexpected behavior that could be difficult to debug. For example, when adding an event listener, use the following code: myDisplayObject.addEventListener(MouseEvent.CLICK, clickHandler); rather than: myDisplayObject.addEventListener("click", clickHandler); Default behavior information Your code can check whether the default behavior for any given event object can be prevented by accessing the cancelable property. The cancelable property holds a Boolean value that indicates whether or not a default behavior can be prevented. You can prevent, or cancel, the default behavior associated with a small number of events using the preventDefault() method. For more information, see “Cancelling default event behavior” on page 326. Event flow information The remaining Event class properties contain important information about an event object and its relationship to the event flow, as described in the following list: ■ The bubbles property contains information about the parts of the event flow in which the event object participates. ■ The eventPhase property indicates the current phase in the event flow. ■ The target property stores a reference to the event target. ■ The currentTarget property stores a reference to the display list object that is currently processing the event object.
  • 324. 324 Handling events The bubbles property An event is said to bubble if its event object participates in the bubbling phase of the event flow, which means that the event object is passed from the target node back through its ancestors until it reaches the Stage. The Event.bubbles property stores a Boolean value that indicates whether the event object participates in the bubbling phase. Because all events that bubble also participate in the capture and target phases, any event that bubbles participates in all three of the event flow phases. If the value is true, the event object participates in all three phases. If the value is false, the event object does not participate in the bubbling phase. The eventPhase property You can determine the event phase for any event object by investigating its eventPhase property. The eventPhase property contains an unsigned integer value that represents one of the three phases of the event flow. The Flash Player API defines a separate EventPhase class that contains three constants that correspond to the three unsigned integer values, as shown in the following code excerpt: package flash.events { public final class EventPhase { public static const CAPTURING_PHASE:uint = 1; public static const AT_TARGET:uint = 2; public static const BUBBLING_PHASE:uint = 3; } } These constants correspond to the three valid values of the eventPhase property. You can use these constants to make your code more readable. For example, if you want to ensure that a function named myFunc() is called only if the event target is in the target stage, you can use the following code to test for this condition: if (event.eventPhase == EventPhase.AT_TARGET) { myFunc(); } The target property The target property holds a reference to the object that is the target of the event. In some cases, this is straightforward, such as when a microphone becomes active, the target of the event object is the Microphone object. If the target is on the display list, however, the display list hierarchy must be taken into account. For example, if a user inputs a mouse click on a point that includes overlapping display list objects, Flash Player always chooses the object that is farthest away from the Stage as the event target.
  • 325. Event objects 325 For complex SWF files, especially those in which buttons are routinely decorated with smaller child objects, the target property may not be used frequently because it will often point to a button’s child object instead of the button. In these situations, the common practice is to add event listeners to the button and use the currentTarget property because it points to the button, whereas the target property may point to a child of the button. The currentTarget property The currentTarget property contains a reference to the object that is currently processing the event object. Although it may seem odd not to know which node is currently processing the event object that you are examining, keep in mind that you can add a listener function to any display object in that event object's event flow, and the listener function can be placed in any location. Moreover, the same listener function can be added to different display objects. As a project increases in size and complexity, the currentTarget property becomes more and more useful. Understanding Event class methods There are three categories of Event class methods: ■ Utility methods, which can create copies of an event object or convert it to a string ■ Event flow methods, which remove event objects from the event flow ■ Default behavior methods, which prevent default behavior or check whether it has been prevented Event class utility methods There are two utility methods in the Event class. The clone() method allows you to create copies of an event object. The toString() method allows you to generate a string representation of the properties of an event object along with their values. Both of these methods are used internally by the event model system, but are exposed to developers for general use. For advanced developers creating subclasses of the Event class, you must override and implement versions of both utility methods to ensure that the event subclass will work properly.
  • 326. 326 Handling events Stopping event flow You can call either the Event.stopPropogation() method or the Event.stopImmediatePropogation() method to prevent an event object from continuing on its way through the event flow. The two methods are nearly identical and differ only in whether the current node’s other event listeners are allowed to execute: ■ The Event.stopPropogation() method prevents the event object from moving on to the next node, but only after any other event listeners on the current node are allowed to execute. ■ The Event.stopImmediatePropogation() method also prevents the event object from moving on to the next node, but does not allow any other event listeners on the current node to execute. Calling either of these methods has no effect on whether the default behavior associated with an event occurs. Use the default behavior methods of the Event class to prevent default behavior. Cancelling default event behavior The two methods that pertain to cancelling default behavior are the preventDefault() method and the isDefaultPrevented() method. Call the preventDefault() method to cancel the default behavior associated with an event. To check whether preventDefault() has already been called on an event object, call the isDefaultPrevented() method, which returns a value of true if the method has already been called and false otherwise. The preventDefault() method will work only if the event’s default behavior can be cancelled. You can check whether this is the case by referring to the API documentation for that event type, or by using ActionScript to examine the cancelable property of the event object. Cancelling the default behavior has no effect on the progress of an event object through the event flow. Use the event flow methods of the Event class to remove an event object from the event flow.
  • 327. Event listeners 327 Subclasses of the Event class For many events, the common set of properties defined in the Event class is sufficient. Other events, however, have unique characteristics that cannot be captured by the properties available in the Event class. For these events, the Flash Player API defines several subclasses of the Event class. Each subclass provides additional properties and event types that are unique to that category of events. For example, events related to mouse input have several unique characteristics that cannot be captured by the properties defined in the Event class. The MouseEvent class extends the Event class by adding ten properties that contain information such as the location of the mouse event and whether specific keys were pressed during the mouse event. An Event subclass also contains constants that represent the event types that are associated with the subclass. For example, the MouseEvent class defines constants for several mouse event types, include the click, doubleClick, mouseDown, and mouseUp event types. As described in the section “Event class utility methods” on page 325, when creating an Event subclass you must override the clone() and toString() methods to provide functionality specific to the subclass. Event listeners Event listeners, which are also called event handlers, are functions that Flash Player executes in response to specific events. Adding an event listener is a two-step process. First, you create a function or class method for Flash Player to execute in response to the event. This is sometimes called the listener function or the event handler function. Second, you use the addEventListener() method to register your listener function with the target of the event or any display list object that lies along the appropriate event flow. Creating a listener function The creation of listener functions is one area where the ActionScript 3.0 event model deviates from the DOM event model. In the DOM event model, there is a clear distinction between an event listener and a listener function: an event listener is an instance of a class that implements the EventListener interface, whereas a listener function is a method of that class named handleEvent(). In the DOM event model, you register the class instance that contains the listener function rather than the actual listener function.
  • 328. 328 Handling events In the ActionScript 3.0 event model, there is no distinction between an event listener and a listener function. ActionScript 3.0 does not have an EventListener interface, and listener functions can be defined outside a class or as part of a class. Moreover, listener functions do not have to be named handleEvent()—they can be named with any valid identifier. In ActionScript 3.0, you register the name of the actual listener function. Listener function defined outside of a class The following code creates a simple SWF file that displays a red square shape. A listener function named clickHandler(), which is not part of a class, listens for mouse click events on the red square. package { import flash.display.Sprite; public class ClickExample extends Sprite { public function ClickExample() { var child:ChildSprite = new ChildSprite(); addChild(child); } } } import flash.display.Sprite; import flash.events.MouseEvent; class ChildSprite extends Sprite { public function ChildSprite() { graphics.beginFill(0xFF0000); graphics.drawRect(0,0,100,100); graphics.endFill(); addEventListener(MouseEvent.CLICK, clickHandler); } } function clickHandler(event:MouseEvent):void { trace("clickHandler detected an event of type: " + event.type); trace("the this keyword refers to: " + this); } When a user interacts with the resulting SWF file by clicking on the square, Flash Player generates the following trace output:
  • 329. Event listeners 329 clickHandler detected an event of type: click the this keyword refers to: [object global] Notice that the event object is passed as an argument to clickHandler(). This allows your listener function to examine the event object. In this example, you use the event object's type property to ascertain that the event is a click event. The example also checks the value of the this keyword. In this case, this represents the global object, which makes sense because the function is defined outside of any custom class or object. Listener function defined as a class method The following example is identical to the previous example that defines the ClickExample class except that the clickHandler() function is defined as a method of the ChildSprite class: package { import flash.display.Sprite; public class ClickExample extends Sprite { public function ClickExample() { var child:ChildSprite = new ChildSprite(); addChild(child); } } } import flash.display.Sprite; import flash.events.MouseEvent; class ChildSprite extends Sprite { public function ChildSprite() { graphics.beginFill(0xFF0000); graphics.drawRect(0,0,100,100); graphics.endFill(); addEventListener(MouseEvent.CLICK, clickHandler); } private function clickHandler(event:MouseEvent):void { trace("clickHandler detected an event of type: " + event.type); trace("the this keyword refers to: " + this); } }
  • 330. 330 Handling events When a user interacts with the resulting SWF file by clicking on the red square, Flash Player generates the following trace output: clickHandler detected an event of type: click the this keyword refers to: [object ChildSprite] Note that the this keyword refers to the ChildSprite instance named child. This is a change in behavior from ActionScript 2.0. If you used components in ActionScript 2.0, you may remember that when a class method was passed in to UIEventDispatcher.addEventListener(), the scope of the method was bound to the component that broadcast the event instead of the class in which the listener method was defined. In other words, if you used this technique in ActionScript 2.0, the this keyword would refer to the component broadcasting the event instead of the ChildSprite instance. This was a significant issue for some programmers because it meant that they could not access other methods and properties of the class containing the listener method. As a workaround, ActionScript 2.0 programmers could use the mx.util.Delegate class to change the scope of the listener method. This is no longer necessary, however, because ActionScript 3.0 creates a bound method when addEventListener() is called. As a result, the this keyword refers to the ChildSprite instance named child, and the programmer has access to the other methods and properties of the ChildSprite class. Event listener that should not be used There is a third technique in which you create a generic object with a property that points to a dynamically assigned listener function, but it is not recommended. It is discussed here because it was commonly used in ActionScript 2.0, but should not be used in ActionScript 3.0. This technique is not recommended because the this keyword will refer to the global object instead of your listener object. The following example is identical to the previous ClickExample class example, except that the listener function is defined as part of a generic object named myListenerObj: package { import flash.display.Sprite; public class ClickExample extends Sprite { public function ClickExample() { var child:ChildSprite = new ChildSprite(); addChild(child); } } }
  • 331. Event listeners 331 import flash.display.Sprite; import flash.events.MouseEvent; class ChildSprite extends Sprite { public function ChildSprite() { graphics.beginFill(0xFF0000); graphics.drawRect(0,0,100,100); graphics.endFill(); addEventListener(MouseEvent.CLICK, myListenerObj.clickHandler); } } var myListenerObj:Object = new Object(); myListenerObj.clickHandler = function (event:MouseEvent):void { trace("clickHandler detected an event of type: " + event.type); trace("the this keyword refers to: " + this); } The results of the trace will look like this: clickHandler detected an event of type: click the this keyword refers to: [object global] You would expect that this would refer to myListenerObj and that the trace output would be [object Object], but instead it refers to the global object. When you pass in a dynamic property name as an argument to addEventListener(), Flash Player is unable to create a bound method. This is because what you are passing as the listener parameter is nothing more than the memory address of your listener function, and Flash Player has no way to link that memory address with the myListenerObj instance. Managing event listeners You can manage your listener functions using the methods of the IEventDispatcher interface. The IEventDispatcher interface is the ActionScript 3.0 version of the EventTarget interface of the DOM event model. Although the name IEventDispatcher may seem to imply that its main purpose is to send (or dispatch) event objects, the methods of this class are actually used much more frequently to register event listeners, check for event listeners, and remove event listeners. The IEventDispatcher interface defines five methods, as shown in the following code: package flash.events { public interface IEventDispatcher {
  • 332. 332 Handling events function addEventListener(eventName:String, listener:Object, useCapture:Boolean=false, priority:Integer=0, useWeakReference:Boolean=false):Boolean; function removeEventListener(eventName:String, listener:Object, useCapture:Boolean=false):Boolean; function dispatchEvent(eventObject:Event):Boolean; function hasEventListener(eventName:String):Boolean; function willTrigger(eventName:String):Boolean; } } The Flash Player API implements the IEventDispatcher interface with the EventDispatcher class, which serves as a base class for all classes that can be event targets or part of an event flow. For example, the DisplayObject class inherits from the EventDispatcher class. This means that any object on the display list has access to the methods of the IEventDispatcher interface. Adding event listeners The addEventListener() method is the workhorse of the IEventDispatcher interface. You use it to register your listener functions. The two required parameters are type and listener. You use the type parameter to specify the type of event. You use the listener parameter to specify the listener function that will execute when the event occurs. The listener parameter can be a reference to either a function or a class method. The useCapture parameter of the addEventListener() method allows you to control the event flow phase on which your listener will be active. If useCapture is set to true, your listener will be active during the capture phase of the event flow. If useCapture is set to false, your listener will be active during the target and bubbling phases of the event flow. To listen for an event during all phases of the event flow, you must call addEventListener() twice, once with useCapture set to true, and then again with useCapture set to false. NOTE Do not use parentheses when you specify the listener parameter. For example, the clickHandler() function is specified without parentheses in the following call to the addEventListener() method: addEventListener(MouseEvent.CLICK, clickHandler).
  • 333. Event listeners 333 The priority parameter of the addEventListener() method is not an official part of the DOM Level 3 event model. It is included in ActionScript 3.0 to provide you with more flexibility in organizing your event listeners. When you call addEventListener(), you can set the priority for that event listener by passing an integer value as the priority parameter. The default value is 0, but you can set it to negative or positive integer values. The higher the number, the sooner that event listener will be executed. Event listeners with the same priority are executed in the order that they were added, so the earlier a listener is added, the sooner it will be executed. The useWeakReference parameter allows you to specify whether the reference to the listener function is weak or normal. Setting this parameter to true allows you to avoid situations in which listener functions persist in memory even though they are no longer needed. Flash Player uses a technique called garbage collection to clear objects from memory that are no longer in use. An object is considered no longer in use if no references to it exist. The garbage collector disregards weak references, which means that a listener function that has only a weak reference pointing to it is eligible for garbage collection. One important consequence of this parameter involves working with display objects’ events. Normally, you might expect a display object to be removed from memory when it is removed from the display list. However, if other objects have subscribed as listeners to that display object, with the useWeakReference parameter set to false (the default), the display object will continue to exist in Flash Player’s memory even though it no longer appears on the screen. To work around this issue, either have all the listeners subscribe to the display object with the useWeakReference parameter set to true, or else remove all the event listeners from the display object using the removeEventListener() method. Removing event listeners You can use the removeEventListener() method to remove an event listener that you no longer need. It is a good idea to remove any listeners that will no longer be used. Required parameters include the eventName and listener parameters, which are the same as the required parameters for the addEventListener() method. Recall that you can listen for events during all event phases by calling addEventListener() twice, once with useCapture set to true, and then again with it set to false. To remove both event listeners, you would need to call removeEventListener() twice, once with useCapture set to true, and then again with it set to false.
  • 334. 334 Handling events Dispatching events The dispatchEvent() method can be used by advanced programmers to dispatch a custom event object into the event flow. The only parameter accepted by this method is a reference to an event object, which must be an instance of the Event class or a subclass of the Event class. Once dispatched, the target property of the event object is set to the object on which dispatchEvent() was called. Checking for existing event listeners The final two methods of the IEventDispatcher interface provide useful information about the existence of event listeners. The hasEventListener() method returns true if an event listener is found for a specific event type on a particular display list object. The willTrigger() method also returns true if a listener is found for a particular display list object, but willTrigger() checks for listeners not only on that display object, but also on all of that display list object’s ancestors for all phases of the event flow. Error events without listeners Exceptions, rather than events, are the primary mechanism for error handling in ActionScript 3.0, but exception handling does not work for asynchronous operations such as loading files. If an error occurs during such an asynchronous operation, Flash Player dispatches an error event object. If you do not create a listener for the error event, the debugger version of Flash Player will bring up a dialog box with information about the error. For example, using an invalid URL when loading a file produces this dialog box in the debugger version of Flash Player:
  • 335. Example: Alarm Clock 335 Most error events are based on the ErrorEvent class, and as such will have a property named text that is used to store the error message that Flash Player displays. The two exceptions are the StatusEvent and NetStatusEvent classes. Both of these classes have a level property (StatusEvent.level and NetStatusEvent.info.level). When the value of the level property is "error", these event types are considered to be error events. An error event will not cause a SWF file to stop running. It will manifest only as a dialog box on the debugger versions of the browser plug-ins and stand-alone players, as a message in the output panel in the authoring player, and as an entry in the log file for Adobe Flex Builder 2. It will not manifest at all in the release versions of Flash Player. Example: Alarm Clock The Alarm Clock example consists of a clock that allows the user to specify a time at which an alarm will go off, as well as a message to be displayed at that time. The Alarm Clock example builds on the SimpleClock application from Chapter 5, “Working with dates and times.” Alarm Clock illustrates several aspects of working with events in ActionScript 3.0, including: ■ Listening and responding to an event ■ Notifying listeners of an event ■ Creating a custom event type To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The Alarm Clock application files can be found in the Samples/AlarmClock folder. The application includes these files: File Description AlarmClockApp.mxml or AlarmClockApp.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/clock/ AlarmClock.as A class which extends the SimpleClock class, adding alarm clock functionality. com/example/programmingas3/clock/ AlarmEvent.as A custom event class (a subclass of flash.events.Event) which serves as the event object for the AlarmClock class’s alarm event. com/example/programmingas3/clock/ AnalogClockFace.as Draws a round clock face and hour, minute, and seconds hands based on the time (described in the SimpleClock example). com/example/programmingas3/clock/ SimpleClock.as A clock interface component with simple timekeeping functionality (described in the SimpleClock example).
  • 336. 336 Handling events Alarm Clock overview The primary functionality of the clock in this example, including tracking the time and displaying the clock face, reuses the SimpleClock application code, which is described in “Example: Simple analog clock” on page 199. The AlarmClock class extends the SimpleClock class from that example by adding the functionality required for an alarm clock, including setting the alarm time and providing notification when the alarm “goes off.” Providing notification when something happens is the job that events are made for. The AlarmClock class exposes the Alarm event, which other objects can listen for in order to perform desired actions. In addition, the AlarmClock class uses an instance of the Timer class to determine when to trigger its alarm. Like the AlarmClock class, the Timer class provides an event to notify other objects (an AlarmClock instance, in this case) when a certain amount of time has passed. As with most ActionScript applications, events form an important part of the functionality of the Alarm Clock sample application. Triggering the alarm As mentioned previously, the only functionality that the AlarmClock class actually provides relates to setting and triggering the alarm. The built-in Timer class (flash.utils.Timer) provides a way for a developer to define code that will be executed after a specified amount of time. The AlarmClock class uses a Timer instance to determine when to set off the alarm. import flash.events.TimerEvent; import flash.utils.Timer; /** * The Timer that will be used for the alarm. */ public var alarmTimer:Timer; ... /** * Instantiates a new AlarmClock of a given size. */ public override function initClock(faceSize:Number = 200):void { super.initClock(faceSize); alarmTimer = new Timer(0, 1); alarmTimer.addEventListener(TimerEvent.TIMER, onAlarm); }
  • 337. Example: Alarm Clock 337 The Timer instance defined in the AlarmClock class is named alarmTimer. The initClock() method, which performs necessary setup operations for the AlarmClock instance, does two things with the alarmTimer variable. First, the variable is instantiated with parameters instructing the Timer instance to wait 0 milliseconds and only trigger its timer event one time. After instantiating alarmTimer, the code calls that variable’s addEventListener() method to indicate that it wants to listen to that variable’s timer event. A Timer instance works by dispatching its timer event after a specified amount of time has passed. The AlarmClock class will need to know when the timer event is dispatched in order to set off its own alarm. By calling addEventListener(), the AlarmClock code registers itself as a listener with alarmTimer. The two parameters indicate that the AlarmClock class wants to listen for the timer event (indicated by the constant TimerEvent.TIMER), and that when the event happens, the AlarmClock class’s onAlarm() method should be called in response to the event. In order to actually set the alarm, the AlarmClock class’s setAlarm() method is called, as follows: /** * Sets the time at which the alarm should go off. * @param hour The hour portion of the alarm time. * @param minutes The minutes portion of the alarm time. * @param message The message to display when the alarm goes off. * @return The time at which the alarm will go off. */ public function setAlarm(hour:Number = 0, minutes:Number = 0, message:String = "Alarm!"):Date { this.alarmMessage = message; var now:Date = new Date(); // Create this time on today's date. alarmTime = new Date(now.fullYear, now.month, now.date, hour, minutes); // Determine if the specified time has already passed today. if (alarmTime <= now) { alarmTime.setTime(alarmTime.time + MILLISECONDS_PER_DAY); } // Stop the alarm timer if it's currently set. alarmTimer.reset(); // Calculate how many milliseconds should pass before the alarm should // go off (the difference between the alarm time and now) and set that // value as the delay for the alarm timer. alarmTimer.delay = Math.max(1000, alarmTime.time - now.time); alarmTimer.start();
  • 338. 338 Handling events return alarmTime; } This method does several things, including storing the alarm message and creating a Date object (alarmTime) representing the actual moment in time when the alarm is to go off. Of most relevance to the current discussion, in the final several lines of the method, the alarmTimer variable’s timer is set and activated. First, its reset() method is called, stopping the timer and resetting it in case it is already running. Next, the current time (represented by the now variable) is subtracted from the alarmTime variable’s value to determine how many milliseconds need to pass before the alarm goes off. The Timer class doesn’t trigger its timer event at an absolute time, so it is this relative time difference that is assigned to the delay property of alarmTimer. Finally, the start() method is called to actually start the timer. Once the specified amount of time has passed, alarmTimer dispatches the timer event. Because the AlarmClock class registered its onAlarm() method as a listener for that event, when the timer event happens, onAlarm() is called. /** * Called when the timer event is dispatched. */ public function onAlarm(event:TimerEvent):void { trace("Alarm!"); var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage); this.dispatchEvent(alarm); } A method that is registered as an event listener must be defined with the appropriate signature (that is, the set of parameters and return type of the method). To be a listener for the Timer class’s timer event, a method must define one parameter whose data type is TimerEvent (flash.events.TimerEvent), a subclass of the Event class. When the Timer instance calls its event listeners, it passes a TimerEvent instance as the event object. Notifying others of the alarm Like the Timer class, the AlarmClock class provides an event that allows other code to receive notifications when the alarm goes off. For a class to use the event-handling framework built into ActionScript, that class must implement the flash.events.IEventDispatcher interface. Most commonly, this is done by extending the flash.events.EventDispatcher class, which provides a standard implementation of IEventDispatcher (or by extending one of EventDispatcher’s subclasses). As described previously, the AlarmClock class extends the SimpleClock class, which in turn extends the Sprite class, which (through a chain of inheritance) extends the EventDispatcher class. All of this means that the AlarmClock class already has built-in functionality to provide its own events.
  • 339. Example: Alarm Clock 339 Other code can register to be notified of the AlarmClock class’s alarm event by calling the addEventListener() method that AlarmClock inherits from EventDispatcher. When an AlarmClock instance is ready to notify other code that its alarm event has been raised, it does so by calling the dispatchEvent() method, which is also inherited from EventDispatcher. var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage); this.dispatchEvent(alarm); These lines of code are taken from the AlarmClock class’s onAlarm() method (shown in its entirety previously). The AlarmClock instance’s dispatchEvent() method is called, which in turn notifies all the registered listeners that the AlarmClock instance’s alarm event has been triggered. The parameter that is passed to dispatchEvent() is the event object that will be passed along to the listener methods. In this case, it is an instance of the AlarmEvent class, an Event subclass created specifically for this example. Providing a custom alarm event All event listeners receive an event object parameter with information about the particular event being triggered. In many cases, the event object is an instance of the Event class. However, in some cases it is useful to provide additional information to event listeners. As described earlier in the chapter, a common way to accomplish this is to define a new class, a subclass of the Event class, and use an instance of that class as the event object. In this example, an AlarmEvent instance is used as the event object when the AlarmClock class’s alarm event is dispatched. The AlarmEvent class, shown here, provides additional information about the alarm event, specifically the alarm message: import flash.events.Event; /** * This custom Event class adds a message property to a basic Event. */ public class AlarmEvent extends Event { /** * The name of the new AlarmEvent type. */ public static const ALARM:String = "alarm"; /** * A text message that can be passed to an event handler * with this event object. */ public var message:String;
  • 340. 340 Handling events /** * Constructor. * @param message The text to display when the alarm goes off. */ public function AlarmEvent(message:String = "ALARM!") { super(ALARM); this.message = message; } ... } The best way to create a custom event object class is to define a class that extends the Event class, as shown in the preceding example. To supplement the inherited functionality, the AlarmEvent class defines a property message that contains the text of the alarm message associated with the event; the message value is passed in as a parameter in the AlarmEvent constructor. The AlarmEvent class also defines the constant ALARM, which can be used to refer to the specific event (alarm) when calling the AlarmClock class’s addEventListener() method. In addition to adding custom functionality, every Event subclass must override the inherited clone() method as part of the ActionScript event-handling framework. Event subclasses can also optionally override the inherited toString() method to include the custom event’s properties in the value returned when the toString() method is called. /** * Creates and returns a copy of the current instance. * @return A copy of the current instance. */ public override function clone():Event { return new AlarmEvent(message); } /** * Returns a String containing all the properties of the current * instance. * @return A string representation of the current instance. */ public override function toString():String { return formatToString("AlarmEvent", "type", "bubbles", "cancelable", "eventPhase", "message"); }
  • 341. Example: Alarm Clock 341 The overridden clone() method needs to return a new instance of the custom Event subclass, with all the custom properties set to match the current instance. In the overridden toString() method, the utility method formatToString() (inherited from Event) is used to provide a string with the name of the custom type, as well as the names and values of all its properties.
  • 343. 343 11 CHAPTER 11 Working with XML ActionScript 3.0 includes a group of classes based on the ECMAScript for XML (E4X) specification (ECMA-357 edition 2). These classes include powerful and easy-to-use functionality for working with XML data. Using E4X, you will be able to develop code with XML data faster than was possible with previous programming techniques. As an added benefit, the code you produce will be easier to read. This chapter describes how to use E4X to process XML data. Contents Basics of XML. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 The E4X approach to XML processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .348 XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 XMLList objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .353 Initializing XML variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .354 Assembling and transforming XML objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .356 Traversing XML structures. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .358 Using XML namespaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .363 XML type conversion. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .364 Reading external XML documents. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .366 Example: Loading RSS data from the Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .367
  • 344. 344 Working with XML Basics of XML Introduction to working with XML XML is a standard way of representing structured information so that it is easy for computers to work with and reasonably easy for people to write and understand. XML is an abbreviation for eXtensible Markup Language. The XML standard is available at www.w3.org/XML/. XML offers a standard and convenient way to categorize data, to make it easier to read, access, and manipulate. XML uses a tree structure and tag structure that is similar to HTML. Here is a simple example of XML data: <song> <title>What you know?</title> <artist>Steve and the flubberblubs</artist> <year>1989</year> <lastplayed>2006-10-17-08:31</lastplayed> </song> XML data can also be more complex, with tags nested in other tags as well as attributes and other structural components. Here is a more complex example of XML data: <album> <title>Questions, unanswered</title> <artist>Steve and the flubberblubs</artist> <year>1989</year> <tracks> <song tracknumber="1" length="4:05"> <title>What do you know?</title> <artist>Steve and the flubberblubs</artist> <lastplayed>2006-10-17-08:31</lastplayed> </song> <song tracknumber="2" length="3:45"> <title>Who do you know?</title> <artist>Steve and the flubberblubs</artist> <lastplayed>2006-10-17-08:35</lastplayed> </song> <song tracknumber="3" length="5:14"> <title>When do you know?</title> <artist>Steve and the flubberblubs</artist> <lastplayed>2006-10-17-08:39</lastplayed> </song> <song tracknumber="4" length="4:19"> <title>Do you know?</title> <artist>Steve and the flubberblubs</artist> <lastplayed>2006-10-17-08:44</lastplayed>
  • 345. Basics of XML 345 </song> </tracks> </album> Notice that this XML document contains other complete XML structures within it (such as the song tags with their children). It also demonstrates other XML structures such as attributes (tracknumber and length in the song tags), and tags that contain other tags rather than containing data (such as the tracks tag). Getting started with XML If you have little or no experience with XML, here is a brief description of the most common aspects of XML data. XML data is written in plain-text form, with a specific syntax for organizing the information into a structured format. Generally, a single set of XML data is known as an XML document. In XML format, data is organized into elements (which can be single data items or containers for other elements) using a hierarchical structure. Every XML document has a single element as the top level or main item; inside this root element there may be a single piece of information, although there are more likely to be other elements, which in turn contain other elements, and so forth. For example, this XML document contains the information about a music album: <song tracknumber="1" length="4:05"> <title>What do you know?</title> <artist>Steve and the flubberblubs</artist> <mood>Happy</mood> <lastplayed>2006-10-17-08:31</lastplayed> </song> Each element is distinguished by a set of tags—the element’s name wrapped in angle brackets (less-than and greater-than signs). The opening tag, indicating the start of the element, has the element name: <title> The closing tag, which marks the end of the element, has a forward slash before the element’s name: </title> If an element contains no content, it can be written as an empty element (sometimes called a self-closing element). In XML, this element: <lastplayed/> is identical to this element: <lastplayed></lastplayed>
  • 346. 346 Working with XML In addition to the element’s content contained between the opening and closing tags, an element can also include other values, known as attributes, defined in the element’s opening tag. For example, this XML element defines a single attribute named length, with the value “4:19”: <song length="4:19"></song> Each XML element has content, which is either a single value, one or more XML elements, or nothing (for an empty element). Learning more about XML To learn more about working with XML, there are a number of additional books and resources for learning more about XML, including these web sites: ■ W3Schools XML Tutorial: http://w3schools.com/xml/ ■ XML.com: http://www.xml.com/ ■ XMLpitstop tutorials, discussion lists, and more: http://xmlpitstop.com/ ActionScript classes for working with XML ActionScript 3.0 includes several classes that are used for working with XML-structured information. The two main classes are as follows: ■ XML: Represents a single XML element, which can be an XML document with multiple children or a single-value element within a document. ■ XMLList: Represents a set of XML elements. An XMLList object is used when there are multiple XML elements that are “siblings” (at the same level, and contained by the same parent, in the XML document’s hierarchy). For instance, an XMLList instance would be the easiest way to work with this set of XML elements (presumably contained in an XML document): <artist type="composer">Fred Wilson</artist> <artist type="conductor">James Schmidt</artist> <artist type="soloist">Susan Harriet Thurndon</artist> For more advanced uses involving XML namespaces, ActionScript also includes the Namespace and QName classes. For more information, see “Using XML namespaces” on page 363. In addition to the built-in classes for working with XML, ActionScript 3.0 also includes several operators that provide specific functionality for accessing and manipulating XML data. This approach to working with XML using these classes and operators is known as ECMAScript for XML (E4X), as defined by the ECMA-357 edition 2 specification.
  • 347. Basics of XML 347 Common XML tasks When you work with XML in ActionScript, you are likely to do the following tasks: ■ Constructing XML documents (adding elements and values) ■ Accessing XML elements, values, and attributes ■ Filtering (searching in) XML elements ■ Looping over a set of XML elements ■ Converting data between XML classes and the String class ■ Working with XML namespaces ■ Loading external XML files Important concepts and terms The following reference list contains important terms used in this chapter: ■ Element: A single item in an XML document, identified as the content contained between a starting tag and an ending tag (including the tags). XML elements can contain text data or other elements, or can be empty. ■ Empty element: An XML element that contains no child elements. Empty elements are often written as self-closing tags (such as <element/>). ■ Document: A single XML structure. An XML document can contain any number of elements (or can consist only of a single empty element); however, an XML document must have a single top-level element that contains all the other elements in the document. ■ Node: Another name for an XML element. ■ Attribute: A named value associated with an element that is written into the opening tag of the element in attributename="value" format, rather than being written as a separate child element nested inside the element. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Essentially all the code listings in this chapter already include the appropriate trace() function call. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie.
  • 348. 348 Working with XML You will see the results of the trace() function in the Output panel. This and other techniques for testing example code listings are described in more detail in “Testing in-chapter example code listings” on page 63. The E4X approach to XML processing The ECMAScript for XML specification defines a set of classes and functionality for working with XML data. These classes and functionality are known collectively as E4X. ActionScript 3.0 includes the following E4X classes: XML, XMLList, QName, and Namespace. The methods, properties, and operators of the E4X classes are designed with the following goals: ■ Simplicity—Where possible, E4X makes it easier to write and understand code for working with XML data. ■ Consistency—The methods and reasoning behind E4X are internally consistent and consistent with other parts of ActionScript. ■ Familiarity—You manipulate XML data with well-known operators, such as the dot (.) operator. Here is an example of manipulating data with E4X: var myXML:XML = <order> <item id='1'> <menuName>burger</menuName> <price>3.95</price> </item> <item id='2'> <menuName>fries</menuName> <price>1.45</price> </item> </order> NOTE There was an XML class in ActionScript 2.0. In ActionScript 3.0 it has been renamed XMLDocument, so that it does not conflict with the ActionScript 3.0 XML class that is part of E4X. In ActionScript 3.0, the legacy classes—XMLDocument, XMLNode, XMLParser, and XMLTag—are included in the flash.xml package primarily for legacy support. The new E4X classes are core classes; you need not import a package to use them. This chapter does not go into detail on the legacy ActionScript 2.0 XML classes. For details on these, see the flash.xml package in the ActionScript 3.0 Language and Components Reference.
  • 349. The E4X approach to XML processing 349 Often, your application will load XML data from an external source, such as a web service or a RSS feed. However, for clarity, the examples in this chapter assign XML data as literals. As the following code shows, E4X includes some intuitive operators, such as the dot (.) and attribute identifier (@) operators, for accessing properties and attributes in the XML: trace(myXML.item[0].menuName); // Output: burger trace(myXML.item.(@id==2).menuName); // Output: fries trace(myXML.item.(menuName=="burger").price); // Output: 3.95 Use the appendChild() method to assign a new child node to the XML, as the following snippet shows: var newItem:XML = <item id="3"> <menuName>medium cola</menuName> <price>1.25</price> </item> myXML.appendChild(newItem); Use the @ and . operators not only to read data, but also to assign data, as in the following: myXML.item[0].menuName="regular burger"; myXML.item[1].menuName="small fries"; myXML.item[2].menuName="medium cola"; myXML.item.(menuName=="regular burger").@quantity = "2"; myXML.item.(menuName=="small fries").@quantity = "2"; myXML.item.(menuName=="medium cola").@quantity = "2"; Use a for loop to iterate through nodes of the XML, as follows: var total:Number = 0; for each (var property:XML in myXML.item) { var q:int = Number(property.@quantity); var p:Number = Number(property.price); var itemTotal:Number = q * p; total += itemTotal; trace(q + " " + property.menuName + " $" + itemTotal.toFixed(2)) } trace("Total: $", total.toFixed(2));
  • 350. 350 Working with XML XML objects An XML object may represent an XML element, attribute, comment, processing instruction, or text element. An XML object is classified as having either simple content or complex content. An XML object that has child nodes is classified as having complex content. An XML object is said to have simple content if it is any one of the following: an attribute, a comment, a processing instruction, or a text node. For example, the following XML object contains complex content, including a comment and a processing instruction: XML.ignoreComments = false; XML.ignoreProcessingInstructions = false; var x1:XML = <order> <!--This is a comment. --> <?PROC_INSTR sample ?> <item id='1'> <menuName>burger</menuName> <price>3.95</price> </item> <item id='2'> <menuName>fries</menuName> <price>1.45</price> </item> </order> As the following example shows, you can now use the comments() and processingInstructions() methods to create new XML objects, a comment and a processing instruction: var x2:XML = x1.comments()[0]; var x3:XML = x1.processingInstructions()[0]; XML properties The XML class has five static properties: ■ The ignoreComments and ignoreProcessingInstructions properties determine whether comments or processing instructions are ignored when the XML object is parsed. ■ The ignoreWhitespace property determines whether white space characters are ignored in element tags and embedded expressions that are separated only by white space characters. ■ The prettyIndent and prettyPrinting properties are used to format the text that is returned by the toString() and toXMLString() methods of the XML class.
  • 351. XML objects 351 For details on these properties, see the ActionScript 3.0 Language and Components Reference. XML methods The following methods allow you to work with the hierarchical structure of XML objects: ■ appendChild() ■ child() ■ childIndex() ■ children() ■ descendants() ■ elements() ■ insertChildAfter() ■ insertChildBefore() ■ parent() ■ prependChild() The following methods allow you to work with XML object attributes: ■ attribute() ■ attributes() The following methods allow you to you work with XML object properties: ■ hasOwnProperty() ■ propertyIsEnumerable() ■ replace() ■ setChildren() The following methods are for working with qualified names and namespaces: ■ addNamespace() ■ inScopeNamespaces() ■ localName() ■ name() ■ namespace() ■ namespaceDeclarations() ■ removeNamespace() ■ setLocalName() ■ setName() ■ setNamespace()
  • 352. 352 Working with XML The following methods are for working with and determining certain types of XML content: ■ comments() ■ hasComplexContent() ■ hasSimpleContent() ■ nodeKind() ■ processingInstructions() ■ text() The following methods are for conversion to strings and for formatting XML objects: ■ defaultSettings() ■ setSettings() ■ settings() ■ normalize() ■ toString() ■ toXMLString() There are a few additional methods: ■ contains() ■ copy() ■ valueOf() ■ length() For details on these methods, see the ActionScript 3.0 Language and Components Reference.
  • 353. XMLList objects 353 XMLList objects An XMLList instance represents an arbitrary collection of XML objects. It can contain full XML documents, XML fragments, or the results of an XML query. The following methods allow you to work with the hierarchical structure of XMLList objects: ■ child() ■ children() ■ descendants() ■ elements() ■ parent() The following methods allow you to work with XMLList object attributes: ■ attribute() ■ attributes() The following methods allow you to you work with XMLList properties: ■ hasOwnProperty() ■ propertyIsEnumerable() The following methods are for working with and determining certain types of XML content: ■ comments() ■ hasComplexContent() ■ hasSimpleContent() ■ processingInstructions() ■ text() The following are for conversion to strings and for formatting the XMLList object: ■ normalize() ■ toString() ■ toXMLString() There are a few additional methods: ■ contains() ■ copy() ■ length() ■ valueOf() For details on these methods, see the ActionScript 3.0 Language and Components Reference.
  • 354. 354 Working with XML For an XMLList object that contains exactly one XML element, you can use all properties and methods of the XML class, because an XMLList with one XML element is treated the same as an XML object. For example, in the following code, because doc.div is an XMLList object containing one element, you can use the appendChild() method from the XML class: var doc:XML = <body> <div> <p>Hello</p> </div> </body>; doc.div.appendChild(<p>World</p>); For a list of XML properties and methods, see “XML objects” on page 350. Initializing XML variables You can assign an XML literal to an XML object, as follows: var myXML:XML = <order> <item id='1'> <menuName>burger</menuName> <price>3.95</price> </item> <item id='2'> <menuName>fries</menuName> <price>1.45</price> </item> </order> As the following snippet shows, you can also use the new constructor to create an instance of an XML object from a string that contains XML data: var str:String = "<order><item id='1'><menuName>burger</menuName>" + "<price>3.95</price></item></order>"; var myXML:XML = new XML(str);
  • 355. Initializing XML variables 355 If the XML data in the string is not well formed (for example, if a closing tag is missing), you will see a run-time error. You can also pass data by reference (from other variables) into an XML object, as the following example shows: var tagname:String = "item"; var attributename:String = "id"; var attributevalue:String = “5”; var content:String = "Chicken"; var x:XML = <{tagname} {attributename}={attributevalue}>{content}</ {tagname}>; trace(x.toXMLString()) // Output: <item id="5">Chicken</item> To load XML data from a URL, use the URLLoader class, as the following example shows: import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; var externalXML:XML; var loader:URLLoader = new URLLoader(); var request:URLRequest = new URLRequest("xmlFile.xml"); loader.load(request); loader.addEventListener(Event.COMPLETE, onComplete); function onComplete(event:Event):void { var loader:URLLoader = event.target as URLLoader; if (loader != null) { externalXML = new XML(loader.data); trace(externalXML.toXMLString()); } else { trace("loader is not a URLLoader!"); } } To read XML data from a socket connection, use the XMLSocket class. For more information, see the XMLSocket entry in the ActionScript 3.0 Language and Components Reference.
  • 356. 356 Working with XML Assembling and transforming XML objects Use the prependChild() method or the appendChild() method to add a property to the beginning or end of an XML object’s list of properties, as the following example shows: var x1:XML = <p>Line 1</p> var x2:XML = <p>Line 2</p> var x:XML = <body></body> x = x.appendChild(x1); x = x.appendChild(x2); x = x.prependChild(<p>Line 0</p>); // x == <body><p>Line 0</p><p>Line 1</p><p>Line 2</p></body> Use the insertChildBefore() method or the insertChildAfter() method to add a property before or after a specified property, as follows: var x:XML = <body> <p>Paragraph 1</p> <p>Paragraph 2</p> </body> var newNode:XML = <p>Paragraph 1.5</p> x = x.insertChildAfter(x.p[0], newNode) x = x.insertChildBefore(x.p[2], <p>Paragraph 1.75</p>) As the following example shows, you can also use curly brace operators ( { and } ) to pass data by reference (from other variables) when constructing XML objects: var ids:Array = [121, 122, 123]; var names:Array = [["Murphy","Pat"], ["Thibaut","Jean"], ["Smith","Vijay"]] var x:XML = new XML("<employeeList></employeeList>"); for (var i:int = 0; i < 3; i++) { var newnode:XML = new XML(); newnode = <employee id={ids[i]}> <last>{names[i][0]}</last> <first>{names[i][1]}</first> </employee>; x = x.appendChild(newnode) }
  • 357. Assembling and transforming XML objects 357 You can assign properties and attributes to an XML object by using the = operator, as in the following: var x:XML = <employee> <lastname>Smith</lastname> </employee> x.firstname = "Jean"; x.@id = "239"; This sets the XML object x to the following: <employee id="239"> <lastname>Smith</lastname> <firstname>Jean</firstname> </employee> You can use the + and += operators to concatenate XMLList objects: var x1:XML = <a>test1</a> var x2:XML = <b>test2</b> var xList:XMLList = x1 + x2; xList += <c>test3</c> This sets the XMLList object xList to the following: <a>test1</a> <b>test2</b> <c>test3</c>
  • 358. 358 Working with XML Traversing XML structures One of the powerful features of XML is its ability to provide complex, nested data via a linear string of text characters. When you load data into an XML object, ActionScript parses the data and loads its hierarchical structure into memory (or it sends a run-time error if the XML data is not well formed). The operators and methods of the XML and XMLList objects make it easy to traverse the structure of XML data. Use the dot (.) operator and the descendent accessor (..) operator to access child properties of an XML object. Consider the following XML object: var myXML:XML = <order> <book ISBN="0942407296"> <title>Baking Extravagant Pastries with Kumquats</title> <author> <lastName>Contino</lastName> <firstName>Chuck</firstName> </author> <pageCount>238</pageCount> </book> <book ISBN="0865436401"> <title>Emu Care and Breeding</title> <editor> <lastName>Case</lastName> <firstName>Justin</firstName> </editor> <pageCount>115</pageCount> </book> </order> The object myXML.book is an XMLList object containing child properties of the myXML object that have the name book. These are two XML objects, matching the two book properties of the myXML object. The object myXML..lastName is an XMLList object containing any descendent properties with the name lastName. These are two XML objects, matching the two lastName of the myXML object. The object myXML.book.editor.lastName is an XMLList object containing any children with the name lastName of children with the name editor of children with the name book of the myXML object: in this case, an XMLList object containing only one XML object (the lastName property with the value "Case").
  • 359. Traversing XML structures 359 Accessing parent and child nodes The parent() method returns the parent of an XML object. You can use the ordinal index values of a child list to access specific child objects. For example, consider an XML object myXML that has two child properties named book. Each child property named book has an index number associated with it: myXML.book[0] myXML.book[1] To access a specific grandchild, you can specify index numbers for both the child and grandchild names: myXML.book[0].title[0] However, if there is only one child of x.book[0] that has the name title, you can omit the index reference, as follows: myXML.book[0].title Similarly, if there is only one book child of the object x, and if that child object has only one title object, you can omit both index references, like this: myXML.book.title You can use the child() method to navigate to children with names based on a variable or expression, as the following example shows: var myXML:XML = <order> <book> <title>Dictionary</title> </book> </order>; var childName:String = "book"; trace(myXML.child(childName).title) // output: Dictionary
  • 360. 360 Working with XML Accessing attributes Use the @ symbol (the attribute identifier operator) to access attributes in an XML or XMLList object, as shown in the following code: var employee:XML = <employee id="6401" code="233"> <lastName>Wu</lastName> <firstName>Erin</firstName> </employee>; trace(employee.@id); // 6401 You can use the * wildcard symbol with the @ symbol to access all attributes of an XML or XMLList object, as in the following code: var employee:XML = <employee id="6401" code="233"> <lastName>Wu</lastName> <firstName>Erin</firstName> </employee>; trace(employee.@*.toXMLString()); // 6401 // 233 You can use the attribute() or attributes() method to access a specific attribute or all attributes of an XML or XMLList object, as in the following code: var employee:XML = <employee id="6401" code="233"> <lastName>Wu</lastName> <firstName>Erin</firstName> </employee>; trace(employee.attribute("id")); // 6401 trace(employee.attribute("*").toXMLString()); // 6401 // 233 trace(employee.attributes().toXMLString()); // 6401 // 233 Note that you can also use the following syntax to access attributes, as the following example shows: employee.attribute("id") employee["@id"] employee.@["id"] These are each equivalent to employee.@id. However, the syntax employee.@id is the preferred approach.
  • 361. Traversing XML structures 361 Filtering by attribute or element value You can use the parentheses operators— ( and ) —to filter elements with a specific element name or attribute value. Consider the following XML object: var x:XML = <employeeList> <employee id="347"> <lastName>Zmed</lastName> <firstName>Sue</firstName> <position>Data analyst</position> </employee> <employee id="348"> <lastName>McGee</lastName> <firstName>Chuck</firstName> <position>Jr. data analyst</position> </employee> </employeeList> The following expressions are all valid: ■ x.employee.(lastName == "McGee")—This is the second employee node. ■ x.employee.(lastName == "McGee").firstName—This is the firstName property of the second employee node. ■ x.employee.(lastName == "McGee").@id—This is the value of the id attribute of the second employee node. ■ x.employee.(@id == 347)—The first employee node. ■ x.employee.(@id == 347).lastName—This is the lastName property of the first employee node. ■ x.employee.(@id > 300)—This is an XMLList with both employee properties. ■ x.employee.(position.toString().search("analyst") > -1)—This is an XMLList with both position properties. If you try to filter on attributes or elements that may not exist, Adobe Flash Player will throw an exception. For example, the final line of following code generates an error, because there is no id attribute in the second p element: var doc:XML = <body> <p id='123'>Hello, <b>Bob</b>.</p> <p>Hello.</p> </body>; trace(doc.p.(@id == '123'));
  • 362. 362 Working with XML Similarly, the final line of following code generates an error because there is no b property of the second p element: var doc:XML = <body> <p id='123'>Hello, <b>Bob</b>.</p> <p>Hello.</p> </body>; trace(doc.p.(b == 'Bob')); To avoid these errors, you can identify the properties that have the matching attributes or elements by using the attribute() and elements() methods, as in the following code: var doc:XML = <body> <p id='123'>Hello, <b>Bob</b>.</p> <p>Hello.</p> </body>; trace(doc.p.(attribute('id') == '123')); trace(doc.p.(elements('b') == 'Bob')); You can also use the hasOwnProperty() method, as in the following code: var doc:XML = <body> <p id='123'>Hello, <b>Bob</b>.</p> <p>Hello.</p> </body>; trace(doc.p.(hasOwnProperty('@id') && @id == '123')); trace(doc.p.(hasOwnProperty('b') && b == 'Bob')); Using the for..in and the for each..in statements ActionScript 3.0 includes the for..in statement and the for each..in statement for iterating through XMLList objects. For example, consider the following XML object, myXML, and the XMLList object, myXML.item. The XMLList object, myXML.item, consists of the two item nodes of the XML object. var myXML:XML = <order> <item id='1' quantity='2'> <menuName>burger</menuName> <price>3.95</price> </item> <item id='2' quantity='2'> <menuName>fries</menuName> <price>1.45</price> </item> </order>;
  • 363. Using XML namespaces 363 The for..in statement lets you iterate over a set of property names in an XMLList: var total:Number = 0; for (var pname:String in myXML.item) { total += myXML.item.@quantity[pname] * myXML.item.price[pname]; } The for each..in statement lets you iterate through the properties in the XMLList: var total2:Number = 0; for each (var prop:XML in myXML.item) { total2 += prop.@quantity * prop.price; } Using XML namespaces Namespaces in an XML object (or document) identify the type of data that the object contains. For example, in sending and delivering XML data to a web service that uses the SOAP messaging protocol, you declare the namespace in the opening tag of the XML: var message:XML = <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <soap:Body xmlns:w="http://www.test.com/weather/"> <w:getWeatherResponse> <w:tempurature >78</w:tempurature> </w:getWeatherResponse> </soap:Body> </soap:Envelope>; The namespace has a prefix, soap, and a URI that defines the namespace, http:// schemas.xmlsoap.org/soap/envelope/. ActionScript 3.0 includes the Namespace class for working with XML namespaces. For the XML object in the previous example, you can use the Namespace class as follows: var soapNS:Namespace = message.namespace("soap"); trace(soapNS); // Output: http://schemas.xmlsoap.org/soap/envelope/ var wNS:Namespace = new Namespace("w", "http://www.test.com/weather/"); message.addNamespace(wNS); var encodingStyle:XMLList = message.@soapNS::encodingStyle; var body:XMLList = message.soapNS::Body; message.soapNS::Body.wNS::GetWeatherResponse.wNS::tempurature = "78";
  • 364. 364 Working with XML The XML class includes the following methods for working with namespaces: addNamespace(), inScopeNamespaces(), localName(), name(), namespace(), namespaceDeclarations(), removeNamespace(), setLocalName(), setName(), and setNamespace(). The default xml namespace directive lets you assign a default namespace for XML objects. For example, in the following, both x1 and x2 have the same default namespace: var ns1:Namespace = new Namespace("http://www.example.com/namespaces/"); default xml namespace = ns1; var x1:XML = <test1 />; var x2:XML = <test2 />; XML type conversion You can convert XML objects and XMLList objects to String values. Similarly, you can convert strings to XML objects and XMLList objects. Also, keep in mind that all XML attribute values, names, and text values are strings. The following sections discuss all these forms of XML type conversion. Converting XML and XMLList objects to strings The XML and XMLList classes include a toString() method and a toXMLString() method. The toXMLString() method returns a string that includes all tags, attributes, namespace declarations, and content of the XML object. For XML objects with complex content (child elements), the toString() method does exactly the same as the toXMLString() method. For XML objects with simple content (those that contain only one text element), the toString() method returns only the text content of the element, as the following example shows: var myXML:XML = <order> <item id='1' quantity='2'> <menuName>burger</menuName> <price>3.95</price> </item> <order>; trace(myXML.item[0].menuName.toXMLString()); // <menuName>burger</menuName> trace(myXML.item[0].menuName.toString()); // burger
  • 365. XML type conversion 365 If you use the trace() method without specifying toString() or toXMLString(), the data is converted using the toString() method by default, as this code shows: var myXML:XML = <order> <item id='1' quantity='2'> <menuName>burger</menuName> <price>3.95</price> </item> <order>; trace(myXML.item[0].menuName); // burger When using the trace() method to debug code, you will often want to use the toXMLString() method so that the trace() method outputs more complete data. Converting strings to XML objects You can use the new XML() constructor to create an XML object from a string, as follows: var x:XML = new XML("<a>test</a>"); If you attempt to convert a string to XML from a string that represents invalid XML or XML that is not well formed, a run-time error is thrown, as follows: var x:XML = new XML("<a>test"); // throws an error Converting attribute values, names, and text values from strings All XML attribute values, names, and text values are String data types, and you may need to convert these to other data types. For example, the following code uses the Number() function to convert text values to numbers: var myXML:XML = <order> <item> <price>3.95</price> </item> <item> <price>1.00</price> </item> </order>;
  • 366. 366 Working with XML var total:XML = <total>0</total>; myXML.appendChild(total); for each (var item:XML in myXML.item) { myXML.total.children()[0] = Number(myXML.total.children()[0]) + Number(item.price.children()[0]); } trace(myXML.total); // 4.35; If this code did not use the Number() function, the code would interpret the + operator as the string concatenation operator, and the trace() method in the last line would output the following: 01.003.95 Reading external XML documents You can use the URLLoader class to load XML data from a URL. To use the following code in your applications, replace the XML_URL value in the example with a valid URL: var myXML:XML = new XML(); var XML_URL:String = "http://www.example.com/Sample3.xml"; var myXMLURL:URLRequest = new URLRequest(XML_URL); var myLoader:URLLoader = new URLLoader(myXMLURL); myLoader.addEventListener("complete", xmlLoaded); function xmlLoaded(event:Event):void { myXML = XML(myLoader.data); trace("Data loaded."); } You can also use the XMLSocket class to set up an asynchronous XML socket connection with a server. For more information, see the ActionScript 3.0 Language and Components Reference.
  • 367. Example: Loading RSS data from the Internet 367 Example: Loading RSS data from the Internet The RSSViewer sample application shows a number of features of working with XML in ActionScript, including the following: ■ Using XML methods to traverse XML data in the form of an RSS feed. ■ Using XML methods to assemble XML data in the form of HTML to use in a text field. The RSS format is widely used to syndicate news via XML. A simple RSS data file may look like the following: <?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel> <title>Alaska - Weather</title> <link>http://www.nws.noaa.gov/alerts/ak.html</link> <description>Alaska - Watches, Warnings and Advisories</description> <item> <title> Short Term Forecast - Taiya Inlet, Klondike Highway (Alaska) </title> <link> http://www.nws.noaa.gov/alerts/ak.html#A18.AJKNK.1900 </link> <description> Short Term Forecast Issued At: 2005-04-11T19:00:00 Expired At: 2005-04-12T01:00:00 Issuing Weather Forecast Office Homepage: http://pajk.arh.noaa.gov </description> </item> <item> <title> Short Term Forecast - Haines Borough (Alaska) </title> <link> http://www.nws.noaa.gov/alerts/ak.html#AKZ019.AJKNOWAJK.190000 </link> <description> Short Term Forecast Issued At: 2005-04-11T19:00:00 Expired At: 2005-04-12T01:00:00 Issuing Weather Forecast Office Homepage: http://pajk.arh.noaa.gov </description> </item> </channel> </rss>
  • 368. 368 Working with XML The SimpleRSS application reads RSS data from the Internet, parses the data for headlines (titles), links, and descriptions, and returns that data. The SimpleRSSUI class provides the UI and calls the SimpleRSS class, which does all of the XML processing. To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The RSSViewer application files can be found in the folder Samples/RSSViewer. The application consists of the following files: Reading and parsing XML data The RSSParser class includes an xmlLoaded() method that converts the input RSS data, stored in the rssXML variable, into an string containing HTML-formatted output, rssOutput. Near the beginning of the method, code sets the default XML namespace if the source RSS data includes a default namespace: if (rssXML.namespace("") != undefined) { default xml namespace = rssXML.namespace(""); } The next lines then loop through the contents of the source XML data, examining each descendant property named item: for each (var item:XML in rssXML..item) { File Description RSSViewer.mxml or RSSViewer.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/rssViewer/ RSSParser.as A class that contains methods that use E4X to traverse RSS (XML) data and generate a corresponding HTML representation. RSSData/ak.rss A sample RSS file. The application is set up to read RSS data from the web, at a Flex RSS feed hosted by Adobe. However, you can easily change the application to read RSS data from this document, which uses a slightly different schema than that of the Flex RSS feed.
  • 369. Example: Loading RSS data from the Internet 369 var itemTitle:String = item.title.toString(); var itemDescription:String = item.description.toString(); var itemLink:String = item.link.toString(); outXML += buildItemHTML(itemTitle, itemDescription, itemLink); } The first three lines simply set string variables to represent the title, description and link properties of the item property of the XML data. The next line then calls the buildItemHTML() method to get HTML data in the form of an XMLList object, using the three new string variables as parameters. Assembling XMLList data The HTML data (an XMLList object) is of the following form: <b>itemTitle</b> <p> itemDescription <br /> <a href="link"> <font color="#008000">More...</font> </a> </p> The first lines of the method clear the default xml namespace: default xml namespace = new Namespace(); The default xml namespace directive has function block-level scope. This means that the scope of this declaration is the buildItemHTML() method. The lines that follow assemble the XMLList, based on the string arguments passed to the function: var body:XMLList = new XMLList(); body += new XML("<b>" + itemTitle + "</b>"); var p:XML = new XML("<p>" + itemDescription + "</p>"); var link:XML = <a></a>; link.@href = itemLink; // <link href="itemLinkString"></link> link.font.@color = "#008000"; // <font color="#008000"></font></a> // 0x008000 = green link.font = "More..."; p.appendChild(<br/>); p.appendChild(link); body += p;
  • 370. 370 Working with XML This XMLList object represents string data suitable for an ActionScript HTML text field. The xmlLoaded() method uses the return value of the buildItemHTML() method and converts it to a string: XML.prettyPrinting = false; rssOutput = outXML.toXMLString(); Extracting the title of the RSS feed and sending a custom event The xmlLoaded() method sets a rssTitle string variable, based on information in the source RSS XML data: rssTitle = rssXML.channel.title.toString(); Finally, the xmlLoaded() method generates an event, which notifies the application that the data is parsed and available: dataWritten = new Event("dataWritten", true);
  • 371. 371 12 CHAPTER 12 Display programming Display programming in ActionScript 3.0 allows you to work with elements that appear on the Stage of Adobe Flash Player 9. This chapter describes the basic concepts for working with on-screen elements. You’ll learn the details about programmatically organizing visual elements. You’ll also learn about creating your own custom classes for display objects. Contents Basics of display programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .372 Core display classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 Advantages of the display list approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .379 Working with display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .382 Manipulating display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Masking display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 Animating objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 Example: SpriteArranger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .423
  • 372. 372 Display programming Basics of display programming Introduction to display programming Each application built with ActionScript 3.0 has a hierarchy of displayed objects known as the display list. The display list contains all the visible elements in the application. Display elements fall into one or more of the following groups: ■ The Stage The Stage is the base container of display objects. Each application has one Stage object, which contains all on-screen display objects. The Stage is the top-level container and is at the top of the display list hierarchy: Display Object Container Display ObjectDisplay Object Container Display Object Container Instance of the main class of the SWF file StageStage Display Object ContainerDisplay Object Display Object
  • 373. Basics of display programming 373 Each SWF file has an associated ActionScript class, known as the main class of the SWF file. When Flash Player opens a SWF file in an HTML page, Flash Player calls the constructor function for that class and the instance that is created (which is always a type of display object) is added as a child of the Stage object. The main class of a SWF file always extends the Sprite class (for more information, see “Advantages of the display list approach” on page 379). You can access the Stage through the stage property of any DisplayObject instance. For more information, see “Setting Stage properties” on page 389. ■ Display objects In ActionScript 3.0, all elements that appear on screen in an application are types of display objects. The flash.display package includes a DisplayObject class, which is a base class extended by a number of other classes. These different classes represent different types of display objects, such as vector shapes, movie clips, and text fields, to name a few. For an overview of these classes, see “Advantages of the display list approach” on page 379. ■ Display object containers Display object containers are special types of display objects that, in addition to having their own visual representation, can also contain child objects that are also display objects. The DisplayObjectContainer class is a subclass of the DisplayObject class. A DisplayObjectContainer object can contain multiple display objects in its child list. For example, the following illustration shows a type of DisplayObjectContainer object known as a Sprite that contains various display objects: In the context of discussing display objects, DisplayObjectContainer objects are also known as display object containers or simply containers. A SimpleButton object. This type of display object has different “up,” “down,” and “over” states. A Bitmap object. In this case, the Bitmap object was loaded from an external JPEG through a Loader object. A Shape object. The “picture frame” contains a rounded rectangle that is drawn in ActionScript. This Shape object has a Drop Shadow filter applied to it. A TextField object.
  • 374. 374 Display programming Although all visible display objects inherit from the DisplayObject class, the type of each is of a specific subclass of DisplayObject class. For example, there is a constructor function for the Shape class or the Video class, but there is no constructor function for the DisplayObject class. As noted earlier, the Stage is a display object container. Common display programming tasks Since so much of ActionScript programming involves creating and manipulating visual elements, there are numerous tasks that are related to display programming. This chapter describes common tasks that apply to all display objects, including: ■ Working with the display list and display object containers ■ Adding display objects to the display list ■ Removing objects from the display list ■ Moving objects among display containers ■ Moving objects in front of or behind other objects ■ Working with the Stage ■ Setting the frame rate ■ Controlling Stage scaling ■ Working with full-screen mode ■ Handling display object events ■ Positioning display objects, including creating drag-and-drop interaction ■ Resizing, scaling, and rotating display objects ■ Applying blending modes, color transformations, and transparency to display objects ■ Masking display objects ■ Animating display objects ■ Loading external display content (such as SWF files or images) Later chapters in this manual describe additional tasks for working with display objects. These tasks include both tasks that apply to any display object and tasks associated with specific types of display objects: ■ Drawing vector graphics with ActionScript on display objects, described in Chapter 14, “Using the drawing API,” on page 447 ■ Applying geometric transformations to display objects, described in Chapter 13, “Working with geometry,” on page 431
  • 375. Basics of display programming 375 ■ Applying graphical filter effects such as blur, glow, drop shadow and more to display objects, described in Chapter 15, “Filtering display objects,” on page 465 ■ Working with MovieClip-specific characteristics, described in Chapter 16, “Working with movie clips,” on page 491 ■ Working with TextField objects, described in Chapter 17, “Working with text,” on page 509 ■ Working with bitmap graphics, described in Chapter 18, “Working with bitmaps,” on page 539 ■ Working with video elements, described in Chapter 19, “Working with video,” on page 555 Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Alpha: The color value representing the amount of transparency (or more correctly, the amount of opacity) in a color. For example, a color with an alpha channel value of 60% only shows 60% of its full strength, and is 40% transparent. ■ Bitmap graphic: A graphic that is defined in the computer as a grid (rows and columns) of colored pixels. Commonly bitmap graphics include digital photos and similar images. ■ Blending mode: A specification of how the contents of two overlapping images should interact. Commonly an opaque image on top of another image simply blocks the image underneath so that it isn’t visible at all; however, different blending modes cause the colors of the images to blend together in different ways so the resulting content is some combination of the two images. ■ Display list: The hierarchy of display objects that will be rendered as visible screen content by Flash Player. The Stage is the root of the display list, and all the display objects that are attached to the Stage or one of its children form the display list (even if the object isn’t actually rendered, for example if it’s outside the boundaries of the Stage). ■ Display object: An object which represents some type of visual content in Flash Player. Only display objects can be included in the display list, and all display object classes are subclasses of the DisplayObject class. ■ Display object container: A special type of display object which can contain child display objects in addition to (generally) having its own visual representation. ■ Main class of the SWF file: The class that defines the behavior for the outermost display object in a SWF file, which conceptually is the class for the SWF file itself. For instance, a SWF created in Flash authoring has a “main timeline” which contains all other timelines; the main class of the SWF file is the class of which the main timeline is an instance.
  • 376. 376 Display programming ■ Masking: A technique of hiding from view certain parts of an image (or conversely, only allowing certain parts of an image to display). The hidden portions of the image become transparent, so content underneath shows through. The term is related to painter’s masking tape that is used to prevent paint from being applied to certain areas. ■ Stage: The visual container that is the base or background of all visual content in a SWF. ■ Transformation: An adjustment to a visual characteristic of a graphic, such as rotating the object, altering its scale, skewing or distorting its shape, or altering its color. ■ Vector graphic: A graphic that is defined in the computer as lines and shapes drawn with particular characteristics (such as thickness, length, size, angle, and position). Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because this chapter is about creating and manipulating visual content, essentially all the code listings in this chapter create visual objects and display them on the screen; testing the sample will involve viewing the result in Flash Player rather than viewing values of variables as in previous chapters. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the code displayed on the screen, and any trace() function calls will display in the Output panel. Techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63.
  • 377. Core display classes 377 Core display classes The ActionScript 3.0 flash.display package includes classes for visual objects that can appear in Flash Player. The following illustration shows the subclass relationships of these core display object classes. The illustration shows the class inheritance of display object classes. Note that some of these classes, specifically StaticText, TextField, and Video, are not in the flash.display package, but they still inherit from the DisplayObject class. All classes that extend the DisplayObject class inherit its methods and properties. For more information, see “Properties and methods of the DisplayObject class” on page 382. You can instantiate objects of the following classes contained in the flash.display package: ■ Bitmap—You use the Bitmap class to define bitmap objects, either loaded from external files or rendered through ActionScript. You can load bitmaps from external files through the Loader class. You can load GIF, JPG, or PNG files. You can also create a BitmapData object with custom data and then create a Bitmap object that uses that data. You can use the methods of the BitmapData class to alter bitmaps, whether they are loaded or created in ActionScript. For more information, see “Loading display objects” on page 419 and Chapter 18, “Working with bitmaps,” on page 539. ■ Loader—You use the Loader class to load external assets (either SWF files or graphics). For more information, see “Loading display content dynamically” on page 418. ■ Shape—You use the Shape class to create vector graphics, such as rectangles, lines, circles, and so on. For more information, see Chapter 14, “Using the drawing API,” on page 447. ■ SimpleButton—A SimpleButton object is the ActionScript representation of a Flash button symbol. A SimpleButton instance has three button states: up, down, and over. MorphShapeAVM1Movie Shape StaticText DisplayObjectContainer TextField Bitmap InteractiveObject SimpleButton StageLoader MovieClip Sprite
  • 378. 378 Display programming ■ Sprite—A Sprite object can contain graphics of its own, and it can contain child display objects. (The Sprite class extends the DisplayObjectContainer class). For more information, see “Working with display object containers” on page 383 and Chapter 14, “Using the drawing API,” on page 447. ■ MovieClip—A MovieClip object is the ActionScript form of a movie clip symbol created in Flash authoring. In practice, a MovieClip is similar to a Sprite object, except that it also has a timeline. For more information, see Chapter 16, “Working with movie clips,” on page 491. The following classes, which are not in the flash.display package, are subclasses of the DisplayObject class: ■ The TextField class, included in the flash.text package, is a display object for text display and input. For more information, see Chapter 17, “Working with text,” on page 509. ■ The Video class, included in the flash.media package, is the display object used for displaying video files. For more information, see Chapter 19, “Working with video,” on page 555. The following classes in the flash.display package extend the DisplayObject class, but you cannot create instances of them. Instead, they serve as parent classes for other display objects, combining common functionality into a single class. ■ AVM1Movie—The AVM1Movie class is used to represent loaded SWF files that are authored in ActionScript 1.0 and 2.0. ■ DisplayObjectContainer—The Loader, Stage, Sprite, and MovieClip classes each extend the DisplayObjectContainer class. For more information, see “Working with display object containers” on page 383. ■ InteractiveObject—InteractiveObject is the base class for all objects used to interact with the mouse and keyboard. SimpleButton, TextField, Video, Loader, Sprite, Stage, and MovieClip objects are all subclasses of the InteractiveObject class. For more information on creating mouse and keyboard interaction, see Chapter 21, “Capturing user input,” on page 631. ■ MorphShape—These objects are created when you create a shape tween in the Flash authoring tool. You cannot instantiate them using ActionScript, but they can be accessed from the display list. ■ Stage—The Stage class extends the DisplayObjectContainer class. There is one Stage instance for an application, and it is at the top of the display list hierarchy. To access the Stage, use the stage property of any DisplayObject instance. For more information, see “Setting Stage properties” on page 389.
  • 379. Advantages of the display list approach 379 Also, the StaticText class, in the flash.text package, extends the DisplayObject class, but you cannot create an instance of it in code. Static text fields are created only in Adobe Flash CS3 Professional. Advantages of the display list approach In ActionScript 3.0, there are separate classes for different types of display objects. In ActionScript 1.0 and 2.0, many of the same types of objects are all included in one class: the MovieClip class. This individualization of classes and the hierarchical structure of display lists have the following benefits: ■ More efficient rendering and reduced memory usage ■ Improved depth management ■ Full traversal of the display list ■ Off-list display objects ■ Easier subclassing of display objects These benefits are described in the next sections. More efficient rendering and smaller file sizes In ActionScript 1.0 and 2.0, you could draw shapes only in a MovieClip object. In ActionScript 3.0, there are simpler display object classes in which you can draw shapes. Because these ActionScript 3.0 display object classes do not include the full set of methods and properties that a MovieClip object includes, they are less taxing on memory and processor resources. For example, each MovieClip object includes properties for the timeline of the movie clip, whereas a Shape object does not. The properties for managing the timeline can use a lot of memory and processor resources. In ActionScript 3.0, using the Shape object results in better performance. The Shape object has less overhead than the more complex MovieClip object. Flash Player does not need to manage unused MovieClip properties, which improves speed and reduces the memory footprint the object uses.
  • 380. 380 Display programming Improved depth management In ActionScript 1.0 and 2.0, depth was managed through a linear depth management scheme and methods such as getNextHighestDepth(). ActionScript 3.0 includes the DisplayObjectContainer class, which has more convenient methods and properties for managing the depth of display objects. In ActionScript 3.0, when you move a display object to a new position in the child list of a DisplayObjectContainer instance, the other children in the display object container are repositioned automatically and assigned appropriate child index positions in the display object container. Also, in ActionScript 3.0 it is always possible to discover all of the child objects of any display object container. Every DisplayObjectContainer instance has a numChildren property, which lists the number of children in the display object container. And since the child list of a display object container is always an indexed list, you can examine every object in the list from index position 0 through the last index position (numChildren - 1). This was not possible with the methods and properties of a MovieClip object in ActionScript 1.0 and 2.0. In ActionScript 3.0, you can easily traverse the display list sequentially; there are no gaps in the index numbers of a child list of a display object container. Traversing the display list and managing the depth of objects is much easier than was possible in ActionScript 1.0 and 2.0. In ActionScript 1.0 and 2.0, a movie clip could contain objects with intermittent gaps in the depth order, which could make it difficult to traverse the list of object. In ActionScript 3.0, each child list of a display object container is cached internally as an array, resulting in very fast lookups (by index). Looping through all children of a display object container is also very fast. In ActionScript 3.0, you can also access children in a display object container by using the getChildByName() method of the DisplayObjectContainer class.
  • 381. Advantages of the display list approach 381 Full traversal of the display list In ActionScript 1.0 and 2.0, you could not access some objects, such as vector shapes, that were drawn in the Flash authoring tool. In ActionScript 3.0, you can access all objects on the display list—both those created using ActionScript and all display objects created in the Flash authoring tool. For details, see “Traversing the display list” on page 387. Off-list display objects In ActionScript 3.0, you can create display objects that are not on the visible display list. These are known as off-list display objects. A display object is added to the visible display list only when you call the addChild() or addChildAt() method of a DisplayObjectContainer instance that has already been added to the display list. You can use off-list display objects to assemble complex display objects, such as those that have multiple display object containers containing multiple display objects. By keeping display objects off-list, you can assemble complicated objects without using the processing time to render these display objects. You can then add an off-list display object to the display list when it is needed. Also, you can move a child of a display object container on and off the display list and to any desired position in the display list at will. Easier subclassing of display objects In ActionScript 1.0 and 2.0, you would often have to add new MovieClip objects to a SWF file to create basic shapes or to display bitmaps. In ActionScript 3.0, the DisplayObject class includes many built-in subclasses, including Shape and Bitmap. Because the classes in ActionScript 3.0 are more specialized for specific types of objects, it is easier to create basic subclasses of the built-in classes. For example, in order to draw a circle in ActionScript 2.0, you could create a CustomCircle class that extends the MovieClip class when an object of the custom class is instantiated. However, that class would also include a number of properties and methods from the MovieClip class (such as totalFrames) that do not apply to the class. In ActionScript 3.0, however, you can create a CustomCircle class that extends the Shape object, and as such does not include the unrelated properties and methods that are contained in the MovieClip class. The following code shows an example of a CustomCircle class: import flash.display.*; private class CustomCircle extends Shape {
  • 382. 382 Display programming var xPos:Number; var yPos:Number; var radius:Number; var color:uint; public function CustomCircle(xInput:Number, yInput:Number, rInput:Number, colorInput:uint) { xPos = xInput; yPos = yInput; radius = rInput; color = colorInput; this.graphics.beginFill(color); this.graphics.drawCircle(xPos, yPos, radius); } } Working with display objects Now that you understand the basic concepts of the Stage, display objects, display object containers, and the display list, this section provides you with some more specific information about working with display objects in ActionScript 3.0. Properties and methods of the DisplayObject class All display objects are subclasses of the DisplayObject class, and as such they inherit the properties and methods of the DisplayObject class. The properties inherited are basic properties that apply to all display objects. For example, each display object has an x property and a y property that specifies the object’s position in its display object container. You cannot create a DisplayObject instance using the DisplayObject class constructor. You must create another type of object (an object that is a subclass of the DisplayObject class), such as a Sprite, to instantiate an object with the new operator. Also, if you want to create a custom display object class, you must create a subclass of one of the display object subclasses that has a usable constructor function (such as the Shape class or the Sprite class). For more information, see the DisplayObject class description in the ActionScript 3.0 Language and Components Reference.
  • 383. Working with display objects 383 Adding display objects to the display list When you instantiate a display object, it will not appear on-screen (on the Stage) until you add the display object instance to a display object container that is on the display list. For example, in the following code, the myText TextField object would not be visible if you omitted the last line of code. In the last line of code, the this keyword must refer to a display object container that is already added to the display list. import flash.display.*; import flash.text.TextField; var myText:TextField = new TextField(); myText.text = "Buenos dias."; this.addChild(myText); When you add any visual element to the Stage, that element becomes a child of the Stage object. The first SWF file loaded in an application (for example, the one that you embed in an HTML page) is automatically added as a child of the Stage. It can be an object of any type that extends the Sprite class. Any display objects that you create without using ActionScript—for example, by adding an MXML tag in Adobe Flex Builder 2 or by placing an item on the Stage in Flash—are added to the display list. Although you do not add these display objects through ActionScript, you can access them through ActionScript. For example, the following code adjusts the width of an object named button1 that was added in the authoring tool (not through ActionScript): button1.width = 200; Working with display object containers If a DisplayObjectContainer object is deleted from the display list, or if it is moved or transformed in some other way, each display object in the DisplayObjectContainer is also deleted, moved, or transformed.
  • 384. 384 Display programming A display object container is itself a type of display object—it can be added to another display object container. For example, the following image shows a display object container, pictureScreen, that contains one outline shape and four other display object containers (of type PictureFrame): In order to have a display object appear in the display list, you must add it to a display object container that is on the display list. You do this by using the addChild() method or the addChildAt() method of the container object. For example, without the final line of the following code, the myTextField object would not be displayed: var myTextField:TextField = new TextField(); myTextField.text = "hello"; this.root.addChild(myTextField); In this code sample, this.root points to the MovieClip display object container that contains the code. In your actual code, you may specify a different container. Use the addChildAt() method to add the child to a specific position in the child list of the display object container. These zero-based index positions in the child list relate to the layering (the front-to-back order) of the display objects. For example, consider the following three display objects. Each object was created from a custom class called Ball. A shape defining the border of the pictureScreen display object container Four display object containers that are children of the pictureScreen object
  • 385. Working with display objects 385 The layering of these display objects in their container can be adjusted using the addChildAt() method. For example, consider the following code: ball_A = new Ball(0xFFCC00, "a"); ball_A.name = "ball_A"; ball_A.x = 20; ball_A.y = 20; container.addChild(ball_A); ball_B = new Ball(0xFFCC00, "b"); ball_B.name = "ball_B"; ball_B.x = 70; ball_B.y = 20; container.addChild(ball_B); ball_C = new Ball(0xFFCC00, "c"); ball_C.name = "ball_C"; ball_C.x = 40; ball_C.y = 60; container.addChildAt(ball_C, 1); After executing this code, the display objects are positioned as follows in the container DisplayObjectContainer object. Notice the layering of the objects. To reposition an object to the top of the display list, simply re-add it to the list. For example, after the previous code, to move ball_A to the top of the stack, use this line of code: container.addChild(ball_A); This code effectively removes ball_A from its location in container’s display list, and re- adds it to the top of the list—which has the end result of moving it to the top of the stack. You can use the getChildAt() method to verify the layer order of the display objects. The getChildAt() method returns child objects of a container based on the index number you pass it. For example, the following code reveals names of display objects at different positions in the child list of the container DisplayObjectContainer object: trace(container.getChildAt(0).name); // ball_A trace(container.getChildAt(1).name); // ball_C trace(container.getChildAt(2).name); // ball_B
  • 386. 386 Display programming If you remove a display object from the parent container’s child list, the higher elements on the list each move down a position in the child index. For example, continuing with the previous code, the following code shows how the display object that was at position 2 in the container DisplayObjectContainer moves to position 1 if a display object that is lower in the child list is removed: container.removeChild(ball_C); trace(container.getChildAt(0).name); // ball_A trace(container.getChildAt(1).name); // ball_B The removeChild() and removeChildAt() methods do not delete a display object instance entirely. They simply remove it from the child list of the container. The instance can still be referenced by another variable. (Use the delete operator to completely remove an object.) Because a display object has only one parent container, you can add an instance of a display object to only one display object container. For example, the following code shows that the display object tf1 can exist in only one container (in this case, a Sprite, which extends the DisplayObjectContainer class): tf1:TextField = new TextField(); tf2:TextField = new TextField(); tf1.name = "text 1"; tf2.name = "text 2"; container1:Sprite = new Sprite(); container2:Sprite = new Sprite(); container1.addChild(tf1); container1.addChild(tf2); container2.addChild(tf1); trace(container1.numChildren); // 1 trace(container1.getChildAt(0).name); // text 2 trace(container2.numChildren); // 1 trace(container2.getChildAt(0).name); // text 1 If you add a display object that is contained in one display object container to another display object container, it is removed from the first display object container’s child list. In addition to the methods described above, the DisplayObjectContainer class defines several methods for working with child display objects, including the following: ■ contains(): Determines whether a display object is a child of a DisplayObjectContainer. ■ getChildByName(): Retrieves a display object by name. ■ getChildIndex(): Returns the index position of a display object. ■ setChildIndex(): Changes the position of a child display object.
  • 387. Working with display objects 387 ■ swapChildren(): Swaps the front-to-back order of two display objects. ■ swapChildrenAt(): Swaps the front-to-back order of two display objects, specified by their index values. For more information, see the relevant entries in the ActionScript 3.0 Language and Components Reference. Recall that a display object that is off the display list—one that is not included in a display object container that is a child of the Stage—is known as an off-list display object. Traversing the display list As you’ve seen, the display list is a tree structure. At the top of the tree is the Stage, which can contain multiple display objects. Those display objects that are themselves display object containers can contain other display objects, or display object containers. Display Object Container Display ObjectDisplay Object Container Display Object Container Instance of the main class of the SWF file StageStage Display Object ContainerDisplay Object Display Object
  • 388. 388 Display programming The DisplayObjectContainer class includes properties and methods for traversing the display list, by means of the child lists of display object containers. For example, consider the following code, which adds two display objects, title and pict, to the container object (which is a Sprite, and the Sprite class extends the DisplayObjectContainer class): var container:Sprite = new Sprite(); var title:TextField = new TextField(); title.text = "Hello"; var pict:Loader = new Loader(); var url:URLRequest = new URLRequest("banana.jpg"); pict.load(url); pict.name = "banana loader"; container.addChild(title); container.addChild(pict); The getChildAt() method returns the child of the display list at a specific index position: trace(container.getChildAt(0) is TextField); // true You can also access child objects by name. Each display object has a name property, and if you don’t assign it, Flash Player assigns a default value, such as "instance1". For example, the following code shows how to use the getChildByName() method to access a child display object with the name "banana loader": trace(container.getChildByName("banana loader") is Loader); // true Using the getChildByName() method can result in slower performance than using the getChildAt() method. Since a display object container can contain other display object containers as child objects in its display list, you can traverse the full display list of the application as a tree. For example, in the code excerpt shown earlier, once the load operation for the pict Loader object is complete, the pict object will have one child display object, which is the bitmap, loaded. To access this bitmap display object, you can write pict.getChildAt(0). You can also write container.getChildAt(0).getChildAt(0) (since container.getChildAt(0) == pict). The following function provides an indented trace() output of the display list from a display object container: function traceDisplayList(container:DisplayObjectContainer, indentString:String = ""):void { var child:DisplayObject; for (var i:uint=0; i < container.numChildren; i++) { child = container.getChildAt(i); trace(indentString, child, child.name); if (container.getChildAt(i) is DisplayObjectContainer) {
  • 389. Working with display objects 389 traceDisplayList(DisplayObjectContainer(child), indentString + "") } } } Setting Stage properties The Stage class overrides most properties and methods of the DisplayObject class. If you call one of these overridden properties or methods, Flash Player throws an exception. For example, the Stage object does not have x or y properties, since its position is fixed as the main container for the application. The x and y properties refer to the position of a display object relative to its container, and since the Stage is not contained in another display object container, these properties do not apply. Controlling the playback frame rate The framerate property of the Stage class is used to set the frame rate for all SWF files loaded into the application. For more information, see the ActionScript 3.0 Language and Components Reference. Controlling Stage scaling When a Flash Player screen is resized, Flash Player automatically adjusts the Stage contents to compensate. The Stage class’s scaleMode property determines how the Stage contents are adjusted. This property can be set to four different values, defined as constants in the flash.display.StageScaleMode class. For three of the scaleMode values (StageScaleMode.EXACT_FIT, StageScaleMode.SHOW_ALL, and StageScaleMode.NO_BORDER), Flash Player will scale the contents of the Stage to fit within its boundaries.The three options differ in determining how the scaling is accomplished: ■ StageScaleMode.EXACT_FIT scales the SWF proportionally. ■ StageScaleMode.SHOW_ALL determines whether a border appears, like the black bars that appear when viewing a wide-screen movie on a standard television. ■ StageScaleMode.NO_BORDER determines whether the content can be partially cropped or not. NOTE Some properties and methods of the Stage class are not available to display objects that are not in the same security sandbox as the first SWF file loaded. For details, see “Stage security” on page 781.
  • 390. 390 Display programming Alternatively, if scaleMode is set to StageScaleMode.NO_SCALE, the Stage contents maintain their defined size when the viewer resizes the Flash Player window. In this scale mode only, the width and height properties of the Stage class can be used to determine the actual pixel dimensions of the resized Flash Player window. (In the other scale modes, the stageWidth and stageHeight properties always reflect the original width and height of the SWF.) In addition, when scaleMode is set to StageScaleMode.NO_SCALE and the SWF file is resized, the Stage class’s resize event is dispatched, allowing you to make adjustments accordingly. Consequently, having scaleMode set to StageScaleMode.NO_SCALE allows you to have greater control over how the screen contents adjust to the window resizing if you desire. For example, in a SWF containing a video and a control bar, you might want to make the control bar stay the same size when the Stage is resized, and only change the size of the video window to accommodate the Stage size change. This is demonstrated in the following example: // videoScreen is a display object (e.g. a Video instance) containing a // video; it is positioned at the top-left corner of the Stage, and // it should resize when the SWF resizes. // controlBar is a display object (e.g. a Sprite) containing several // buttons; it should stay positioned at the bottom-left corner of the // Stage (below videoScreen) and it should not resize when the SWF // resizes. import flash.display.Stage; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; var swfStage:Stage = videoScreen.stage; swfStage.scaleMode = StageScaleMode.NO_SCALE; swfStage.align = StageAlign.TOP_LEFT; function resizeDisplay(event:Event):void { var swfWidth:int = swfStage.stageWidth; var swfHeight:int = swfStage.stageHeight; // Resize the video window. var newVideoHeight:Number = swfHeight - controlBar.height; videoScreen.height = newVideoHeight; videoScreen.scaleX = videoScreen.scaleY; // Reposition the control bar. controlBar.y = newVideoHeight; } swfStage.addEventListener(Event.RESIZE, resizeDisplay);
  • 391. Working with display objects 391 Working with full-screen mode Full-screen mode allows you to make a SWF fill a viewer’s entire monitor, without any borders, menu bars, and so forth. The Stage class’s displayState property is used to toggle full-screen mode on and off for a SWF. The displayState property can be set to one of the values defined by the constants in the flash.display.StageDisplayState class. To turn on full- screen mode, set displayState to StageDisplayState.FULL_SCREEN: // mySprite is a Sprite instance, already added to the display list mySprite.stage.displayState = StageDisplayState.FULL_SCREEN; To exit full-screen mode, set the displayState property to StageDisplayState.NORMAL: mySprite.stage.displayState = StageDisplayState.NORMAL; In addition, a user can choose to leave full-screen mode by switching focus to a different window or by using one of several key combinations: the Esc key (all platforms), Ctrl-W (Windows), Command-W (Mac), or Alt-F4 (Windows). Stage scaling behavior for full-screen mode is the same as under normal mode; the scaling is controlled by the Stage class’s scaleMode property. As always, if the scaleMode property is set to StageScaleMode.NO_SCALE, the Stage’s stageWidth and stageHeight properties change to reflect the size of the screen area occupied by the SWF (the entire screen, in this case). You can use the Stage class’s fullScreen event to detect and respond when full-screen mode is turned on or off. For example, you might want to reposition, add, or remove items from the screen when entering or leaving full-screen mode, as in this example: import flash.events.FullScreenEvent; function fullScreenRedraw(event:FullScreenEvent):void { if (event.fullScreen) { // Remove input text fields. // Add a button that closes full-screen mode. } else { // Re-add input text fields. // Remove the button that closes full-screen mode. } } mySprite.stage.addEventListener(FullScreenEvent.FULL_SCREEN, fullScreenRedraw); As this code shows, the event object for the fullScreen event is an instance of the flash.events.FullScreenEvent class, which includes a fullScreen property indicating whether full-screen mode is enabled (true) or not (false).
  • 392. 392 Display programming When working with full-screen mode in ActionScript, you’ll want to keep the following considerations in mind: ■ Full-screen mode can only be initiated through ActionScript in response to a mouse click (including right-click) or keypress. ■ For users with multiple monitors, the SWF content will expand to fill only one monitor. Flash Player uses a metric to determine which monitor contains the greatest portion of the SWF, and uses that monitor for full-screen mode. ■ For a SWF file embedded in an HTML page, the HTML code to embed Flash Player must include a param tag and embed attribute with the name allowFullScreen and value true, like this: <object> ... <param name="allowFullScreen" value="true" /> <embed ... allowfullscreen="true" /> </object> If you are using JavaScript in a web page to generate the SWF-embedding tags, you must alter the JavaScript to add the allowFullScreen param tag and attribute. For example, if your HTML page uses the AC_FL_RunContent() function (which is used by both Flex Builder and Flash-generated HTML pages), you should add the allowFullScreen parameter to that function call as follows: AC_FL_RunContent( ... 'allowFullScreen','true', ... ); //end AC code This does not apply to SWF files running in the stand-alone Flash Player. ■ All keyboard-related ActionScript, such as keyboard events and text entry in TextField instances, is disabled in full-screen mode. The exception is the keyboard shortcuts that close full-screen mode. There are a few additional security-related restrictions you’ll want to understand, too. These are described in “Security sandboxes” on page 770. Handling events for display objects The DisplayObject class inherits from the EventDispatcher class. This means that every display object can participate fully in the event model (described in Chapter 10, “Handling events,” on page 313). Every display object can use its addEventListener() method— inherited from the EventDispatcher class—to listen for a particular event, but only if the listening object is part of the event flow for that event.
  • 393. Working with display objects 393 When Flash Player dispatches an event object, that event object makes a round-trip journey from the Stage to the display object where the event occurred. For example, if a user clicks on a display object named child1, Flash Player dispatches an event object from the Stage through the display list hierarchy down to the child1 display object. The event flow is conceptually divided into three phases, as illustrated in this diagram: For more information, see Chapter 10, “Handling events,” on page 313. One important issue to keep in mind when working with display object events is the effect that event listeners can have on whether display objects are automatically removed from memory (garbage collected) when they’re removed from the display list. If a display object has objects subscribed as listeners to its events, that display object will not be removed from memory even when it’s removed from the display list, because it will still have references to those listener objects. For more information, see “Managing event listeners” on page 331. Choosing a DisplayObject subclass With several options to choose from, one of the important decisions you’ll make when you’re working with display objects is which display object to use for what purpose. Here are some guidelines to help you decide. These same suggestions apply whether you need an instance of a class or you’re choosing a base class for a class you’re creating: ■ If you don’t need an object that can be a container for other display objects (that is, you just need one that serves as a stand-alone screen element), choose one of these DisplayObject or InteractiveObject subclasses, depending on what it will be used for: ■ Bitmap for displaying a bitmap image. ■ TextField for adding text. ■ Video for displaying video. Stage Parent Node Child1 Node Child2 Node Capture Phase Bubbling Phase Target Phase
  • 394. 394 Display programming ■ Shape for a “canvas” for drawing content on-screen. In particular, if you want to create an instance for drawing shapes on the screen, and it won’t be a container for other display objects, you’ll gain significant performance benefits using Shape instead of Sprite or MovieClip. ■ MorphShape, StaticText, or SimpleButton for Flash authoring-specific items. (You can’t create instances of these classes programmatically, but you can create variables with these data types to refer to items created using the Flash authoring program.) ■ If you need a variable to refer to the main Stage, use the Stage class as its data type. ■ If you need a container for loading an external SWF file or image file, use a Loader instance. The loaded content will be added to the display list as a child of the Loader instance. Its data type will depend on the nature of the loaded content, as follows: ■ A loaded image will be a Bitmap instance. ■ A loaded SWF file written in ActionScript 3.0 will be a Sprite or MovieClip instance (or an instance of a subclass of those classes, as specified by the content creator). ■ A loaded SWF file written in ActionScript 1.0 or ActionScript 2.0 will be an AVM1Movie instance. ■ If you need an object to serve as a container for other display objects (whether or not you’ll also be drawing onto the display object using ActionScript), choose one of the DisplayObjectContainer subclasses: ■ Sprite if the object will be created using only ActionScript, or as the base class for a custom display object that will be created and manipulated solely with ActionScript. ■ MovieClip if you’re creating a variable to refer to a movie clip symbol created in the Flash authoring tool. ■ If you are creating a class that will be associated with a movie clip symbol in the Flash library, choose one of these DisplayObjectContainer subclasses as your class’s base class: ■ MovieClip if the associated movie clip symbol has content on more than one frame ■ Sprite if the associated movie clip symbol has content only on the first frame
  • 395. Manipulating display objects 395 Manipulating display objects Regardless of which display object you choose to use, there are a number of manipulations that all display objects have in common as elements that are displayed on the screen. For example, they can all be positioned on the screen, moved forward or backward in the stacking order of display objects, scaled, rotated, and so forth. Because all display objects inherit this functionality from their common base class (DisplayObject), this functionality behaves the same whether you’re manipulating a TextField instance, a Video instance, a Shape instance, or any other display object. The following sections detail several of these common display object manipulations. Changing position The most basic manipulation to any display object is positioning it on the screen. To set a display object’s position, change the object’s x and y properties. myShape.x = 17; myShape.y = 212; The display object positioning system treats the Stage as a Cartesian coordinate system (the common grid system with a horizontal x axis and vertical y axis). The origin of the coordinate system (the 0,0 coordinate where the x and y axes meet) is at the top-left corner of the Stage. From there, x values are positive going right and negative going left, while (in contrast to typical graphing systems) y values are positive going down and negative going up. For example, the previous lines of code move the object myShape to the x coordinate 17 (17 pixels to the right of the origin) and y coordinate 212 (212 pixels below the origin). By default, when a display object is created using ActionScript, the x and y properties are both set to 0, placing the object at the top-left corner of its parent content. Changing position relative to the Stage It’s important to remember that the x and y properties always refer to the position of the display object relative to the 0,0 coordinate of its parent display object’s axes. So for a Shape instance (such as a circle) contained inside a Sprite instance, setting the Shape object’s x and y properties to 0 will place the circle at the top-left corner of the Sprite, which is not necessarily the top-left corner of the Stage. To position an object relative to the global Stage coordinates, you can use the globalToLocal() method of any display object to convert coordinates from global (Stage) coordinates to local (display object container) coordinates, like this: // Position the shape at the top-left corner of the Stage, // regardless of where its parent is located.
  • 396. 396 Display programming // Create a Sprite, positioned at x:200 and y:200. var mySprite:Sprite = new Sprite(); mySprite.x = 200; mySprite.y = 200; this.addChild(mySprite); // Draw a dot at the Sprite's 0,0 coordinate, for reference. mySprite.graphics.lineStyle(1, 0x000000); mySprite.graphics.beginFill(0x000000); mySprite.graphics.moveTo(0, 0); mySprite.graphics.lineTo(1, 0); mySprite.graphics.lineTo(1, 1); mySprite.graphics.lineTo(0, 1); mySprite.graphics.endFill(); // Create the circle Shape instance. var circle:Shape = new Shape(); mySprite.addChild(circle); // Draw a circle with radius 50 and center point at x:50, y:50 in the Shape. circle.graphics.lineStyle(1, 0x000000); circle.graphics.beginFill(0xff0000); circle.graphics.drawCircle(50, 50, 50); circle.graphics.endFill(); // Move the Shape so its top-left corner is at the Stage's 0, 0 coordinate. var stagePoint:Point = new Point(0, 0); var targetPoint:Point = mySprite.globalToLocal(stagePoint); circle.x = targetPoint.x; circle.y = targetPoint.y; You can likewise use the DisplayObject class’s localToGlobal() method to convert local coordinates to Stage coordinates. Creating drag-and-drop interaction One common reason for moving a display object is to create a drag-and-drop interaction, so that when the user clicks an object, the object moves as the mouse moves, until the mouse button is released. Drag-and-drop interaction can be created in two ways in ActionScript. In either case, two mouse events are used: when the mouse button is pressed down, the object is told to follow the mouse cursor, and when it’s released, the object is told to stop following the mouse cursor. The first way, using the startDrag() method, is simpler, but more limited. When the mouse button is pressed, the startDrag() method of the display object to be dragged is called. When the mouse button is released, the stopDrag() method is called. // This code creates a drag-and-drop interaction using the startDrag()
  • 397. Manipulating display objects 397 // technique. // square is a DisplayObject (e.g. a MovieClip or Sprite instance). import flash.events.MouseEvent; // This function is called when the mouse button is pressed. function startDragging(event:MouseEvent):void { square.startDrag(); } // This function is called when the mouse button is released. function stopDragging(event:MouseEvent):void { square.stopDrag(); } square.addEventListener(MouseEvent.MOUSE_DOWN, startDragging); square.addEventListener(MouseEvent.MOUSE_UP, stopDragging); This technique suffers from one fairly significant limitation: only one item at a time can be dragged using startDrag(). If one display object is being dragged and the startDrag() method is called on another display object, the first display object stops following the mouse immediately. For example, if the startDragging() function is changed as shown here, only the circle object will be dragged, in spite of the square.startDrag() method call: function startDragging(event:MouseEvent):void { square.startDrag(); circle.startDrag(); } As a consequence of the fact that only one object can be dragged at a time using startDrag(), the stopDrag() method can be called on any display object and it stops whatever object is currently being dragged. If you need to drag more than one display object, or to avoid the possibility of conflicts where more than one object might potentially use startDrag(), it’s best to use the mouse-following technique to create the dragging effect. With this technique, when the mouse button is pressed, a function is subscribed as a listener to the mouseMove event of the Stage. This function, which is then called every time the mouse moves, causes the dragged object to jump to the x, y coordinate of the mouse. Once the mouse button is released, the function is unsubscribed as a listener, meaning it is no longer called when the mouse moves and the object stops following the mouse cursor. Here is some code that demonstrates this technique: // This code creates a drag-and-drop interaction using the mouse-following // technique. // circle is a DisplayObject (e.g. a MovieClip or Sprite instance).
  • 398. 398 Display programming import flash.events.MouseEvent; var offsetX:Number; var offsetY:Number; // This function is called when the mouse button is pressed. function startDragging(event:MouseEvent):void { // Record the difference (offset) between where // the cursor was when the mouse button was pressed and the x, y // coordinate of the circle when the mouse button was pressed. offsetX = event.stageX - circle.x; offsetY = event.stageY - circle.y; // tell Flash Player to start listening for the mouseMove event stage.addEventListener(MouseEvent.MOUSE_MOVE, dragCircle); } // This function is called when the mouse button is released. function stopDragging(event:MouseEvent):void { // Tell Flash Player to stop listening for the mouseMove event. stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragCircle); } // This function is called every time the mouse moves, // as long as the mouse button is pressed down. function dragCircle(event:MouseEvent):void { // Move the circle to the location of the cursor, maintaining // the offset between the cursor’s location and the // location of the dragged object. circle.x = event.stageX - offsetX; circle.y = event.stageY - offsetY; // Instruct Flash Player to refresh the screen after this event. event.updateAfterEvent(); } circle.addEventListener(MouseEvent.MOUSE_DOWN, startDragging); circle.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
  • 399. Manipulating display objects 399 In addition to making a display object follow the mouse cursor, a common part of drag-and- drop interaction includes moving the dragged object to the front of the display, so that it appears to be floating above all the other objects. For example, suppose you have two objects, a circle and a square, that both have a drag-and-drop interaction. If the circle happens to be below the square on the display list, and you click and drag the circle so that the cursor is over the square, the circle will appear to slide behind the square, which breaks the drag-and-drop illusion. Instead, you can make it so that when the circle is clicked, it moves to the top of the display list, and thus always appears on top of any other content. The following code (adapted from the previous example) creates a drag-and-drop interaction for two display objects, a circle and a square. Whenever the mouse button is pressed over either one, that item is moved to the top of the Stage’s display list, so that the dragged item always appears on top. Code that is new or changed from the previous listing appears in boldface. // This code creates a drag-and-drop interaction using the mouse-following // technique. // circle and square are DisplayObjects (e.g. MovieClip or Sprite // instances). import flash.display.DisplayObject; import flash.events.MouseEvent; var offsetX:Number; var offsetY:Number; var draggedObject:DisplayObject; // This function is called when the mouse button is pressed. function startDragging(event:MouseEvent):void { // remember which object is being dragged draggedObject = DisplayObject(event.target); // Record the difference (offset) between where the cursor was when // the mouse button was pressed and the x, y coordinate of the // dragged object when the mouse button was pressed. offsetX = event.stageX - draggedObject.x; offsetY = event.stageY - draggedObject.y; // move the selected object to the top of the display list stage.addChild(draggedObject); // Tell Flash Player to start listening for the mouseMove event. stage.addEventListener(MouseEvent.MOUSE_MOVE, dragObject); } // This function is called when the mouse button is released. function stopDragging(event:MouseEvent):void
  • 400. 400 Display programming { // Tell Flash Player to stop listening for the mouseMove event. stage.removeEventListener(MouseEvent.MOUSE_MOVE, dragObject); } // This function is called every time the mouse moves, // as long as the mouse button is pressed down. function dragObject(event:MouseEvent):void { // Move the dragged object to the location of the cursor, maintaining // the offset between the cursor’s location and the location // of the dragged object. draggedObject.x = event.stageX - offsetX; draggedObject.y = event.stageY - offsetY; // Instruct Flash Player to refresh the screen after this event. event.updateAfterEvent(); } circle.addEventListener(MouseEvent.MOUSE_DOWN, startDragging); circle.addEventListener(MouseEvent.MOUSE_UP, stopDragging); square.addEventListener(MouseEvent.MOUSE_DOWN, startDragging); square.addEventListener(MouseEvent.MOUSE_UP, stopDragging); To extend this effect further, such as for a game where tokens or cards are moved among piles, you could add the dragged object to the Stage’s display list when it’s “picked up,” and then add it to another display list—such as the “pile” where it is dropped—when the mouse button is released. Finally, to enhance the effect, you could apply a drop shadow filter to the display object when it is clicked (when you start dragging it) and remove the drop shadow when the object is released. For details on using the drop shadow filter and other display object filters in ActionScript, see Chapter 15, “Filtering display objects,” on page 465. Panning and scrolling display objects If you have a display object that is too large for the area in which you want it to display it, you can use the scrollRect property to define the viewable area of the display object. In addition, by changing the scrollRect property in response to user input, you can cause the content to pan left and right or scroll up and down.
  • 401. Manipulating display objects 401 The scrollRect property is an instance of the Rectangle class, which is a class that combines the values needed to define a rectangular area as a single object. To initially define the viewable area of the display object, create a new Rectangle instance and assign it to the display object’s scrollRect property. Later, to scroll or pan, you read the scrollRect property into a separate Rectangle variable, and change the desired property (for instance, change the Rectangle instance’s x property to pan or y property to scroll). Then you reassign that Rectangle instance to the scrollRect property to notify the display object of the changed value. For example, the following code defines the viewable area for a TextField object named bigText that is too tall to fit in the SWF file’s boundaries. When the two buttons named up and down are clicked, they call functions that cause the contents of the TextField object to scroll up or down by modifying the y property of the scrollRect Rectangle instance. import flash.events.MouseEvent; import flash.geom.Rectangle; // Define the initial viewable area of the TextField instance: // left: 0, top: 0, width: TextField’s width, height: 350 pixels. bigText.scrollRect = new Rectangle(0, 0, bigText.width, 350); // Cache the TextField as a bitmap to improve performance. bigText.cacheAsBitmap = true; // called when the "up" button is clicked function scrollUp(event:MouseEvent):void { // Get access to the current scroll rectangle. var rect:Rectangle = bigText.scrollRect; // Decrease the y value of the rectangle by 20, effectively // shifting the rectangle down by 20 pixels. rect.y -= 20; // Reassign the rectangle to the TextField to "apply" the change. bigText.scrollRect = rect; } // called when the "down" button is clicked function scrollDown(event:MouseEvent):void { // Get access to the current scroll rectangle. var rect:Rectangle = bigText.scrollRect; // Increase the y value of the rectangle by 20, effectively // shifting the rectangle up by 20 pixels. rect.y += 20; // Reassign the rectangle to the TextField to "apply" the change. bigText.scrollRect = rect; }
  • 402. 402 Display programming up.addEventListener(MouseEvent.CLICK, scrollUp); down.addEventListener(MouseEvent.CLICK, scrollDown); As this example illustrates, when you work with the scrollRect property of a display object, it’s best to specify that Flash Player should cache the display object’s content as a bitmap, using the cacheAsBitmap property. When you do so, Flash Player doesn’t have to re-draw the entire contents of the display object each time it is scrolled, and can instead use the cached bitmap to render the necessary portion directly to the screen. For details, see “Caching display objects” on page 405. Manipulating size and scaling objects You can measure and manipulate the size of a display object in two ways, using either the dimension properties (width and height) or the scale properties (scaleX and scaleY). Every display object has a width property and a height property, which are initially set to the size of the object in pixels. You can read the values of those properties to measure the size of the display object. You can also specify new values to change the size of the object, as follows: // Resize a display object. square.width = 420; square.height = 420; // Determine the radius of a circle display object. var radius:Number = circle.width / 2; Changing the height or width of a display object causes the object to scale, meaning its contents stretch or squeeze to fit in the new area. If the display object contains only vector shapes, those shapes will be redrawn at the new scale, with no loss in quality. Any bitmap graphic elements in the display object will be scaled rather than redrawn. So, for example, a digital photo whose width and height are increased beyond the actual dimensions of the pixel information in the image will be pixelated, making it look jagged. When you change the width or height properties of a display object, Flash Player updates the scaleX and scaleY properties of the object as well. These properties represent the relative size of the display object compared to its original size. The scaleX and scaleY properties use fraction (decimal) values to represent percentage. For example, if a display object’s width has been changed so that it’s half as wide as its original size, the object’s scaleX property will have the value .5, meaning 50 percent. If its height has been doubled, its scaleY property will have the value 2, meaning 200 percent. // circle is a display object whose width and height are 150 pixels. // At original size, scaleX and scaleY are 1 (100%). trace(circle.scaleX); // output: 1 trace(circle.scaleY); // output: 1
  • 403. Manipulating display objects 403 // When you change the width and height properties, // Flash Player changes the scaleX and scaleY properties accordingly. circle.width = 100; circle.height = 75; trace(circle.scaleX); // output: 0.6622516556291391 trace(circle.scaleY); // output: 0.4966887417218543 Size changes are not proportional. In other words, if you change the height of a square but not its width, its proportions will no longer be the same, and it will be a rectangle instead of a square. If you want to make relative changes to the size of a display object, you can set the values of the scaleX and scaleY properties to resize the object, as an alternative to setting the width or height properties. For example, this code changes the width of the display object named square, and then alters the vertical scale (scaleY) to match the horizontal scale, so that the size of the square stays proportional. // Change the width directly. square.width = 150; // Change the vertical scale to match the horizontal scale, // to keep the size proportional. square.scaleY = square.scaleX; Controlling distortion when scaling Normally when a display object is scaled (for example, stretched horizontally), the resulting distortion is spread equally across the object, so that each part is stretched the same amount. For graphics and design elements, this is probably what you want. However, sometimes it’s preferable to have control over which portions of the display object stretch and which portions remain unchanged. One common example of this is a button that’s a rectangle with rounded corners. With normal scaling, the corners of the button will stretch, making the corner radius change as the button resizes.
  • 404. 404 Display programming However, in this case it would be preferable to have control over the scaling—to be able to designate certain areas which should scale (the straight sides and middle) and areas which shouldn’t (the corners)—so that scaling happens without visible distortion. You can use 9-slice scaling (Scale-9) to create display objects where you have control over how the objects scale. With 9-slice scaling, the display object is divided into nine separate rectangles (a 3 by 3 grid, like the grid of a tic-tac-toe board). The rectangles aren’t necessarily the same size—you designate where the grid lines are placed. Any content that lies in the four corner rectangles (such as the rounded corners of a button) will not be stretched or compressed when the display object scales. The top-center and bottom-center rectangles will scale horizontally but not vertically, while the left-middle and right-middle rectangles will scale vertically but not horizontally. The center rectangle will scale both horizontally and vertically. Keeping this in mind, if you’re creating a display object and you want certain content to never scale, you just have to make sure that the dividing lines of the 9-slice scaling grid are placed so that the content ends up in one of the corner rectangles. In ActionScript, setting a value for the scale9Grid property of a display object turns on 9- slice scaling for the object and defines the size of the rectangles in the object’s Scale-9 grid. You use an instance of the Rectangle class as the value for the scale9Grid property, as follows: myButton.scale9Grid = new Rectangle(32, 27, 71, 64);
  • 405. Manipulating display objects 405 The four parameters of the Rectangle constructor are the x coordinate, y coordinate, width, and height. In this example, the rectangle’s top-left corner is placed at the point x: 32, y: 27 on the display object named myButton. The rectangle is 71 pixels wide and 65 pixels tall (so its right edge is at the x coordinate 103 on the display object and its bottom edge is at the y coordinate 92 on the display object). The actual area contained in the region defined by the Rectangle instance represents the center rectangle of the Scale-9 grid. The other rectangles are calculated by Flash Player by extending the sides of the Rectangle instance, as shown here: In this case, as the button scales up or down, the rounded corners will not stretch or compress, but the other areas will adjust to accommodate the scaling. Caching display objects As your designs in Flash grow in size, whether you are creating an application or complex scripted animations, you need to consider performance and optimization. When you have content that remains static (such as a rectangle Shape instance), Flash does not optimize the content. Therefore, when you change the position of the rectangle, Flash redraws the entire Shape instance. myButton.width = 131; myButton.height = 106; myButton.width = 73; myButton.height = 69; myButton.width = 54; myButton.height = 141;
  • 406. 406 Display programming You can cache specified display objects to improve the performance of your SWF file. The display object is a surface, essentially a bitmap version of the instance’s vector data, which is data that you do not intend to change much over the course of your SWF file. Therefore, instances with caching turned on are not continually redrawn as the SWF file plays, letting the SWF file render quickly. Setting a display object’s cacheAsBitmap property to true makes the display object cache a bitmap representation of itself. Flash creates a surface object for the instance, which is a cached bitmap instead of vector data. If you change the bounds of the display object, the surface is recreated instead of resized. Surfaces can nest within other surfaces. The child surface copies its bitmap onto its parent surface. For more information, see “Enabling bitmap caching” on page 408. The DisplayObject class’s opaqueBackground property and scrollRect property are related to bitmap caching using the cacheAsBitmap property. Although these three properties are independent of each other, the opaqueBackground and scrollRect properties work best when an object is cached as a bitmap—you see performance benefits for the opaqueBackground and scrollRect properties only when you set cacheAsBitmap to true. For more information about scrolling display object content, see “Panning and scrolling display objects” on page 400. For more information about setting an opaque background, see “Setting an opaque background color” on page 409. For information on alpha channel masking, which requires you to set the cacheAsBitmap property to true, see “Alpha channel masking” on page 415. When to enable caching Enabling caching for a display object creates a surface, which has several advantages, such as helping complex vector animations to render fast. There are several scenarios in which you will want to enable caching. It might seem as though you would always want to enable caching to improve the performance of your SWF files; however, there are situations in which enabling caching does not improve performance, or can even decrease it. This section describes scenarios in which caching should be used, and when to use regular display objects. NOTE You can update the vector data, at which time the surface is recreated. Therefore, the vector data cached in the surface does not need to remain the same for the entire SWF file.
  • 407. Manipulating display objects 407 Overall performance of cached data depends on how complex the vector data of your instances are, how much of the data you change, and whether or not you set the opaqueBackground property. If you are changing small regions, the difference between using a surface and using vector data could be negligible. You might want to test both scenarios with your work before you deploy the application. When to use bitmap caching The following are typical scenarios in which you might see significant benefits when you enable bitmap caching. ■ Complex background image: An application that contains a detailed and complex background image of vector data (perhaps an image where you applied the trace bitmap command, or artwork that you created in Adobe Illustrator®). You might animate characters over the background, which slows the animation because the background needs to continuously regenerate the vector data. To improve performance, you can set the opaqueBackground property of the background display object to true. The background is rendered as a bitmap and can be redrawn quickly, so that your animation plays much faster. ■ Scrolling text field: An application that displays a large amount of text in a scrolling text field. You can place the text field in a display object that you set as scrollable with scrolling bounds (the scrollRect property). This enables fast pixel scrolling for the specified instance. When a user scrolls the display object instance, Flash shifts the scrolled pixels up and generates the newly exposed region instead of regenerating the entire text field. ■ Windowing system: An application with a complex system of overlapping windows. Each window can be open or closed (for example, web browser windows). If you mark each window as a surface (by setting the cacheAsBitmap property to true), each window is isolated and cached. Users can drag the windows so that they overlap each other, and each window doesn’t need to regenerate the vector content. ■ Alpha channel masking: When you are using alpha channel masking, you must set the cacheAsBitmap property to true. For more information, see “Alpha channel masking” on page 415. Enabling bitmap caching in all of these scenarios improves the responsiveness and interactivity of the application by optimizing the vector graphics. In addition, whenever you apply a filter to a display object, cacheAsBitmap is automatically set to true by Flash Player, even if you explicitly set it to false. If you clear all the filters from the display object, the cacheAsBitmap property returns to the value it was last set to.
  • 408. 408 Display programming When to avoid using bitmap caching Misusing this feature could negatively affect your SWF file. When you use bitmap caching, remember the following guidelines: ■ Do not overuse surfaces (display objects with caching enabled). Each surface uses more memory than a regular display object, which means that you should only enable surfaces when you need to improve rendering performance. A cached bitmap can use significantly more memory than a regular display object. For example, if a Sprite instance on the Stage is 250 pixels by 250 pixels in size, when cached it might use 250 KB instead of 1 KB when it’s a regular (un-cached) Sprite instance. ■ Avoid zooming into cached surfaces. If you overuse bitmap caching, a large amount of memory is consumed (see previous bullet), especially if you zoom in on the content. ■ Use surfaces for display object instances that are largely static (non-animating). You can drag or move the instance, but the contents of the instance should not animate or change a lot. (Animation or changing content are more likely with a MovieClip instance containing animation or a Video instance.) For example, if you rotate or transform an instance, the instance changes between the surface and vector data, which is difficult to process and negatively affects your SWF file. ■ If you mix surfaces with vector data, it increases the amount of processing that Flash Player (and sometimes the computer) needs to do. Group surfaces together as much as possible—for example, when you create windowing applications. Enabling bitmap caching To enable bitmap caching for a display object, you set its cacheAsBitmap property to true: mySprite.cacheAsBitmap = true; After you set the cacheAsBitmap property to true, you might notice that the display object automatically pixel-snaps to whole coordinates. When you test the SWF file, you should notice that any animation performed on a complex vector image renders much faster. A surface (cached bitmap) is not created, even if cacheAsBitmap is set to true, if one or more of the following occurs: ■ The bitmap is greater than 2880 pixels in height or width. ■ The bitmap fails to allocate (because of an out-of-memory error).
  • 409. Manipulating display objects 409 Setting an opaque background color You can set an opaque background for a display object. For example, when your SWF has a background that contains complex vector art, you can set the opaqueBackground property to a specified color (typically the same color as the Stage). The color is specified as a number (commonly a hexadecimal color value). The background is then treated as a bitmap, which helps optimize performance. When you set cacheAsBitmap to true, and also set the opaqueBackground property to a specified color, the opaqueBackground property allows the internal bitmap to be opaque and rendered faster. If you do not set cacheAsBitmap to true, the opaqueBackground property adds an opaque vector-square shape to the background of the display object. It does not create a bitmap automatically. The following example shows how to set the background of a display object to optimize performance: myShape.cacheAsBitmap = true; myShape.opaqueBackground = 0xFF0000; In this case, the background color of the Shape named myShape is set to red (0xFF0000). Assuming the Shape instance contains a drawing of a green triangle, on a Stage with a white background, this would show up as a green triangle with red in the empty space in the Shape instance’s bounding box (the rectangle that completely encloses the Shape). Of course, this code would make more sense if it were used with a Stage with a solid red background. On another colored background, that color would be specified instead. For example, in a SWF with a white background, the opaqueBackground property would most likely be set to 0xFFFFFF, or pure white. Applying blending modes Blending modes involve combining the colors of one image (the base image) with the colors of another image (the blend image) to produce a third image—the resulting image is the one that is actually displayed on the screen. Each pixel value in an image is processed with the corresponding pixel value of the other image to produce a pixel value for that same position in the result.
  • 410. 410 Display programming Every display object has a blendMode property that can be set to one of the following blending modes. These are constants defined in the BlendMode class. Alternatively, you can use the String values (in parentheses) that are the actual values of the constants. ■ BlendMode.ADD ("add"): Commonly used to create an animated lightening dissolve effect between two images. ■ BlendMode.ALPHA ("alpha"): Commonly used to apply the transparency of the foreground on the background. ■ BlendMode.DARKEN ("darken"): Commonly used to superimpose type. ■ BlendMode.DIFFERENCE ("difference"): Commonly used to create more vibrant colors. ■ BlendMode.ERASE ("erase"): Commonly used to cut out (erase) part of the background using the foreground alpha. ■ BlendMode.HARDLIGHT ("hardlight"): Commonly used to create shading effects. ■ BlendMode.INVERT ("invert"): Used to invert the background. ■ BlendMode.LAYER ("layer"): Used to force the creation of a temporary buffer for precomposition for a particular display object. ■ BlendMode.LIGHTEN ("lighten"): Commonly used to superimpose type. ■ BlendMode.MULTIPLY ("multiply"): Commonly used to create shadows and depth effects. ■ BlendMode.NORMAL ("normal"): Used to specify that the pixel values of the blend image override those of the base image. ■ BlendMode.OVERLAY ("overlay"): Commonly used to create shading effects. ■ BlendMode.SCREEN ("screen"): Commonly used to create highlights and lens flares. ■ BlendMode.SUBTRACT ("subtract"): Commonly used to create an animated darkening dissolve effect between two images. Adjusting DisplayObject colors You can use the methods of the ColorTransform class (flash.geom.ColorTransform) to adjust the color of a display object. Each display object has a transform property, which is an instance of the Transform class, and contains information about various transformations that are applied to the display object (such as rotation, changes in scale or position, and so forth). In addition to its information about geometric transformations, the Transform class also includes a colorTransform property, which is an instance of the ColorTransform class, and provides access to make color adjustments to the display object. To access the color transformation information of a display object, you can use code such as this: var colorInfo:ColorTransform = myDisplayObject.transform.colorTransform;
  • 411. Manipulating display objects 411 Once you’ve created a ColorTransform instance, you can read its property values to find out what color transformations have already been applied, or you can set those values to make color changes to the display object. To update the display object after any changes, you must reassign the ColorTransform instance back to the transform.colorTransform property. var colorInfo:ColorTransform = my DisplayObject.transform.colorTransform; // Make some color transformations here. // Commit the change. myDisplayObject.transform.colorTransform = colorInfo; Setting color values with code The color property of the ColorTransform class can be used to assign a specific red, green, blue (RGB) color value to the display object. The following example uses the color property to change the color of the display object named square to blue, when the user clicks a button named blueBtn: // square is a display object on the Stage. // blueBtn, redBtn, greenBtn, and blackBtn are buttons on the Stage. import flash.events.MouseEvent; import flash.geom.ColorTransform; // Get access to the ColorTransform instance associated with square. var colorInfo:ColorTransform = square.transform.colorTransform; // This function is called when blueBtn is clicked. function makeBlue(event:MouseEvent):void { // Set the color of the ColorTransform object. colorInfo.color = 0x003399; // apply the change to the display object square.transform.colorTransform = colorInfo; } blueBtn.addEventListener(MouseEvent.CLICK, makeBlue); Note that when you change a display object’s color using the color property, it completely changes the color of the entire object, regardless of whether the object previously had multiple colors. For example, if there is a display object containing a green circle with black text on top, setting the color property of that object’s associated ColorTransform instance to a shade of red will make the entire object, circle and text, turn red (so the text will no longer be distinguishable from the rest of the object).
  • 412. 412 Display programming Altering color and brightness effects with code Suppose you have a display object with multiple colors (for example, a digital photo) and you don’t want to completely recolor the object; you just want to adjust the color of a display object based on the existing colors. In this scenario, the ColorTransform class includes a series of multiplier and offset properties that you can use to make this type of adjustment. The multiplier properties, named redMultiplier, greenMultiplier, blueMultiplier, and alphaMultiplier, work like colored photographic filters (or colored sunglasses), amplifying or diminishing certain colors in the display object. The offset properties (redOffset, greenOffset, blueOffset, and alphaOffset) can be used to add extra amounts of a certain color to the object, or to specify the minimum value that a particular color can have. These multiplier and offset properties are identical to the advanced color settings that are available for movie clip symbols in the Flash authoring tool when you choose Advanced from the Color pop-up menu on the Property inspector. The following code loads a JPEG image and applies a color transformation to it, which adjusts the red and green channels as the mouse pointer moves along the x axis and y axis. In this case, because no offset values are specified, the color value of each color channel displayed on screen will be a percentage of the original color value in the image—meaning that the most red or green displayed in any given pixel will be the original amount of red or green in that pixel. import flash.display.Loader; import flash.events.MouseEvent; import flash.geom.Transform; import flash.geom.ColorTransform; import flash.net.URLRequest; // Load an image onto the Stage. var loader:Loader = new Loader(); var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/ images/image1.jpg"); loader.load(url); this.addChild(loader); // This function is called when the mouse moves over the loaded image. function adjustColor(event:MouseEvent):void { // Access the ColorTransform object for the Loader (containing the image) var colorTransformer:ColorTransform = loader.transform.colorTransform; // Set the red and green multipliers according to the mouse position. // The red value ranges from 0% (no red) when the cursor is at the left // to 100% red (normal image appearance) when the cursor is at the right. // The same applies to the green channel, except it's controlled by the // position of the mouse in the y axis.
  • 413. Manipulating display objects 413 colorTransformer.redMultiplier = (loader.mouseX / loader.width) * 1; colorTransformer.greenMultiplier = (loader.mouseY / loader.height) * 1; // Apply the changes to the display object. loader.transform.colorTransform = colorTransformer; } loader.addEventListener(MouseEvent.MOUSE_MOVE, adjustColor); Rotating objects Display objects can be rotated using the rotation property. You can read this value to find out whether an object has been rotated, or to rotate the object you can set this property to a number (in degrees) representing the amount of rotation to be applied to the object. For instance, this line of code rotates the object named square 45 degrees (one eighth of one complete revolution): square.rotation = 45; Alternatively, you can rotate a display object using a transformation matrix, described in Chapter 13, “Working with geometry,” on page 431. Fading objects You can control the transparency of a display object to make it partially (or completely transparent), or change the transparency to make the object appear to fade in or out. The DisplayObject class’s alpha property defines the transparency (or more accurately, the opacity) of a display object. The alpha property can be set to any value between 0 and 1, where 0 is completely transparent, and 1 is completely opaque. For example, these lines of code make the object named myBall partially (50 percent) transparent when it is clicked with the mouse: function fadeBall(event:MouseEvent):void { myBall.alpha = .5; } myBall.addEventListener(MouseEvent.CLICK, fadeBall); You can also alter the transparency of a display object using the color adjustments available through the ColorTransform class. For more information, see “Adjusting DisplayObject colors” on page 410.
  • 414. 414 Display programming Masking display objects You can use a display object as a mask to create a hole through which the contents of another display object are visible. Defining a mask To indicate that a display object will be the mask for another display object, set the mask object as the mask property of the display object to be masked: // Make the object maskSprite be a mask for the object mySprite. mySprite.mask = maskSprite; The masked display object is revealed under all opaque (nontransparent) areas of the display object acting as the mask. For instance, the following code creates a Shape instance containing a red 100 by 100 pixel square and a Sprite instance containing a blue circle with a radius of 25 pixels. When the circle is clicked, it is set as the mask for the square, so that the only part of the square that shows is the part that is covered by the solid part of the circle. In other words, only a red circle will be visible. // This code assumes it’s being run within a display object container // such as a MovieClip or Sprite instance. import flash.display.Shape; // Draw a square and add it to the display list. var square:Shape = new Shape(); square.graphics.lineStyle(1, 0x000000); square.graphics.beginFill(0xff0000); square.graphics.drawRect(0, 0, 100, 100); square.graphics.endFill(); this.addChild(square); // Draw a circle and add it to the display list. var circle:Sprite = new Sprite(); circle.graphics.lineStyle(1, 0x000000); circle.graphics.beginFill(0x0000ff); circle.graphics.drawCircle(25, 25, 25); circle.graphics.endFill(); this.addChild(circle); function maskSquare(event:MouseEvent):void { square.mask = circle; circle.removeEventListener(MouseEvent.CLICK, maskSquare); }
  • 415. Manipulating display objects 415 circle.addEventListener(MouseEvent.CLICK, maskSquare); The display object that is acting as a mask can be draggable, animated, resized dynamically, and can use separate shapes within a single mask. The mask display object doesn’t necessarily need to be added to the display list. However, if you want the mask object to scale when the Stage is scaled or if you want to enable user interaction with the mask (such as user-controlled dragging and resizing), the mask object must be added to the display list. The actual z-index (front-to-back order) of the display objects doesn’t matter, as long as the mask object is added to the display list. (The mask object will not appear on the screen except as a mask.) If the mask object is a MovieClip instance with multiple frames, it plays all the frames in its timeline, the same as it would if it were not serving as a mask. You can remove a mask by setting the mask property to null: // remove the mask from mySprite mySprite.mask = null; You cannot use a mask to mask another mask. You cannot set the alpha property of a mask display object. Only fills are used in a display object that is used as a mask; strokes are ignored. About masking device fonts You can use a display object to mask text that is set in a device font. When you use a display object to mask text set in a device font, the rectangular bounding box of the mask is used as the masking shape. That is, if you create a non-rectangular display object mask for device font text, the mask that appears in the SWF file is the shape of the rectangular bounding box of the mask, not the shape of the mask itself. Alpha channel masking Alpha channel masking is supported if both the mask and the masked display objects use bitmap caching, as shown here: // maskShape is a Shape instance which includes a gradient fill. mySprite.cacheAsBitmap = true; maskShape.cacheAsBitmap = true; mySprite.mask = maskShape; For instance, one application of alpha channel masking is to use a filter on the mask object independently of a filter that is applied to the masked display object. In the following example, an external image file is loaded onto the Stage. That image (or more accurately, the Loader instance it is loaded into) will be the display object that is masked. A gradient oval (solid black center fading to transparent at the edges) is drawn over the image; this will be the alpha mask. Both display objects have bitmap caching turned on. The oval is set as a mask for the image, and it is then made draggable.
  • 416. 416 Display programming // This code assumes it’s being run within a display object container // such as a MovieClip or Sprite instance. import flash.display.GradientType; import flash.display.Loader; import flash.display.Sprite; import flash.geom.Matrix; import flash.net.URLRequest; // Load an image and add it to the display list. var loader:Loader = new Loader(); var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/ images/image1.jpg"); loader.load(url); this.addChild(loader); // Create a Sprite. var oval:Sprite = new Sprite(); // Draw a gradient oval. var colors:Array = [0x000000, 0x000000]; var alphas:Array = [1, 0]; var ratios:Array = [0, 255]; var matrix:Matrix = new Matrix(); matrix.createGradientBox(200, 100, 0, -100, -50); oval.graphics.beginGradientFill(GradientType.RADIAL, colors, alphas, ratios, matrix); oval.graphics.drawEllipse(-100, -50, 200, 100); oval.graphics.endFill(); // add the Sprite to the display list this.addChild(oval); // Set cacheAsBitmap = true for both display objects. loader.cacheAsBitmap = true; oval.cacheAsBitmap = true; // Set the oval as the mask for the loader (and its child, the loaded image) loader.mask = oval; // Make the oval draggable. oval.startDrag(true); Animating objects Animation is the process of making something move, or alternatively, of making something change over time. Scripted animation is a fundamental part of video games, and is often used to add polish and useful interaction clues to other applications.
  • 417. Animating objects 417 The fundamental idea behind scripted animation is that a change needs to take place, and that change needs to be divided into increments over time. It’s easy to make something repeat in ActionScript, using a common looping statement. However, a loop will run through all its iterations before updating the display. To create scripted animation, you need to write ActionScript that performs some action repeatedly over time and also updates the screen each time it runs. For example, imagine you want to create a simple animation, such as making a ball travel across the screen. ActionScript includes a simple mechanism that allows you to track the passage of time and update the screen accordingly—meaning you could write code that moves the ball a small amount each time, until it reaches its destination. After each move the screen would update, making the cross-Stage motion visible to the viewer. From a practical standpoint, it makes sense to synchronize scripted animation with the SWF file’s frame rate (in other words, make one animation change each time a new frame displays or would display), since that is how fast Flash Player updates the screen. Each display object has an enterFrame event that is dispatched according to the frame rate of the SWF file—one event per frame. Most developers who create scripted animation use the enterFrame event as a way to create actions that repeat over time. You could write code that listens to the enterFrame event, moving the animated ball a certain amount each frame, and as the screen is updated (each frame), the ball would be redrawn in its new location, creating motion. In the following example, a circle Sprite instance, named circle, is created on the Stage. When the user clicks the circle, a scripted animation sequence begins, causing circle to fade (its alpha property is decreased) until it is completely transparent: import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; // draw a circle and add it to the display list var circle:Sprite = new Sprite(); circle.graphics.beginFill(0x990000); circle.graphics.drawCircle(50, 50, 50); circle.graphics.endFill(); addChild(circle); // When this animation starts, this function is called every frame. // The change made by this function (updated to the screen every NOTE Another way to perform an action repeatedly over time is to use the Timer class. A Timer instance triggers an event notification each time a specified amount of time has past. You could write code that performs animation by handling the Timer class’s timer event, setting the time interval to a small one (some fraction of a second). For more information about using the Timer class, see “Controlling time intervals” on page 196.
  • 418. 418 Display programming // frame) is what causes the animation to occur. function fadeCircle(event:Event):void { circle.alpha -= .05; if (circle.alpha <= 0) { circle.removeEventListener(Event.ENTER_FRAME, fadeCircle); } } function startAnimation(event:MouseEvent):void { circle.addEventListener(Event.ENTER_FRAME, fadeCircle); } circle.addEventListener(MouseEvent.CLICK, startAnimation); When the user clicks the circle, the function fadeCircle() is subscribed as a listener of the enterFrame event, meaning it begins to be called once per frame. That function fades circle by changing its alpha property, so once per frame the circle’s alpha decreases by .05 (5 percent) and the screen is updated. Eventually, when the alpha value is 0 (circle is completely transparent), the fadeCircle() function is removed as an event listener, ending the animation. The same code could be used, for example, to create animated motion instead of fading. By substituting a different property for alpha in the function that is an enterFrame event listener, that property will be animated instead. For example, changing this line circle.alpha -= .05; to this code circle.x += 5; will animate the x property, causing the circle to move to the right across the Stage. The condition that ends the animation could be changed to end the animation (that is, unsubscribe the enterFrame listener) when the desired x coordinate is reached. Loading display content dynamically You can load any of the following external display assets into an ActionScript 3.0 application: ■ A SWF file authored in ActionScript 3.0—This file can be a Sprite, MovieClip, or any class that extends Sprite. ■ An image file—This includes JPG, PNG, and GIF files. ■ An AVM1 SWF file—This is a SWF file written in ActionScript 1.0 or 2.0.
  • 419. Loading display content dynamically 419 You load these assets by using the Loader class. Loading display objects Loader objects are used to load SWF files and graphics files into an application. The Loader class is a subclass of the DisplayObjectContainer class. A Loader object can contain only one child display object in its display list—the display object representing the SWF or graphic file that it loads. When you add a Loader object to the display list, as in the following code, you also add the loaded child display object to the display list once it loads: var pictLdr:Loader = new Loader(); var pictURL:String = "banana.jpg" var pictURLReq:URLRequest = new URLRequest(pictURL); pictLdr.load(pictURLReq); this.addChild(pictLdr); Once the SWF file or image is loaded, you can move the loaded display object to another display object container, such as the container DisplayObjectContainer object in this example: import flash.display.*; import flash.net.URLRequest; import flash.events.Event; var container:Sprite = new Sprite(); addChild(container); var pictLdr:Loader = new Loader(); var pictURL:String = "banana.jpg" var pictURLReq:URLRequest = new URLRequest(pictURL); pictLdr.load(pictURLReq); pictLdr.contentLoaderInfo.addEventListener(Event.COMPLETE, imgLoaded); function imgLoaded(event:Event):void { container.addChild(pictLdr.content); }
  • 420. 420 Display programming Monitoring loading progress Once the file has started loading, a LoaderInfo object is created. A LoaderInfo object provides information such as load progress, the URLs of the loader and loadee, the number of bytes total for the media, and the nominal height and width of the media. A LoaderInfo object also dispatches events for monitoring the progress of the load. The following diagram shows the different uses of the LoaderInfo object—for the instance of the main class of the SWF file, for a Loader object, and for an object loaded by the Loader object: The LoaderInfo object can be accessed as a property of both the Loader object and the loaded display object. As soon as loading begins, the LoaderInfo object can be accessed through the contentLoaderInfo property of the Loader object. Once the display object has finished loading, the LoaderInfo object can also be accessed as a property of the loaded display object through the display object’s loaderInfo property. The loaderInfo property of the loaded display object refers to the same LoaderInfo object as the contentLoaderInfo property of the Loader object. In other words, a LoaderInfo object is shared between a loaded object and the Loader object that loaded it (between loader and loadee). In order to access properties of loaded content, you will want to add an event listener to the LoaderInfo object, as in the following code: import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; Stage Instance of the main class of the SWF file Loader object LoaderInfo object content contentLoaderInfo property loaderInfo property LoaderInfo object loaderInfo property
  • 421. Loading display content dynamically 421 var ldr:Loader = new Loader(); var urlReq:URLRequest = new URLRequest("Circle.swf"); ldr.load(urlReq); ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded); addChild(ldr); function loaded(event:Event):void { var content:Sprite = event.target.content; content.scaleX = 2; } For more information, see Chapter 10, “Handling events,” on page 313. Specifying loading context When you load an external file into Flash Player through the load() or loadBytes() method of the Loader class, you can optionally specify a context parameter. This parameter is a LoaderContext object. The LoaderContext class includes three properties that let you define the context of how the loaded content can be used: ■ checkPolicyFile: Use this property only when loading an image file (not a SWF file). If you set this property to true, the Loader checks the origin server for a cross-domain policy file (see “Website controls (cross-domain policy files)” on page 765). This is necessary only for content originating from domains other than that of the SWF file containing the Loader object. If the server grants permission to the Loader domain, ActionScript from SWF files in the Loader domain can access data in the loaded image; in other words, you can use the BitmapData.draw() command to access data in the loaded image. Note that a SWF file from other domains than that of the Loader object can call Security.allowDomain() to permit a specific domain. ■ securityDomain: Use this property only when loading a SWF file (not an image). Specify this for a SWF file from a domain other than that of the file containing the Loader object. When you specify this option, Flash Player checks for the existence of a cross-domain policy file, and if one exists, SWF files from the domains permitted in the cross-policy file can cross-script the loaded SWF content. You can specify flash.system.SecurityDomain.currentDomain as this parameter.
  • 422. 422 Display programming ■ applicationDomain: Use this property only when loading a SWF file written in ActionScript 3.0 (not an image or a SWF file written in ActionScript 1.0 or 2.0). When loading the file, you can specify that the file be included in the same application domain as that of the Loader object, by setting the applicationDomain parameter to flash.system.ApplicationDomain.currentDomain. By putting the loaded SWF file in the same application domain, you can access its classes directly. This can be useful if you are loading a SWF file that contains embedded media, which you can access via their associated class names. For more information, see “Using the ApplicationDomain class” on page 700. Here’s an example of checking for a cross-domain policy file when loading a bitmap from another domain: var context:LoaderContext = new LoaderContext(); context.checkPolicyFile = true; var urlReq:URLRequest = new URLRequest("http://www.[your_domain_here].com/ photo11.jpg"); var ldr:Loader = new Loader(); ldr.load(urlReq, context); Here’s an example of checking for a cross-domain policy file when loading a SWF from another domain, in order to place the file in the same security sandbox as the Loader object. Additionally, the code adds the classes in the loaded SWF file to the same application domain as that of the Loader object: var context:LoaderContext = new LoaderContext(); context.securityDomain = SecurityDomain.currentDomain; context.applicationDomain = ApplicationDomain.currentDomain; var urlReq:URLRequest = new URLRequest("http://www.[your_domain_here].com/ library.swf"); var ldr:Loader = new Loader(); ldr.load(urlReq, context); For more information, see the LoaderContext class in the ActionScript 3.0 Language and Components Reference.
  • 423. Example: SpriteArranger 423 Example: SpriteArranger The SpriteArranger sample application builds upon the Geometric Shapes sample application described separately (see “Example: GeometricShapes” on page 180). The SpriteArranger sample application illustrates a number of concepts for dealing with display objects: ■ Extending display object classes ■ Adding objects to the display list ■ Layering display objects and working with display object containers ■ Responding to display object events ■ Using properties and methods of display objects To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The SpriteArranger application files can be found in the folder Examples/SpriteArranger. The application consists of the following files: File Description SpriteArranger.mxml or SpriteArranger.fla The main application file in Flash (FLA) or Flex (MXML). com/example/programmingas3/ SpriteArranger/CircleSprite.as A class defining a type of Sprite object that renders a circle on-screen. com/example/programmingas3/ SpriteArranger/DrawingCanvas.as A class defining the canvas, which is a display object container that contains GeometricSprite objects. com/example/programmingas3/ SpriteArranger/SquareSprite.as A class defining a type of Sprite object that renders a square on-screen. com/example/programmingas3/ SpriteArranger/TriangleSprite.as A class defining a type of Sprite object that renders a triangle on-screen. com/example/programmingas3/ SpriteArranger/GeometricSprite.as A class that extends the Sprite object, used to define an on-screen shape. The CircleSprite, SquareSprite, and TriangleSprite each extend this class. com/example/programmingas3/ geometricshapes/IGeometricShape.as The base interface defining methods to be implemented by all geometric shape classes. com/example/programmingas3/ geometricshapes/IPolygon.as An interface defining methods to be implemented by geometric shape classes that have multiple sides.
  • 424. 424 Display programming Defining the SpriteArranger classes The SpriteArranger application lets the user add a variety of display objects to the on-screen “canvas.” The DrawingCanvas class defines a drawing area, a type of display object container, to which the user can add on-screen shapes. These on-screen shapes are instances of one of the subclasses of the GeometricSprite class. The DrawingCanvas class The DrawingCanvas class extends the Sprite class, and this inheritance is defined in the DrawingCanvas class declaration, as follows: public class DrawingCanvas extends Sprite The Sprite class is a subclass of the DisplayObjectContainer and DisplayObject classes, and the DrawingCanvas class uses methods and properties of those classes. The DrawingCanvas() constructor method sets up a Rectangle object, bounds, which is property that is later used in drawing the outline of the canvas. It then calls the initCanvas() method, as follows: this.bounds = new Rectangle(0, 0, w, h); initCanvas(fillColor, lineColor); com/example/programmingas3/ geometricshapes/RegularPolygon.as A type of geometric shape that has sides of equal length positioned symmetrically around the shape’s center. com/example/programmingas3/ geometricshapes/Circle.as A type of geometric shape that defines a circle. com/example/programmingas3/ geometricshapes/EquilateralTriangle.as A subclass of RegularPolygon that defines a triangle with all sides the same length. com/example/programmingas3/ geometricshapes/Square.as A subclass of RegularPolygon defining a rectangle with all four sides the same length. com/example/programmingas3/ geometricshapes/ GeometricShapeFactory.as A class containing a “factory method” for creating shapes given a shape type and size. File Description
  • 425. Example: SpriteArranger 425 AS the following example shows, the initCanvas() method defines various properties of the DrawingCanvas object, which were passed as arguments to the constructor function: this.lineColor = lineColor; this.fillColor = fillColor; this.width = 500; this.height = 200; The initCanvas() method then calls the drawBounds() method, which draws the canvas using the DrawingCanvas class’s graphics property. The graphics property is inherited from the Shape class. this.graphics.clear(); this.graphics.lineStyle(1.0, this.lineColor, 1.0); this.graphics.beginFill(this.fillColor, 1.0); this.graphics.drawRect(bounds.left - 1, bounds.top - 1, bounds.width + 2, bounds.height + 2); this.graphics.endFill(); The following additional methods of the DrawingCanvas class are invoked based on user interactions with the application: ■ The addShape() and describeChildren() methods, which are described in “Adding display objects to the canvas” on page 426 ■ The moveToBack(), moveDown(), moveToFront(), and moveUp() methods, which are described in “Rearranging display object layering” on page 429 ■ The onMouseUp() method, which is described in “Clicking and dragging display objects” on page 428
  • 426. 426 Display programming The GeometricSprite class and its subclasses Each display object the user can add to the canvas is an instance of one of the following subclasses of the GeometricSprite class: ■ CircleSprite ■ SquareSprite ■ TriangleSprite The GeometricSprite class extends the flash.display.Sprite class: public class GeometricSprite extends Sprite The GeometricSprite class includes a number of properties common to all GeometricSprite objects. These are set in the constructor function based on parameters passed to the function. For example: this.size = size; this.lineColor = lColor; this.fillColor = fColor; The geometricShape property of the GeometricSprite class defines an IGeometricShape interface, which defines the mathematical properties, but not the visual properties, of the shape. The classes that implement the IGeometricShape interface are defined in the GeometricShapes sample application (see “Example: GeometricShapes” on page 180). The GeometricSprite class defines the drawShape() method, which is further refined in the override definitions in each subclass of GeometricSprite. For more information, see the “Adding display objects to the canvas” section, which follows. The GeometricSprite class also provides the following methods: ■ The onMouseDown() and onMouseUp() methods, which are described in “Clicking and dragging display objects” on page 428 ■ The showSelected() and hideSelected() methods, which are described in “Clicking and dragging display objects” on page 428 Adding display objects to the canvas When the user clicks the Add Shape button, the application calls the addShape() method of the DrawingCanvas class. It instantiates a new GeometricSprite by calling the appropriate constructor function of one of the GeometricSprite subclasses, as the following example shows:
  • 427. Example: SpriteArranger 427 public function addShape(shapeName:String, len:Number):void { var newShape:GeometricSprite; switch (shapeName) { case "Triangle": newShape = new TriangleSprite(len); break; case "Square": newShape = new SquareSprite(len); break; case "Circle": newShape = new CircleSprite(len); break; } newShape.alpha = 0.8; this.addChild(newShape); } Each constructor method calls the drawShape() method, which uses the graphics property of the class (inherited from the Sprite class) to draw the appropriate vector graphic. For example, the drawShape() method of the CircleSprite class includes the following code: this.graphics.clear(); this.graphics.lineStyle(1.0, this.lineColor, 1.0); this.graphics.beginFill(this.fillColor, 1.0); var radius:Number = this.size / 2; this.graphics.drawCircle(radius, radius, radius); The second to last line of the addShape() function sets the alpha property of the display object (inherited from the DisplayObject class), so that each display object added to the canvas is slightly transparent, letting the user see the objects behind it. The final line of the addChild() method adds the new display object to the child list of the instance of the DrawingCanvas class, which is already on the display list. This causes the new display object to appear on the Stage. The interface for the application includes two text fields, selectedSpriteTxt and outputTxt. The text properties of these text fields are updated with information about the GeometricSprite objects that have been added to the canvas or selected by the user. The GeometricSprite class handles this information-reporting task by overriding the toString() method, as follows: public override function toString():String { return this.shapeType + “ of size “ + this.size + " at " + this.x + ", " + this.y; }
  • 428. 428 Display programming The shapeType property is set to the appropriate value in the constructor method of each GeometricSprite subclass. For example, the toString() method might return the following value for a CircleSprite instance recently added to the DrawingCanvas instance: Circle of size 50 at 0, 0 The describeChildren() method of the DrawingCanvas class loops through the canvas’s child list, using the numChildren property (inherited from the DisplayObjectContainer class) to set the limit of the for loop. It generates a string listing each child, as follows: var desc:String = ""; var child:DisplayObject; for (var i:int=0; i < this.numChildren; i++) { child = this.getChildAt(i); desc += i + ": " + child + 'n'; } The resulting string is used to set the text property of the outputTxt text field. Clicking and dragging display objects When the user clicks on a GeometricSprite instance, the application calls the onMouseDown() event handler. As the following shows, this event handler is set to listen for mouse down events in the constructor function of the GeometricSprite class: this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); The onMouseDown() method then calls the showSelected() method of the GeometricSprite object. If it is the first time this method has been called for the object, the method creates a new Shape object named selectionIndicator and it uses the graphics property of the Shape object to draw a red highlight rectangle, as follows: this.selectionIndicator = new Shape(); this.selectionIndicator.graphics.lineStyle(1.0, 0xFF0000, 1.0); this.selectionIndicator.graphics.drawRect(-1, -1, this.size + 1, this.size + 1); this.addChild(this.selectionIndicator); If this is not the first time the onMouseDown() method is called, the method simply sets the selectionIndicator shape’s visible property (inherited from the DisplayObject class), as follows: this.selectionIndicator.visible = true; The hideSelected() method hides the selectionIndicator shape of the previously selected object by setting its visible property to false.
  • 429. Example: SpriteArranger 429 The onMouseDown() event handler method also calls the startDrag() method (inherited from the Sprite class), which includes the following code: var boundsRect:Rectangle = this.parent.getRect(this.parent); boundsRect.width -= this.size; boundsRect.height -= this.size; this.startDrag(false, boundsRect); This lets the user drag the selected object around the canvas, within the boundaries set by the boundsRect rectangle. When the user releases the mouse button, the mouseUp event is dispatched. The constructor method of the DrawingCanvas sets up the following event listener: this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); This event listener is set for the DrawingCanvas object, rather than for the individual GeometricSprite objects. This is because when the GeometricSprite object is dragged, it could end up behind another display object (another GeometricSprite object) when the mouse is released. The display object in the foreground would receive the mouse up event but the display object the user is dragging would not. Adding the listener to the DrawingCanvas object ensures that the event is always handled. The onMouseUp() method calls the onMouseUp() method of the GeometricSprite object, which in turn calls the stopDrag() method of the GeometricSprite object. Rearranging display object layering The user interface for the application includes buttons labeled Move Back, Move Down, Move Up, and Move to Front. When the user clicks one of these buttons, the application calls the corresponding method of the DrawingCanvas class: moveToBack(), moveDown(), moveUp(), or moveToFront(). For example, the moveToBack() method includes the following code: public function moveToBack(shape:GeometricSprite):void { var index:int = this.getChildIndex(shape); if (index > 0) { this.setChildIndex(shape, 0); } } The method uses the setChildIndex() method (inherited from the DisplayObjectContainer class) to position the display object in index position 0 in the child list of the DrawingCanvas instance (this).
  • 430. 430 Display programming The moveDown() method works similarly, except that it decrements the index position of the display object by 1 in the child list of the DrawingCanvas instance: public function moveDown(shape:GeometricSprite):void { var index:int = this.getChildIndex(shape); if (index > 0) { this.setChildIndex(shape, index - 1); } } The moveUp() and moveToFront() methods work similarly to the moveToBack() and moveDown() methods.
  • 431. 431 13 CHAPTER 13 Working with geometry The flash.geom package contains classes that define geometric objects such as points, rectangles, and transformation matrixes. You use these classes to define the properties of objects that are used in other classes. Contents Basics of geometry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 Using Point objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434 Using Rectangle objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .436 Using Matrix objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440 Example: Applying a matrix transformation to a display object . . . . . . . . . . . . . . . 442 Basics of geometry Introduction to working with geometry Geometry may be a subject many people try to get through in school while retaining as little as possible, but a little geometry knowledge can be a powerful tool in ActionScript. The flash.geom package contains classes that define geometric objects such as points, rectangles, and transformation matrixes. These classes don’t necessarily provide functionality by themselves; however, they are used to define the properties of objects that are used in other classes. All the geometry classes are based around the notion that locations on the screen are represented as a two-dimensional plane. The screen is treated like a flat graph with a horizontal (x) axis and a vertical (y) axis. Any location (or point) on the screen can be represented as a pair of x and y values—the coordinates of that location.
  • 432. 432 Working with geometry Every display object, include the Stage, has its own coordinate space—essentially its own graph for plotting the locations of child display objects, drawings, and so forth. Commonly, the origin (the place with the coordinate 0, 0 where the x and y axes meet) is placed at the top-left corner of the display object. While this is always true for the Stage, it is not necessarily true for any other display object. As in standard two-dimensional coordinate systems, values on the x axis get bigger going toward the right, and smaller going toward the left—for locations to the left of the origin, the x coordinate is negative. However, contrary to traditional coordinate systems, in ActionScript values on the y axis get bigger going down and smaller going up the screen (with values above the origin having a negative y coordinate). Since the top-left corner of the Stage is the origin of its coordinate space, any object on the Stage will have an x coordinate greater than 0 and smaller than the Stage width, and will have a y coordinate larger than 0 and smaller than the Stage height. You can use Point class instances to represent individual points in a coordinate space. You can create a Rectangle instance to represent a rectangular region in a coordinate space. For advanced users, you can use a Matrix instance to apply multiple or complex transformations to a display object. Many simple transformations, such as rotation, position, and scale changes, can be applied directly to a display object using that object’s properties. For more information on applying transformations using display object properties, see “Manipulating display objects” on page 395. Common geometry tasks The following tasks are things you’ll likely want to accomplish using the geometry classes in ActionScript: ■ Calculating distance between two points ■ Determining coordinates of a point in different coordinate spaces ■ Moving a display object using angle and distance ■ Working with Rectangle instances: ■ Repositioning a Rectangle instance ■ Resizing a Rectangle instance ■ Determining combined size or overlapping areas of Rectangle instances ■ Creating Matrix objects ■ Using a Matrix object to apply transformations to a display object
  • 433. Basics of geometry 433 Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Cartesian coordinates: Coordinates are commonly written as a pair of number (like 5, 12 or 17, -23). The two numbers are the x coordinate and the y coordinate, respectively. ■ Coordinate space: The graph of coordinates contained in a display object, on which its child elements are positioned. ■ Origin: The point in a coordinate space where the x axis meets the y axis. This point has the coordinate 0, 0. ■ Point: A single location in a coordinate space. In the 2-d coordinate system used in ActionScript, a point is defined by its location along the x axis and the y axis (the point’s coordinates). ■ Registration point: In a display object, the origin (0, 0 coordinate) of its coordinate space. ■ Scale: The size of an object relative to its original size. When used as a verb, to scale an object means to change its size by stretching or shrinking the object. ■ Translate: To change a point’s coordinates from one coordinate space to another. ■ Transformation: An adjustment to a visual characteristic of a graphic, such as rotating the object, altering its scale, skewing or distorting its shape, or altering its color. ■ X axis: The horizontal axis in the 2-d coordinate system used in ActionScript. ■ Y axis: The vertical axis in the 2-d coordinate system used in ActionScript. Working through in-chapter examples Many of the examples in this chapter demonstrate calculations or changing values; most of those examples include the appropriate trace() function calls to demonstrate the results of the code. To test these examples, do the following: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the code listing’s trace() functions in the Output panel. Some of the chapter’s examples demonstrate applying transformations to display objects. For those examples, the results of the example will be seen visually rather than through text output. To test the transformation examples, do the following: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline.
  • 434. 434 Working with geometry 3. Open the Actions panel and copy the code listing into the Script pane. 4. Create a movie clip symbol instance on the Stage. For example, draw a shape, select it, choose Modify > Convert to symbol, and give the symbol a name. 5. With the Stage movie clip selected, in the Property inspector, give the instance an instance name. The name should match the name used for the display object in the example code listing—for example, if the code listing applies a transformation to an object named myDisplayObject, you should name your movie clip instance myDisplayObject as well. 6. Run the program using Control > Test Movie. On the screen you will see the results of the transformations applied to the object as specified in the code listing. Techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63. Using Point objects A Point object defines a Cartesian pair of coordinates. It represents location in a two- dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. To define a Point object, you set its x and y properties, as follows: import flash.geom.*; var pt1:Point = new Point(10, 20); // x == 10; y == 20 var pt2:Point = new Point(); pt2.x = 10; pt2.y = 20; Finding the distance between two points You can use the distance() method of the Point class to find the distance between two points in a coordinate space. For example, the following code finds the distance between the registration points of two display objects, circle1 and circle2, in the same display object container: import flash.geom.*; var pt1:Point = new Point(circle1.x, circle1.y); var pt2:Point = new Point(circle2.x, circle2.y); var distance:Number = Point.distance(pt1, pt2);
  • 435. Using Point objects 435 Translating coordinate spaces If two display objects are in different display object containers, they may be in different coordinate spaces. You can use the localToGlobal() method of the DisplayObject class to translate the coordinates to the same (global) coordinate space, that of the Stage. For example, the following code finds the distance between the registration points of two display objects, circle1 and circle2, in the different display object containers: import flash.geom.*; var pt1:Point = new Point(circle1.x, circle1.y); pt1 = circle1.localToGlobal(pt1); var pt2:Point = new Point(circle1.x, circle1.y); pt2 = circle2.localToGlobal(pt2); var distance:Number = Point.distance(pt1, pt2); Similarly, to find the distance of the registration point of a display object named target from a specific point on the Stage, you can use the localToGlobal() method of the DisplayObject class: import flash.geom.*; var stageCenter:Point = new Point(); stageCenter.x = this.stage.stageWidth / 2; stageCenter.y = this.stage.stageHeight / 2; var targetCenter:Point = new Point(target.x, target.y); targetCenter = target.localToGlobal(targetCenter); var distance:Number = Point.distance(stageCenter, targetCenter); Moving a display object by a specified angle and distance You can use the polar() method of the Point class to move a display object a specific distance by a specific angle. For example, the following code moves the myDisplayObject object 100 pixels by 60 degrees: import flash.geom.*; var distance:Number = 100; var angle:Number = 2 * Math.PI * (90 / 360); var translatePoint:Point = Point.polar(distance, angle); myDisplayObject.x += translatePoint.x; myDisplayObject.y += translatePoint.y;
  • 436. 436 Working with geometry Other uses of the Point class You can use Point objects with the following methods and properties: Using Rectangle objects A Rectangle object defines a rectangular area. A Rectangle object has a position, defined by the x and y coordinates of its top-left corner, a width property, and a height property. You can define these properties for a new Rectangle object by invoking the Rectangle() constructor function, as follows: import flash.geom.Rectangle; var rx:Number = 0; var ry:Number = 0; var rwidth:Number = 100; var rheight:Number = 50; var rect1:Rectangle = new Rectangle(rx, ry, rwidth, rheight); Class Methods or properties Description DisplayObjectContainer areInaccessibleObjectsUnderPoint() getObjectsUnderPoint() Used to return a list of objects under a point in a display object container. BitmapData hitTest() Used to define the pixel in the BitmapData object as well as the point that you are checking for a hit. BitmapData applyFilter() copyChannel() merge() paletteMap() pixelDissolve() threshold() Used to define the positions of rectangles that define the operations. Matrix deltaTransformPoint() transformPoint() Used to define points for which you want to apply a transformation. Rectangle bottomRight size topLeft Used to define these properties.
  • 437. Using Rectangle objects 437 Resizing and repositioning Rectangle objects There are a number of ways to resize and reposition Rectangle objects. You can directly reposition the Rectangle object by changing its x and y properties. This has no effect on the width or height of the Rectangle object. import flash.geom.Rectangle; var x1:Number = 0; var y1:Number = 0; var width1:Number = 100; var height1:Number = 50; var rect1:Rectangle = new Rectangle(x1, y1, width1, height1); trace(rect1) // (x=0, y=0, w=100, h=50) rect1.x = 20; rect1.y = 30; trace(rect1); // (x=20, y=30, w=100, h=50) As the following code shows, if you change the left or top property of a Rectangle object, it is also repositioned, with its x and y properties matching the left and top properties, respectively. However, the position of the bottom-left corner of the Rectangle object does not change, so it is resized. import flash.geom.Rectangle; var x1:Number = 0; var y1:Number = 0; var width1:Number = 100; var height1:Number = 50; var rect1:Rectangle = new Rectangle(x1, y1, width1, height1); trace(rect1) // (x=0, y=0, w=100, h=50) rect1.left = 20; rect1.top = 30; trace(rect1); // (x=30, y=20, w=70, h=30) Similarly, as the following example shows, if you change the bottom or right property of a Rectangle object, the position of its top-left corner does not change, so it is resized accordingly: import flash.geom.Rectangle; var x1:Number = 0; var y1:Number = 0; var width1:Number = 100; var height1:Number = 50; var rect1:Rectangle = new Rectangle(x1, y1, width1, height1); trace(rect1) // (x=0, y=0, w=100, h=50) rect1.right = 60; trect1.bottom = 20; trace(rect1); // (x=0, y=0, w=60, h=20)
  • 438. 438 Working with geometry You can also reposition a Rectangle object by using the offset() method, as follows: import flash.geom.Rectangle; var x1:Number = 0; var y1:Number = 0; var width1:Number = 100; var height1:Number = 50; var rect1:Rectangle = new Rectangle(x1, y1, width1, height1); trace(rect1) // (x=0, y=0, w=100, h=50) rect1.offset(20, 30); trace(rect1); // (x=20, y=30, w=100, h=50) The offsetPt() method works similarly, except that it takes a Point object as its parameter, rather than x and y offset values. You can also resize a Rectangle object by using the inflate() method, which includes two parameters, dx and dy. The dx parameter represents the number of pixels that the left and right sides of the rectangle will move from the center, and the dy parameter represents the number of pixels that the top and bottom of the rectangle will move from the center: import flash.geom.Rectangle; var x1:Number = 0; var y1:Number = 0; var width1:Number = 100; var height1:Number = 50; var rect1:Rectangle = new Rectangle(x1, y1, width1, height1); trace(rect1) // (x=0, y=0, w=100, h=50) rect1.inflate(6,4); trace(rect1); // (x=-6, y=-4, w=112, h=58) The inflatePt() method works similarly, except that it takes a Point object as its parameter, rather than dx and dy values. Finding unions and intersections of Rectangle objects You use the union() method to find the rectangular region formed by the boundaries of two rectangles: import flash.display.*; import flash.geom.Rectangle; var rect1:Rectangle = new Rectangle(0, 0, 100, 100); trace(rect1); // (x=0, y=0, w=100, h=100) var rect2:Rectangle = new Rectangle(120, 60, 100, 100); trace(rect2); // (x=120, y=60, w=100, h=100) trace(rect1.union(rect2)); // (x=0, y=0, w=220, h=160)
  • 439. Using Rectangle objects 439 You use the intersection() method to find the rectangular region formed by the overlapping region of two rectangles: import flash.display.*; import flash.geom.Rectangle; var rect1:Rectangle = new Rectangle(0, 0, 100, 100); trace(rect1); // (x=0, y=0, w=100, h=100) var rect2:Rectangle = new Rectangle(80, 60, 100, 100); trace(rect2); // (x=120, y=60, w=100, h=100) trace(rect1.intersection(rect2)); // (x=80, y=60, w=20, h=40) You use the intersects() method to find out whether two rectangles intersect. You can also use the intersects() method to find out whether a display object is in a certain region of the Stage. For example, in the following code, assume that the coordinate space of the display object container that contains the circle object is the same as that of the Stage. The example shows how to use the intersects() method to determine if a display object, circle, intersects specified regions of the Stage, defined by the target1 and target2 Rectangle objects: import flash.display.*; import flash.geom.Rectangle; var circle:Shape = new Shape(); circle.graphics.lineStyle(2, 0xFF0000); circle.graphics.drawCircle(250, 250, 100); addChild(circle); var circleBounds:Rectangle = circle.getBounds(stage); var target1:Rectangle = new Rectangle(0, 0, 100, 100); trace(circleBounds.intersects(target1)); // false var target2:Rectangle = new Rectangle(0, 0, 300, 300); trace(circleBounds.intersects(target2)); // true Similarly, you can use the intersects() method to find out whether the bounding rectangles of two display objects overlap. You can use the getRect() method of the DisplayObject class to include any additional space that the strokes of a display object may add to a bounding region.
  • 440. 440 Working with geometry Other uses of Rectangle objects Rectangle objects are used in the following methods and properties: Using Matrix objects The Matrix class represents a transformation matrix that determines how to map points from one coordinate space to another. You can perform various graphical transformations on a display object by setting the properties of a Matrix object, applying that Matrix object to the matrix property of a Transform object, and then applying that Transform object as the transform property of the display object. These transformation functions include translation (x and y repositioning), rotation, scaling, and skewing. Class Methods or properties Description BitmapData applyFilter(), colorTransform(), copyChannel(), copyPixels(), draw(), fillRect(), generateFilterRect(), getColorBoundsRect(), getPixels(), merge(), paletteMap(), pixelDissolve(), setPixels(), and threshold() Used as the type for some parameters to define a region of the BitmapData object. DisplayObject getBounds(), getRect(), scrollRect, scale9Grid Used as the data type for the property or the data type returned. PrintJob addPage() Used to define the printArea parameter. Sprite startDrag() Used to define the bounds parameter. TextField getCharBoundaries() Used as the return value type. Transform pixelBounds Used as the data type.
  • 441. Using Matrix objects 441 Defining Matrix objects Although you could define a matrix by directly adjusting the properties (a, b, c, d, tx, ty) of a Matrix object, it is easier to use the createBox() method. This method includes parameters that let you directly define the scaling, rotation, and translation effects of the resulting matrix. For example, the following code creates a Matrix object that has the effect of scaling an object horizontally by 2.0, scaling it vertically by 3.0, rotating it by 45 degrees, moving (translating) it 10 pixels to the right, and moving it 20 pixels down: var matrix:Matrix = new Matrix(); var scaleX:Number = 2.0; var scaleY:Number = 3.0; var rotation:Number = 2 * Math.PI * (45 / 360); var tx:Number = 10; var ty:Number = 20; matrix.createBox(scaleX, scaleY, rotation, tx, ty); You can also adjust the scaling, rotation, and translation effects of a Matrix object by using the scale(), rotate(), and translate() methods. Note that these methods combine with the values of the existing Matrix object. For instance, the following code sets a Matrix object that scales an object by a factor of 4 and rotates it 60 degrees, since the scale() and rotate() methods are called twice: var matrix:Matrix = new Matrix(); var rotation:Number = 2 * Math.PI * (30 / 360); // 30° var scaleFactor:Number = 2; matrix.scale(scaleFactor, scaleFactor); matrix.rotate(rotation); matrix.scale(scaleX, scaleY); matrix.rotate(rotation); myDisplayObject.transform.matrix = matrix; To apply a skew transformation to a Matrix object, adjust its b or c property. Adjusting the b property skews the matrix vertically, and adjusting the c property skews the matrix horizontally. The following code skews the myMatrix Matrix object vertically by a factor of 2: var skewMatrix:Matrix = new Matrix(); skewMatrix.b = Math.tan(2); myMatrix.concat(skewMatrix);
  • 442. 442 Working with geometry You can apply a Matrix transformation to the transform property of a display object. For example, the following code applies a matrix transformation to a display object named myDisplayObject: var matrix:Matrix = myDisplayObject.transform.matrix; var scaleFactor:Number = 2; var rotation:Number = 2 * Math.PI * (60 / 360); // 60° matrix.scale(scaleFactor, scaleFactor); matrix.rotate(rotation); myDisplayObject.transform.matrix = matrix; The first line sets a Matrix object to the existing transformation matrix used by the myDisplayObject display object (the matrix property of the transformation property of the myDisplayObject display object). This way, the Matrix class methods that you call will have a cumulative effect on the display object’s existing position, scale, and rotation. Example: Applying a matrix transformation to a display object The DisplayObjectTransformer sample application shows a number of features of using the Matrix class to transform a display object, including the following: ■ Rotating the display object ■ Scaling the display object ■ Translating (repositioning) the display object ■ Skewing the display object NOTE The ColorTransform class is also included in the flash.geometry package. This class is used to set the colorTransform property of a Transform object. Since it does not apply any sort of geometrical transformation, it is not discussed in this chapter. For more information, see the ColorTransform class in the ActionScript 3.0 Language and Components Reference.
  • 443. Example: Applying a matrix transformation to a display object 443 The application provides an interface for adjusting the parameters of the matrix transformation, as follows: When the user clicks the Transform button, the application applies the appropriate transformation. The original display object, and the display object rotated by -45° and scaled by 50%
  • 444. 444 Working with geometry To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The DisplayObjectTransformer application files can be found in the folder Samples/DisplayObjectTransformer. The application consists of the following files: Defining the MatrixTransformer class The MatrixTransformer class includes static methods that apply geometric transformations of Matrix objects. The transform() method The transform() method includes parameters for each of the following: ■ sourceMatrix—The input matrix, which the method transforms ■ xScale and yScale—The x and y scale factor ■ dx and dy—The x and y translation amounts, in pixels ■ rotation—The rotation amount, in degrees ■ skew—The skew factor, as a percentage ■ skewType—The direction in which the skew, either "right" or "left" The return value is the resulting matrix. The transform() method calls the following static methods of the class: ■ skew() ■ scale() ■ translate() ■ rotate() Each returns the source matrix with the applied transformation. File Description DisplayObjectTransformer.mxml or DisplayObjectTransformer.fla The main application file in Flash (FLA) or Flex (MXML) com/example/programmingas3/geometry/ MatrixTransformer.as A class that contains methods for applying matrix transformations. img/ A directory containing sample image files used by the application.
  • 445. Example: Applying a matrix transformation to a display object 445 The skew() method The skew() method skews the matrix by adjusting the b and c properties of the matrix. An optional parameter, unit, determines the units used to define the skew angle, and if necessary, the method converts the angle value to radians: if (unit == "degrees") { angle = Math.PI * 2 * angle / 360; } if (unit == "gradients") { angle = Math.PI * 2 * angle / 100; } A skewMatrix Matrix object is created and adjusted to apply the skew transformation. Initially, it is the identity matrix, as follows: var skewMatrix:Matrix = new Matrix(); The skewSide parameter determines the side to which the skew is applied. If it is set to "right", the following code sets the b property of the matrix: skewMatrix.b = Math.tan(angle); Otherwise, the bottom side is skewed by adjusting the c property of the Matrix, as follows: skewMatrix.c = Math.tan(angle); The resulting skew is then applied to the existing matrix by concatenating the two matrixes, as the following example shows: sourceMatrix.concat(skewMatrix); return sourceMatrix; The scale() method As the following example shows, the scale() method first adjusts the scale factor if it is provided as a percentage, and then uses the scale() method of the matrix object: if (percent) { xScale = xScale / 100; yScale = yScale / 100; } sourceMatrix.scale(xScale, yScale); return sourceMatrix;
  • 446. 446 Working with geometry The translate() method The translate() method simply applies the dx and dy translation factors by calling the translate() method of the matrix object, as follows: sourceMatrix.translate(dx, dy); return sourceMatrix; The rotate() method The rotate() method converts the input rotation factor to radians (if it is provided in degrees or gradients), and then calls the rotate() method of the matrix object: if (unit == "degrees") { angle = Math.PI * 2 * angle / 360; } if (unit == "gradients") { angle = Math.PI * 2 * angle / 100; } sourceMatrix.rotate(angle); return sourceMatrix; Calling the MatrixTransformer.transform() method from the application The application contains a user interface for getting the transformation parameters from the user. It then passes these, along with the matrix property of the transform property of the display object, to the Matrix.transform() method, as follows: tempMatrix = MatrixTransformer.transform(tempMatrix, xScaleSlider.value, yScaleSlider.value, dxSlider.value, dySlider.value, rotationSlider.value, skewSlider.value, skewSide ); The application then applies the return value to the matrix property of the transform property of the display object, thereby triggering the transformation: img.content.transform.matrix = tempMatrix;
  • 447. 447 14 CHAPTER 14 Using the drawing API Although imported images and artwork are important, the functionality known as the drawing API, which allows you to draw lines and shapes in ActionScript, gives you the freedom to start an application with the computer equivalent of a blank canvas, on which you can create whatever images you wish. The ability to create your own graphics opens up broad possibilities for your applications. With the techniques covered in this chapter you can create a drawing program, make animated, interactive art, or programmatically create your own user interface elements, among many possibilities. Contents Basics of using the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .448 Understanding the Graphics class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Drawing lines and curves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 Drawing shapes using built-in methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .453 Creating gradient lines and fills . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454 Using the Math class with drawing methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460 Animating with the drawing API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 Example: Algorithmic Visual Generator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .462
  • 448. 448 Using the drawing API Basics of using the drawing API Introduction to using the drawing API The drawing API is the name for the functionality built into ActionScript that allows you to create vector graphics—lines, curves, shapes, fills, and gradients—and display them on the screen using ActionScript. The flash.display.Graphics class provides this functionality. You can draw with ActionScript on any Shape, Sprite, or MovieClip instance, using the graphics property defined in each of those classes. (Each of those classes’ graphics property is in fact an instance of the Graphics class.) If you’re just getting started with drawing with code, the Graphics class includes several methods that make it easy to draw common shapes like circles, ellipses, rectangles, and rectangles with rounded corners. You can draw them as empty lines or filled shapes. When you need more advanced functionality, the Graphics class also includes methods for drawing lines and quadratic Bézier curves, which you can use in conjunction with the trigonometry functions in the Math class to create any shape you need. Common drawing API tasks The following tasks are things you’ll likely want to accomplish using the drawing API in ActionScript, which are described in this chapter: ■ Defining line styles and fill styles for drawing shapes ■ Drawing straight lines and curves ■ Using methods for drawing shapes such as circles, ellipses, and rectangles ■ Drawing with gradient lines and fills ■ Defining a matrix for creating a gradient ■ Using trigonometry with the drawing API ■ Incorporating the drawing API into animation Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Anchor point: One of the two end points of a quadratic Bézier curve. ■ Control point: The point that defines the direction and amount of curve of a quadratic Bézier curve. The curved line never reaches the control point; however, the line curves as though being drawn toward the control point.
  • 449. Basics of using the drawing API 449 ■ Coordinate space: The graph of coordinates contained in a display object, on which its child elements are positioned. ■ Fill: The solid inner portion of a shape that has a line filled in with color, or all of a shape that has no outline. ■ Gradient: A color that consists of a gradual transition from one color to one or more other colors (as opposed to a solid color). ■ Point: A single location in a coordinate space. In the 2-d coordinate system used in ActionScript, a point is defined by its location along the x axis and the y axis (the point’s coordinates). ■ Quadratic Bézier curve: A type of curve defined by a particular mathematical formula. In this type of curve, the shape of the curve is calculated based on the positions of the anchor points (the end points of the curve) and a control point that defines the amount and direction of the curve. ■ Scale: The size of an object relative to its original size. When used as a verb, to scale an object means to change its size by stretching or shrinking the object. ■ Stroke: The outline portion of a shape that has a line filled in with color, or the lines of an un-filled shape. ■ Translate: To change a point’s coordinates from one coordinate space to another. ■ X axis: The horizontal axis in the 2-d coordinate system used in ActionScript. ■ Y axis: The vertical axis in the 2-d coordinate system used in ActionScript. Working through in-chapter examples While you’re working through the chapter you may want to test some of the example code listings. Because this chapter deals with drawing visual content, testing the code listings involves running the code and viewing the results in the SWF that’s created. To test the code listings: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Run the program using Control > Test Movie. You will see the results of the code listing in the SWF file that’s created.
  • 450. 450 Using the drawing API Understanding the Graphics class Each Shape, Sprite, and MovieClip object has a graphics property, which is an instance of the Graphics class. The Graphics class includes properties and methods for drawing lines, fills, and shapes. If you want a display object to use solely as a canvas for drawing content, you can use a Shape instance. A Shape instance will perform better than other display objects for drawing, because it doesn’t have the overhead of the additional functionality in the Sprite and MovieClip classes. If you want a display object on which you can draw graphical content and also want that object to contain other display objects, you can use a Sprite instance. For more information on determining which display object to use for various tasks, see “Choosing a DisplayObject subclass” on page 393. Drawing lines and curves All drawing that you do with a Graphics instance is based on basic drawing with lines and curves. Consequently, all ActionScript drawing must be performed using the same series of steps: ■ Define line and fill styles ■ Set the initial drawing position ■ Draw lines, curves, and shapes (optionally moving the drawing point) ■ If necessary, finish creating a fill Defining line and fill styles To draw with the graphics property of a Shape, Sprite, or MovieClip instance, you must first define the style (line size and color, fill color) to use when drawing. Just like when you use the drawing tools in Adobe Flash CS3 Professional or another drawing application, when you’re using ActionScript to draw you can draw with or without a stroke, and with or without a fill color. You specify the appearance of the stroke using the lineStyle() or lineGradientStyle() method. To create a solid line, use the lineStyle() method. When calling this method, the most common values you’ll specify are the first three parameters: line thickness, color, and alpha. For example, this line of code tells the Shape named myShape to draw lines that are 2 pixels thick, red (0x990000), and 75% opaque: myShape.graphics.lineStyle(2, 0x990000, .75);
  • 451. Drawing lines and curves 451 The default value for the alpha parameter is 1.0 (100%), so you can leave that parameter off if you want a completely opaque line. The lineStyle() method also accepts two additional parameters for pixel hinting and scale mode; for more information about using those parameters see the description of the Graphics.lineStyle() method in the ActionScript 3.0 Language and Components Reference. To create a gradient line, use the lineGradientStyle() method. This method is described in “Creating gradient lines and fills” on page 454. If you want to create a filled shape, you call the beginFill(), beginGradientFill(), or beginBitmapFill() methods before starting the drawing. The most basic of these, the beginFill() method, accepts two parameters: the fill color, and (optionally) an alpha value for the fill color. For example, if you want to draw a shape with a solid green fill, you would use the following code (assuming you’re drawing on an object named myShape): myShape.graphics.beginFill(0x00FF00); Calling any fill method implicitly ends any previous fill before starting a new one. Calling any method that specifies a stroke style replaces the previous stroke, but does not alter a previously specified fill, and vice versa. Once you have specified the line style and fill properties, the next step is to indicate the starting point for your drawing. The Graphics instance has a drawing point, like the tip of a pen on a piece of paper. Wherever the drawing point is located, that is where the next drawing action will begin. Initially a Graphics object begins with its drawing point at the point 0, 0 in the coordinate space of the object on which it’s drawing. To start the drawing at a different point, you can first call the moveTo() method before calling one of the drawing methods. This is analogous to lifting the pen tip off of the paper and moving it to a new position. With the drawing point in place you draw using a series of calls to the drawing methods lineTo() (for drawing straight lines) and curveTo() (for drawing curved lines). While drawing, if you have specified a fill color, you can tell Adobe Flash Player to close off the fill by calling the endFill() method. If you have not drawn a closed shape (in other words, if at the time you call endFill() the drawing point is not at the starting point of the shape), when you call the endFill() method Flash Player automatically closes the shape by drawing a straight line from the current drawing point to the location specified in the most recent moveTo() call. If you have started a fill and not called endFill(), calling beginFill() (or one of the other fill methods) closes the current fill and starts the new one. TIP While you are drawing, you can call the moveTo() method at any time to move the drawing point to a new position without drawing.
  • 452. 452 Using the drawing API Drawing straight lines When you call the lineTo() method, the Graphics object draws a straight line from the current drawing point to the coordinates you specify as the two parameters in the method call, drawing with the line style you have specified. For example, this line of code puts the drawing point at the point 100, 100 then draws a line to the point 200, 200: myShape.graphics.moveTo(100, 100); myShape.graphics.lineTo(200, 200); The following example draws red and green triangles with a height of 100 pixels: var triangleHeight:uint = 100; var triangle:Shape = new Shape(); // red triangle, starting at point 0, 0 triangle.graphics.beginFill(0xFF0000); triangle.graphics.moveTo(triangleHeight/2, 0); triangle.graphics.lineTo(triangleHeight, triangleHeight); triangle.graphics.lineTo(0, triangleHeight); triangle.graphics.lineTo(triangleHeight/2, 0); // green triangle, starting at point 200, 0 triangle.graphics.beginFill(0x00FF00); triangle.graphics.moveTo(200 + triangleHeight/2, 0); triangle.graphics.lineTo(200 + triangleHeight, triangleHeight); triangle.graphics.lineTo(200, triangleHeight); triangle.graphics.lineTo(200 + triangleHeight/2, 0); this.addChild(triangle); Drawing curves The curveTo() method draws a quadratic Bézier curve. This draws an arc that connects two points (called anchor points) while bending toward a third point (called the control point). The Graphics object uses the current drawing position as the first anchor point. When you call the curveTo() method, you pass four parameters: the x and y coordinates of the control point, followed by the x and y coordinates of the second anchor point. For example, the following code draws a curve starting at point 100, 100 and ending at point 200, 200. Because the control point is at point 175, 125, this creates a curve that moves to the right and then downward: myShape.graphics.moveTo(100, 100); myShape.graphics.curveTo(175, 125, 200, 200); The following example draws red and green circular objects with a width and height of 100 pixels. Note that due to the nature of the quadratic Bézier equation, these are not perfect circles:
  • 453. Drawing shapes using built-in methods 453 var size:uint = 100; var roundObject:Shape = new Shape(); // red circular shape roundObject.graphics.beginFill(0xFF0000); roundObject.graphics.moveTo(size / 2, 0); roundObject.graphics.curveTo(size, 0, size, size / 2); roundObject.graphics.curveTo(size, size, size / 2, size); roundObject.graphics.curveTo(0, size, 0, size / 2); roundObject.graphics.curveTo(0, 0, size / 2, 0); // green circular shape roundObject.graphics.beginFill(0x00FF00); roundObject.graphics.moveTo(200 + size / 2, 0); roundObject.graphics.curveTo(200 + size, 0, 200 + size, size / 2); roundObject.graphics.curveTo(200 + size, size, 200 + size / 2, size); roundObject.graphics.curveTo(200, size, 200, size / 2); roundObject.graphics.curveTo(200, 0, 200 + size / 2, 0); this.addChild(roundObject); Drawing shapes using built-in methods For convenience when drawing common shapes such as circles, ellipses, rectangles, and rectangles with rounded corners, ActionScript 3.0 has methods that draw these common shapes for you. These are the drawCircle(), drawEllipse(), drawRect(), drawRoundRect(), and drawRoundRectComplex() methods of the Graphics class. These methods may be used in place of the lineTo() and curveTo() methods. Note, however, that you must still specify line and fill styles before calling these methods. The following example recreates the example of drawing red, green, and blue squares with width and height of 100 pixels. This code uses the drawRect() method, and additionally specifies that the fill color has an alpha of 50% (0.5): var squareSize:uint = 100; var square:Shape = new Shape(); square.graphics.beginFill(0xFF0000, 0.5); square.graphics.drawRect(0, 0, squareSize, squareSize); square.graphics.beginFill(0x00FF00, 0.5); square.graphics.drawRect(200, 0, squareSize, squareSize); square.graphics.beginFill(0x0000FF, 0.5); square.graphics.drawRect(400, 0, squareSize, squareSize); square.graphics.endFill(); this.addChild(square);
  • 454. 454 Using the drawing API In a Sprite or MovieClip object, the drawing content created with the graphics property always appears behind all child display objects that are contained by the object. Also, the graphics property content is not a separate display object so it does not appear in the list of a Sprite or MovieClip object’s children. For example, the following Sprite object has a circle drawn with its graphics property, and it has a TextField object in its list of child display objects: var mySprite:Sprite = new Sprite(); mySprite.graphics.beginFill(0xFFCC00); mySprite.graphics.drawCircle(30, 30, 30); var label:TextField = new TextField(); label.width = 200; label.text = "They call me mellow yellow..."; label.x = 20; label.y = 20; mySprite.addChild(label); this.addChild(mySprite); Note that the TextField appears on top of the circle drawn with the graphics object. Creating gradient lines and fills The graphics object can also draw strokes and fills with gradients rather than solid colors. A gradient stroke is created with the lineGradientStyle() method and a gradient fill is created with the beginGradientFill() method. Both methods accept the same parameters. The first four are required: type, colors, alphas, and ratios. The remaining four are optional but are useful for advanced customization. ■ The first parameter specifies the type of gradient you are creating. Acceptable values are GradientFill.LINEAR or GradientFill.RADIAL. ■ The second parameter specifies the array of the color values to use. In a linear gradient, the colors will be arranged from left to right. In a radial gradient, they will be arranged from inside to outside. The order of the colors of the array represents the order that the colors will be drawn in the gradient. ■ The third parameter specifies the alpha transparency values of the corresponding colors in the previous parameter. ■ The fourth parameter specifies ratios, or the emphasis each color has within the gradient. Acceptable values range from 0-255. These values do not represent any width or height, but rather the position within the gradient; 0 represents the beginning of the gradient, 255 represents the end of the gradient. The array of ratios must increase sequentially and have the same number of entries as both the color and alpha arrays specified in the second and third parameters.
  • 455. Creating gradient lines and fills 455 Although the fifth parameter, the transformation matrix, is optional, it is commonly used because it provides an easy and powerful way to control the gradient’s appearance. This parameter accepts a Matrix instance. The easiest way to create a Matrix object for a gradient is to use the Matrix class’s createGradientBox() method. Defining a Matrix object for use with a gradient You use the beginGradientFill() and lineGradientStyle() methods of the flash.display.Graphics class to define gradients for use in shapes. When you define a gradient, you supply a matrix as one of the parameters of these methods. The easiest way to define the matrix is by using the Matrix class’s createGradientBox() method, which creates a matrix that is used to define the gradient. You define the scale, rotation, and position of the gradient using the parameters passed to the createGradientBox() method. The createGradientBox() method accepts the following parameters: ■ Gradient box width: the width (in pixels) to which the gradient will spread ■ Gradient box height: the height (in pixels) to which the gradient will spread ■ Gradient box rotation: the rotation (in radians) that will be applied to the gradient ■ Horizontal translation: how far (in pixels) the gradient is shifted horizontally ■ Vertical translation: how far (in pixels) the gradient is shifted vertically For example, consider a gradient with the following characteristics: ■ GradientType.LINEAR ■ Two colors, green and blue, with the ratios array set to [0, 255] ■ SpreadMethod.PAD ■ InterpolationMethod.LINEAR_RGB
  • 456. 456 Using the drawing API The following examples show gradients in which the rotation parameter of the createGradientBox() method differs as indicated, but all other settings stay the same: width = 100; height = 100; rotation = 0; tx = 0; ty = 0; width = 100; height = 100; rotation = Math.PI/4; // 45° tx = 0; ty = 0; width = 100; height = 100; rotation = Math.PI/2; // 90° tx = 0; ty = 0;
  • 457. Creating gradient lines and fills 457 The following examples show the effects on a green-to-blue linear gradient in which the rotation, tx, and ty parameters of the createGradientBox() method differ as indicated, but all other settings stay the same: The width, height, tx, and ty parameters of the createGradientBox() method affect the size and position of a radial gradient fill as well, as the following example shows: width = 50; height = 100; rotation = 0; tx = 0; ty = 0; width = 50; height = 100; rotation = 0 tx = 50; ty = 0; width = 100; height = 50; rotation = Math.PI/2; // 90° tx = 0; ty = 0; width = 100; height = 50; rotation = Math.PI/2; // 90° tx = 0; ty = 50; width = 50; height = 100; rotation = 0; tx = 25; ty = 0;
  • 458. 458 Using the drawing API The following code produces the last radial gradient illustrated: import flash.display.Shape; import flash.display.GradientType; import flash.geom.Matrix; var type:String = GradientType.RADIAL; var colors:Array = [0x00FF00, 0x000088]; var alphas:Array = [1, 1]; var ratios:Array = [0, 255]; var spreadMethod:String = SpreadMethod.PAD; var interp:String = InterpolationMethod.LINEAR_RGB; var focalPtRatio:Number = 0; var matrix:Matrix = new Matrix(); var boxWidth:Number = 50; var boxHeight:Number = 100; var boxRotation:Number = Math.PI/2; // 90° var tx:Number = 25; var ty:Number = 0; matrix.createGradientBox(boxWidth, boxHeight, boxRotation, tx, ty); var square:Shape = new Shape; square.graphics.beginGradientFill(type, colors, alphas, ratios, matrix, spreadMethod, interp, focalPtRatio); square.graphics.drawRect(0, 0, 100, 100); addChild(square); Note that the width and height of the gradient fill is determined by the width and height of the gradient matrix rather than the width or height that is drawn using the Graphics object. When drawing with the Graphics object, you draw what exists at those coordinates in the gradient matrix. Even if you use one of the shape methods of a Graphics object such as drawRect(), the gradient does not stretch itself to the size of the shape that is drawn—the gradient’s size must be specified in the gradient matrix itself.
  • 459. Creating gradient lines and fills 459 The following illustrates the visual difference between the dimensions of the gradient matrix and the dimensions of the draw itself: var myShape:Shape = new Shape(); var gradientBoxMatrix:Matrix = new Matrix(); gradientBoxMatrix.createGradientBox(100, 40, 0, 0, 0); myShape.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x00FF00, 0x0000FF], [1, 1, 1], [0, 128, 255], gradientBoxMatrix); myShape.graphics.drawRect(0, 0, 50, 40); myShape.graphics.drawRect(0, 50, 100, 40); myShape.graphics.drawRect(0, 100, 150, 40); myShape.graphics.endFill(); this.addChild(myShape); This code draws three gradients with the same fill style, specified with an equal distribution of red, green, and blue. The gradients are drawn using the drawRect() method with pixel widths of 50, 100, and 150 respectively. The gradient matrix which is specified in the beginGradientFill() method is created with a width of 100 pixels. This means that the first gradient will encompass only half of the gradient spectrum, the second will encompass all of it, and the third will encompass all of it and have an additional 50 pixels of blue extending to the right. The lineGradientStyle() method works similarly to beginGradientFill() except that in addition to defining the gradient, you must specify the thickness of the stroke using the lineStyle() method before drawing. The following code draws a box with a red, green, and blue gradient stroke: var myShape:Shape = new Shape(); var gradientBoxMatrix:Matrix = new Matrix(); gradientBoxMatrix.createGradientBox(200, 40, 0, 0, 0); myShape.graphics.lineStyle(5, 0); myShape.graphics.lineGradientStyle(GradientType.LINEAR, [0xFF0000, 0x00FF00, 0x0000FF], [1, 1, 1], [0, 128, 255], gradientBoxMatrix); myShape.graphics.drawRect(0, 0, 200, 40); this.addChild(myShape); For more information on the Matrix class, see “Using Matrix objects” on page 440.
  • 460. 460 Using the drawing API Using the Math class with drawing methods A Graphics object draws circles and squares, but can also draw more complex forms, particularly when the drawing methods are used in combination with the properties and methods of the Math class. The Math class contains constants of common mathematical interest, such as Math.PI (approximately 3.14159265...), a constant for the ratio of the circumference of a circle to its diameter. It also contains methods for trigonometry functions, including Math.sin(), Math.cos(), and Math.tan() among others. Drawing shapes using these methods and constants create more dynamic visual effects, particularly when used with repetition or recursion. Many methods of the Math class expect circular measurements in units of radians rather than degrees. Converting between these two types of units is a common use of the Math class: var degrees = 121; var radians = degrees * Math.PI / 180; trace(radians) // 2.111848394913139 The following example creates a sine wave and a cosine wave, to highlight the difference between the Math.sin() and Math.cos() methods for a given value. var sinWavePosition = 100; var cosWavePosition = 200; var sinWaveColor:uint = 0xFF0000; var cosWaveColor:uint = 0x00FF00; var waveMultiplier:Number = 10; var waveStretcher:Number = 5; var i:uint; for(i = 1; i < stage.stageWidth; i++) { var sinPosY:Number = Math.sin(i / waveStretcher) * waveMultiplier; var cosPosY:Number = Math.cos(i / waveStretcher) * waveMultiplier; graphics.beginFill(sinWaveColor); graphics.drawRect(i, sinWavePosition + sinPosY, 2, 2); graphics.beginFill(cosWaveColor); graphics.drawRect(i, cosWavePosition + cosPosY, 2, 2); }
  • 461. Animating with the drawing API 461 Animating with the drawing API One advantage of creating content with the drawing API is that you are not limited to positioning your content once. What you draw can be modified by maintaining and modifying the variables you use to draw. You can convey animation by changing variables and redrawing, either over a period of frames or with a timer. For example, the following code changes the display with each passing frame (by listening to the Event.ENTER_FRAME event), incrementing the current degree count, and directs the graphics object to clear and redraw with the updated position. stage.frameRate = 31; var currentDegrees:Number = 0; var radius:Number = 40; var satelliteRadius:Number = 6; var container:Sprite = new Sprite(); container.x = stage.stageWidth / 2; container.y = stage.stageHeight / 2; addChild(container); var satellite:Shape = new Shape(); container.addChild(satellite); addEventListener(Event.ENTER_FRAME, doEveryFrame); function doEveryFrame(event:Event):void { currentDegrees += 4; var radians:Number = getRadians(currentDegrees); var posX:Number = Math.sin(radians) * radius; var posY:Number = Math.cos(radians) * radius; satellite.graphics.clear(); satellite.graphics.beginFill(0); satellite.graphics.drawCircle(posX, posY, satelliteRadius); } function getRadians(degrees:Number):Number { return degrees * Math.PI / 180; } To produce a significantly different result, you can experiment by modifying the initial seed variables at the beginning of the code, currentDegrees, radius, and satelliteRadius. For example, try shrinking the radius variable and/or increasing the totalSatellites variable. This is only one example of how the drawing API can create a visual display whose complexity conceals the simplicity of its creation.
  • 462. 462 Using the drawing API Example: Algorithmic Visual Generator The Algorithmic Visual Generator example dynamically draws to the stage several “satellites”, or circles moving in a circular orbit. Among the features explored are: ■ Using the drawing API to draw a basic shape with dynamic appearances ■ Connecting user interaction with the properties that are used in a draw ■ Conveying animation by clearing the stage on each frame and redrawing The example in the previous subsection animated a solitary “satellite” using the Event.ENTER_FRAME event. This example expands upon this, building a control panel with series of sliders that immediately update the visual display of several satellites. This example formalizes the code into external classes and wraps the satellite creation code into a loop, storing a reference to each satellite in a satellites array. To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The application files can be found in the folder Samples/AlgorithmicVisualGenerator. This folder contains the following files: Setting the listeners The application first creates three listeners. The first listens for a dispatched event from the control panel that a rebuild of the satellites is necessary. The second listens to changes to the size of the SWF file’s stage. The third listens for each passing frame in the SWF file and to redraw using the doEveryFrame() function. File Description AlgorithmicVisualGenerator.fla The main application file in Flash (FLA). com/example/programmingas3/ algorithmic/AlgorithmicVisualGenerator.as The class that provides the main functionality of the application, including drawing satellites on the stage and responding to events from the control panel to update the variables that affect the drawing of satellites. com/example/programmingas3/ algorithmic/ControlPanel.as A class that manages user interaction with several sliders and dispatching events when this occurs. com/example/programmingas3/ algorithmic/Satellite.as A class which represents the display object that rotates in an orbit around a central point and contains properties related to its current draw state.
  • 463. Example: Algorithmic Visual Generator 463 Creating the satellites Once these listeners are set, the build() function is called. This function first calls the clear() function, which empties the satellites array and clears any previous draws to the stage. This is necessary since the build() function could be recalled whenever the control panel sends an event to do so, such as when the color settings have been changed. In such a case, the satellites must be removed and recreated. The function then creates the satellites, setting the initial properties needed for creation, such as a the position variable, which starts at a random position in the orbit, and the color variable, which in this example does not change once the satellite has been created. As each satellite is created, a reference to it is added to the satellites array. When the doEveryFrame() function is called, it will update to all satellites in this array. Updating the satellite position The doEveryFrame() function is the heart of the application’s animation process. It is called for every frame, at a rate equal the framerate of the SWF file. Because the variables of the draw change slightly, this conveys the appearance of animation. The function first clears all previous draws and redraws the background. Then, it loops through each satellite container and increments the position property of each satellite, and updates the radius and orbitRadius properties that may have changed from user interaction with the control panel. Finally, the satellite updates to its new position by calling the draw() method of the Satellite class. Note that the counter, i, only increments up to the visibleSatellites variable. This is because if the user has limited the amount of satellites that are displayed through the control panel, the remaining satellites in the loop should not be redrawn but should instead be hidden. This occurs in a loop which immediately follows the loop responsible for drawing. When the doEveryFrame() function completes, the number of visibleSatellites update in position across the screen.
  • 464. 464 Using the drawing API Responding to user interaction User interaction occurs via the control panel, which is managed by the ControlPanel class. This class sets a listener along with the individual minimum, maximum, and default values of each slider. As the user moves these sliders, the changeSetting() function is called. This function updates the properties of the control panel. If the change requires a rebuild of the display, an event is dispatched which is then handled in the main application file. As the control panel settings change, the doEveryFrame() function draws each satellite with the updated variables. Customizing further This example is only a basic schematic of how to generate visuals using the drawing API. It uses relatively few lines of code to create an interactive experience that appears quite complex. Even so, this example could be extended with minor changes. A few ideas: ■ The doEveryFrame() function could increment the color value of the satellite. ■ The doEveryFrame() function could shrink or expand the satellite radius over time. ■ The satellite radius does not have to be circular; it could use the Math class to move according to a sine wave, for example. ■ Satellites could use hit detection with other satellites. The drawing API can be used as an alternative to creating visual effects in the Flash authoring environment, drawing basic shapes at run time. But it can also be used to create visual effects of a variety and scope that are not possible to create by hand. Using the drawing API and a bit of mathematics, the ActionScript author can give life to many unexpected creations.
  • 465. 465 15 CHAPTER 15 Filtering display objects Historically, the application of filter effects to bitmap images has been the domain of specialized image editing software such as Adobe Photoshop® and Adobe Fireworks®. ActionScript 3.0 includes the flash.filters package, which contains a series of bitmap effect filter classes to allow developers to programmatically apply filters to bitmaps and display objects to achieve many of the same effects that are available in graphics manipulation applications. Contents Basics of filtering display objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .465 Creating and applying filters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .467 Available display filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .472 Example: Filter Workbench . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490 Basics of filtering display objects Introduction to filtering display objects One of the ways to add polish to an application is to add simple graphic effects, such as a drop shadow behind a photo to create the illusion of 3-d, or a glow around a button to show that it is active. ActionScript 3.0 includes nine filters that you can apply to any display object or to a BitmapData instance. These range from basic filters, such as the drop shadow and glow filters, to complex filters for creating various effects, such as the displacement map filter and the convolution filter.
  • 466. 466 Filtering display objects Common filtering tasks The following tasks are things you’ll likely want to accomplish using filters in ActionScript: ■ Creating a filter ■ Applying a filter to a display object ■ Applying a filter to the image data in a BitmapData instance ■ Removing filters from an object ■ Creating various filter effects, such as: ■ Glow ■ Blur ■ Drop shadow ■ Sharpness ■ Displacement ■ Edge detection ■ Embossing ■ and other effects Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Bevel: An edge created by lightening pixels on two sides and darkening pixels on the opposite two sides, creating a three-dimensional border effect commonly used for raised or indented buttons and similar graphics. ■ Convolution: Distorting pixels in an image by combining each pixel’s value with the values of some or all of its neighboring pixels, using various ratios. ■ Displacement: Shifting or moving pixels in an image to a new position. ■ Matrix: A grid of numbers used to perform certain mathematical calculations by applying the numbers in the grid to various values, then combining the results. Working through in-chapter examples While you’re working through the chapter, you may want to test the example code listings that are provided. Because this chapter deals with creating and manipulating visual content, testing the code involves running the code and viewing the results in the SWF that’s created. Nearly all the examples either create content using the drawing API or load images to which filters are applied.
  • 467. Creating and applying filters 467 To test the code in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code into the Script pane. 4. Run the program using Control > Test Movie. You see the results of the code in the SWF file that’s created. Nearly all the example code includes code that creates a bitmap image, so you can just test the code directly without needing to provide any bitmap content. Alternatively you can change the code listings to load your own images and use those in place of the ones in the examples. Creating and applying filters Filters allow you to apply a range of effects to bitmap and display objects, ranging from drop shadows to bevels and blurs. Each filter is defined as a class, so applying filters involves creating instances of filter objects, which is no different from constructing any other object. Once you’ve created an instance of a filter object, it can easily be applied to a display object by using the object’s filters property, or in the case of a BitmapData object, by using the applyFilter() method. Creating a new filter To create a new filter object, simply call the constructor method of your selected filter class. For example, to create a new DropShadowFilter object, use the following code: import flash.filters.DropShadowFilter; var myFilter:DropShadowFilter = new DropShadowFilter(); Although not shown here, the DropShadowFilter() constructor (like all the filter classes’ constructors) accepts several optional parameters that can be used to customize the appearance of the filter effect. Applying a filter Once you've constructed a filter object, you can apply it to a display object or a BitmapData object; how you apply the filter depends on the object to which you’re applying it.
  • 468. 468 Filtering display objects Applying a filter to a display object When you apply filter effects to a display object, you apply them through the filters property. The filters property of a display object is an Array instance, whose elements are the filter objects applied to the display object. To apply a single filter to a display object, create the filter instance, add it to an Array instance, and assign that Array object to the display object’s filters property: import flash.display.Bitmap; import flash.display.BitmapData; import flash.filters.DropShadowFilter; // Create a bitmapData object and render it to screen var myBitmapData:BitmapData = new BitmapData(100,100,false,0xFFFF3300); var myDisplayObject:Bitmap = new Bitmap(myBitmapData); addChild(myDisplayObject); // Create a DropShadowFilter instance. var dropShadow:DropShadowFilter = new DropShadowFilter(); // Create the filters array, adding the filter to the array by passing it as // a parameter to the Array() constructor. var filtersArray:Array = new Array(dropShadow); // Assign the filters array to the display object to apply the filter. myDisplayObject.filters = filtersArray; If you want to assign multiple filters to the object, simply add all the filters to the Array instance before assigning it to the filters property. You can add multiple objects to an Array by passing them as parameters to its constructor. For example, this code applies a bevel filter and a glow filter to the previously created display object: import flash.filters.BevelFilter; import flash.filters.GlowFilter; // Create the filters and add them to an array. var bevel:BevelFilter = new BevelFilter(); var glow:GlowFilter = new GlowFilter(); var filtersArray:Array = new Array(bevel, glow); // Assign the filters array to the display object to apply the filter. myDisplayObject.filters = filtersArray; NOTE When you’re creating the array containing the filters, you can create it using the new Array() constructor (as shown in the previous examples) or you can use Array literal syntax, wrapping the filters in square braces ( [] ). For instance, this line of code: var filters:Array = new Array(dropShadow, blur); does the same thing as this line of code: var filters:Array = [dropShadow, blur];
  • 469. Creating and applying filters 469 If you apply multiple filters to display objects, they are applied in a cumulative, sequential manner. For example, if a filters array has two elements, a bevel filter added first and a drop shadow filter added second, the drop shadow filter is applied to both the bevel filter and the display object. This is because of the drop shadow filter’s second position in the filters array. If you want to apply filters in a noncumulative manner, you must apply each filter to a new copy of the display object. If you’re only assigning one or a few filters to a display object, you can create the filter instance and assign it to the object in a single statement. For instance, the following line of code applies a blur filter to a display object called myDisplayObject: myDisplayObject.filters = [new BlurFilter()]; The previous code creates an Array instance using Array literal syntax (square braces), creates a new BlurFilter instance as an element in the Array, and assigns that Array to the filters property of the display object named myDisplayObject. Removing filters from a display object Removing all filters from a display object is as simple as assigning a null value to the filters property: myDisplayObject.filters = null; If you’ve applied multiple filters to an object and want to remove only one of the filters, you must go through several steps to change the filters property array. For more information, see “Changing filters at run time” on page 470. Applying a filter to a BitmapData object Applying a filter to a BitmapData object requires the use of the BitmapData object’s applyFilter() method: myBitmapData.applyFilter(sourceBitmapData); The applyFilter() method applies a filter to a source BitmapData object, producing a new, filtered image. This method does not modify the original source image; instead, the result of the filter being applied to the source image is stored in the BitmapData instance on which the applyFilter() method is called.
  • 470. 470 Filtering display objects How filters work Display object filtering works by caching a copy of the original object as a transparent bitmap. Once a filter has been applied to an display object, Adobe Flash Player caches the object as a bitmap for as long as the object has a valid filter list. This source bitmap is then used as the original image for all subsequently applied filter effects. Each display object usually contains two bitmaps: one with the original unfiltered source display object and another for the final image after filtering. The final image is used when rendering. As long as the display object does not change, the final image does not need updating. Potential issues for working with filters There are several potential sources of confusion or trouble to keep in mind when you’re working with filters. These are described in the following sections. Filters and bitmap caching To apply a filter to a display object, bitmap caching must be enabled for that object. When you apply a filter to a display object whose cacheAsBitmap property is set to false, Flash Player automatically sets the value of the object’s cacheAsBitmap property to true. If you later remove all the filters from the display object, Flash Player resets the cacheAsBitmap property to the last value it was set to. Changing filters at run time If a display object already has one or more filters applied to it, you can’t add additional filters to the filters property array. Instead, to add or change the set of filters being applied, you create a copy of the entire filters array and make your modifications to this (temporary) array. You then reassign this array back to the filters property of the display object in order for the filters to be applied to the object. The following code demonstrates this process. Initially, a glow filter is applied to the display object named myDisplayObject; later, when the display object is clicked, the function addFilters() is called. In this function, two additional filters are applied to myDisplayObject: import flash.events.MouseEvent; import flash.filters.*; myDisplayObject.filters = [new GlowFilter()]; function addFilters(event:MouseEvent):void
  • 471. Creating and applying filters 471 { // Make a copy of the filters array. var filtersCopy:Array = myDisplayObject.filters; // Make desired changes to the filters (in this case, adding filters). filtersCopy.push(new BlurFilter()); filtersCopy.push(new DropShadowFilter()); // Apply the changes by re-assigning the array to the filters property. myDisplayObject.filters = filtersCopy; } myDisplayObject.addEventListener(MouseEvent.CLICK, addFilters); Filters and object transformations No filtered region—a drop shadow, for example—outside of a display object’s bounding box rectangle is considered to be part of the surface for the purposes of hit detection (determining if an instance overlaps or intersects with another instance). Because the DisplayObject class’s hit detection methods are vector-based, you cannot perform a hit detection on the bitmap result. For example, if you apply a bevel filter to a button instance, hit detection is not available on the beveled portion of the instance. Scaling, rotating, and skewing are not supported by filters; if the filtered display object itself is scaled (if scaleX and scaleY are not 100%), the filter effect does not scale with the instance. This means that the original shape of the instance rotates, scales, or skews; however, the filter does not rotate, scale, or skew with the instance. You can animate an instance with a filter to create realistic effects, or nest instances and use the BitmapData class to animate filters to achieve this effect. Filters and Bitmap objects When you apply any filter to a BitmapData object, the cacheAsBitmap property is automatically set to true. In this way, the filter is actually applied to the copy of the object rather than the original. This copy is then placed on the main display (over the original object) as close as possible to the nearest pixel. If the bounds of the original bitmap change, the filtered copy bitmap is recreated from scratch, rather than being stretched or distorted. If you clear all filters for a display object, the cacheAsBitmap property is reset to what it was before the filter was applied.
  • 472. 472 Filtering display objects Available display filters ActionScript 3.0 includes nine filter classes that you can apply to display objects and BitmapData objects: ■ Bevel filter (BevelFilter class) ■ Blur filter (BlurFilter class) ■ Drop shadow filter (DropShadowFilter class) ■ Glow filter (GlowFilter class) ■ Gradient bevel filter (GradientBevelFilter class) ■ Gradient glow filter (GradientGlowFilter class) ■ Color matrix filter (ColorMatrixFilter class) ■ Convolution filter (ConvolutionFilter class) ■ Displacement map filter (DisplacementMapFilter class) The first six filters are simple filters that can be used to create one specific effect, with some customization of the effect available. Those six filters can be applied using ActionScript, and can also be applied to objects in Adobe Flash CS3 Professional using the Filters panel. Consequently, even if you’re applying filters using ActionScript, if you have the Flash authoring tool you can use the visual interface to quickly try out different filters and settings to figure out how to create a desired effect. The final three filters are available in ActionScript only. Those filters, the color matrix filter, convolution filter, and displacement map filter, are much more flexible in the types of effects that they can be used to create; rather than being optimized for a single effect, they provide power and flexibility. For example, by selecting different values for its matrix, the convolution filter can be used to create effects such as blurring, embossing, sharpening, finding color edges, transformations, and more. Each of the filters, whether simple or complex, can be customized using their properties. Generally, you have two choices for setting filter properties. All the filters let you set the properties by passing parameter values to the filter object’s constructor. Alternatively, whether or not you set the filter properties by passing parameters, you can adjust the filters later by setting values for the filter object’s properties. Most of the example code listings set the properties directly, in order to make the example easier to follow. Nevertheless, you could usually achieve the same result in fewer lines of code by passing the values as parameters in the filter object’s constructor. For more details on the specifics of each filter, its properties and its constructor parameters, see the listings for the flash.filters package in the ActionScript 3.0 Language and Components Reference.
  • 473. Available display filters 473 Bevel filter The BevelFilter class allows you to add a 3D beveled edge to the filtered object. This filter makes the hard corners or edges of your object look like they have been chiseled, or beveled, away. The BevelFilter class properties allow you to customize the appearance of the bevel. You can set highlight and shadow colors, bevel edge blurs, bevel angles, and bevel edge placement; you can even create a knockout effect. The following example loads an external image and applies a bevel filter to it. import flash.display.*; import flash.filters.BevelFilter; import flash.filters.BitmapFilterQuality; import flash.filters.BitmapFilterType; import flash.net.URLRequest; // Load an image onto the Stage. var imageLoader:Loader = new Loader(); var url:String = "http://www.helpexamples.com/flash/images/image3.jpg"; var urlReq:URLRequest = new URLRequest(url); imageLoader.load(urlReq); addChild(imageLoader); // Create the bevel filter and set filter properties. var bevel:BevelFilter = new BevelFilter(); bevel.distance = 5; bevel.angle = 45; bevel.highlightColor = 0xFFFF00; bevel.highlightAlpha = 0.8; bevel.shadowColor = 0x666666; bevel.shadowAlpha = 0.8; bevel.blurX = 5; bevel.blurY = 5; bevel.strength = 5; bevel.quality = BitmapFilterQuality.HIGH; bevel.type = BitmapFilterType.INNER; bevel.knockout = false; // Apply filter to the image. imageLoader.filters = [bevel];
  • 474. 474 Filtering display objects Blur filter The BlurFilter class smears, or blurs, a display object and its contents. Blur effects are useful for giving the impression that an object is out of focus or for simulating fast movement, as in a motion blur. By setting the quality property of the blur filter to low, you can simulate a softly out-of-focus lens effect. Setting the quality property to high results in a smooth blur effect similar to a Gaussian blur. The following example creates a circle object using the drawCircle() method of the Graphics class and applies a blur filter to it: import flash.display.Sprite; import flash.filters.BitmapFilterQuality; import flash.filters.BlurFilter; // Draw a circle. var redDotCutout:Sprite = new Sprite(); redDotCutout.graphics.lineStyle(); redDotCutout.graphics.beginFill(0xFF0000); redDotCutout.graphics.drawCircle(145, 90, 25); redDotCutout.graphics.endFill(); // Add the circle to the display list. addChild(redDotCutout); // Apply the blur filter to the rectangle. var blur:BlurFilter = new BlurFilter(); blur.blurX = 10; blur.blurY = 10; blur.quality = BitmapFilterQuality.MEDIUM; redDotCutout.filters = [blur]; Drop shadow filter Drop shadows give the impression that there is a separate light source situated above a target object. The position and intensity of this light source can be modified to produce a variety of different drop shadow effects. The drop shadow filter uses an algorithm that is similar to the blur filter’s algorithm. The main difference is that the drop shadow filter has a few more properties that you can modify to simulate different light-source attributes (such as alpha, color, offset and brightness). The drop shadow filter also allows you to apply custom transformation options on the style of the drop shadow, including inner or outer shadow and knockout (also known as cutout) mode.
  • 475. Available display filters 475 The following code creates a square box sprite and applies a drop shadow filter to it: import flash.display.Sprite; import flash.filters.DropShadowFilter; // Draw a box. var boxShadow:Sprite = new Sprite(); boxShadow.graphics.lineStyle(1); boxShadow.graphics.beginFill(0xFF3300); boxShadow.graphics.drawRect(0, 0, 100, 100); boxShadow.graphics.endFill(); addChild(boxShadow); // Apply the drop shadow filter to the box. var shadow:DropShadowFilter = new DropShadowFilter(); shadow.distance = 10; shadow.angle = 25; // You can also set other properties, such as the shadow color, // alpha, amount of blur, strength, quality, and options for // inner shadows and knockout effects. boxShadow.filters = [shadow]; Glow filter The GlowFilter class applies a lighting effect to display objects, making it appear that a light is being shined up from underneath the object to create a soft glow. Similar to the drop shadow filter, the glow filter includes properties to modify the distance, angle, and color of the light source to produce varying effects. The GlowFilter also has several options for modifying the style of the glow, including inner or outer glow and knockout mode. The following code creates a cross using the Sprite class and applies a glow filter to it: import flash.display.Sprite; import flash.filters.BitmapFilterQuality; import flash.filters.GlowFilter; // Create a cross graphic. var crossGraphic:Sprite = new Sprite(); crossGraphic.graphics.lineStyle(); crossGraphic.graphics.beginFill(0xCCCC00); crossGraphic.graphics.drawRect(60, 90, 100, 20); crossGraphic.graphics.drawRect(100, 50, 20, 100); crossGraphic.graphics.endFill(); addChild(crossGraphic);
  • 476. 476 Filtering display objects // Apply the glow filter to the cross shape. var glow:GlowFilter = new GlowFilter(); glow.color = 0x009922; glow.alpha = 1; glow.blurX = 25; glow.blurY = 25; glow.quality = BitmapFilterQuality.MEDIUM; crossGraphic.filters = [glow]; Gradient bevel filter The GradientBevelFilter class lets you apply an enhanced bevel effect to display objects or BitmapData objects. Using a gradient color on the bevel greatly improves the spatial depth of the bevel, giving edges a more realistic, 3D appearance. The following code creates a rectangle object using the drawRect() method of the Shape class and applies a gradient bevel filter to it. import flash.display.Shape; import flash.filters.BitmapFilterQuality; import flash.filters.GradientBevelFilter; // Draw a rectangle. var box:Shape = new Shape(); box.graphics.lineStyle(); box.graphics.beginFill(0xFEFE78); box.graphics.drawRect(100, 50, 90, 200); box.graphics.endFill(); // Apply a gradient bevel to the rectangle. var gradientBevel:GradientBevelFilter = new GradientBevelFilter(); gradientBevel.distance = 8; gradientBevel.angle = 225; // opposite of 45 degrees gradientBevel.colors = [0xFFFFCC, 0xFEFE78, 0x8F8E01]; gradientBevel.alphas = [1, 0, 1]; gradientBevel.ratios = [0, 128, 255]; gradientBevel.blurX = 8; gradientBevel.blurY = 8; gradientBevel.quality = BitmapFilterQuality.HIGH; // Other properties let you set the filter strength and set options // for inner bevel and knockout effects. box.filters = [gradientBevel]; // Add the graphic to the display list. addChild(box);
  • 477. Available display filters 477 Gradient glow filter The GradientGlowFilter class lets you apply an enhanced glow effect to display objects or BitmapData objects. The effect gives you greater color control of the glow, and in turn produces a more realistic glow effect. Additionally, the gradient glow filter allows you to apply a gradient glow to the inner, outer, or upper edges of an object. The following example draws a circle on the Stage, and applies a gradient glow filter to it. As you move the mouse further to the right and down, the amount of blur increases in the horizontal and vertical directions respectively. In addition, any time you click on the Stage, the strength of the blur increases. import flash.events.MouseEvent; import flash.filters.BitmapFilterQuality; import flash.filters.BitmapFilterType; import flash.filters.GradientGlowFilter; // Create a new Shape instance. var shape:Shape = new Shape(); // Draw the shape. shape.graphics.beginFill(0xFF0000, 100); shape.graphics.moveTo(0, 0); shape.graphics.lineTo(100, 0); shape.graphics.lineTo(100, 100); shape.graphics.lineTo(0, 100); shape.graphics.lineTo(0, 0); shape.graphics.endFill(); // Position the shape on the Stage. addChild(shape); shape.x = 100; shape.y = 100; // Define a gradient glow. var gradientGlow:GradientGlowFilter = new GradientGlowFilter(); gradientGlow.distance = 0; gradientGlow.angle = 45; gradientGlow.colors = [0x000000, 0xFF0000]; gradientGlow.alphas = [0, 1]; gradientGlow.ratios = [0, 255]; gradientGlow.blurX = 10; gradientGlow.blurY = 10; gradientGlow.strength = 2; gradientGlow.quality = BitmapFilterQuality.HIGH; gradientGlow.type = BitmapFilterType.OUTER;
  • 478. 478 Filtering display objects // Define functions to listen for two events. function onClick(event:MouseEvent):void { gradientGlow.strength++; shape.filters = [gradientGlow]; } function onMouseMove(event:MouseEvent):void { gradientGlow.blurX = (stage.mouseX / stage.stageWidth) * 255; gradientGlow.blurY = (stage.mouseY / stage.stageHeight) * 255; shape.filters = [gradientGlow]; } stage.addEventListener(MouseEvent.CLICK, onClick); stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); Example: Combining basic filters The following code example uses several basic filters, combined with a Timer for creating repeating actions, to create an animated traffic light simulation. import flash.display.Shape; import flash.events.TimerEvent; import flash.filters.BitmapFilterQuality; import flash.filters.BitmapFilterType; import flash.filters.DropShadowFilter; import flash.filters.GlowFilter; import flash.filters.GradientBevelFilter; import flash.utils.Timer; var count:Number = 1; var distance:Number = 8; var angleInDegrees:Number = 225; // opposite of 45 degrees var colors:Array = [0xFFFFCC, 0xFEFE78, 0x8F8E01]; var alphas:Array = [1, 0, 1]; var ratios:Array = [0, 128, 255]; var blurX:Number = 8; var blurY:Number = 8; var strength:Number = 1; var quality:Number = BitmapFilterQuality.HIGH; var type:String = BitmapFilterType.INNER; var knockout:Boolean = false; // Draw the rectangle background for the traffic light. var box:Shape = new Shape(); box.graphics.lineStyle(); box.graphics.beginFill(0xFEFE78); box.graphics.drawRect(100, 50, 90, 200); box.graphics.endFill();
  • 479. Available display filters 479 // Draw the 3 circles for the three lights. var stopLight:Shape = new Shape(); stopLight.graphics.lineStyle(); stopLight.graphics.beginFill(0xFF0000); stopLight.graphics.drawCircle(145,90,25); stopLight.graphics.endFill(); var cautionLight:Shape = new Shape(); cautionLight.graphics.lineStyle(); cautionLight.graphics.beginFill(0xFF9900); cautionLight.graphics.drawCircle(145,150,25); cautionLight.graphics.endFill(); var goLight:Shape = new Shape(); goLight.graphics.lineStyle(); goLight.graphics.beginFill(0x00CC00); goLight.graphics.drawCircle(145,210,25); goLight.graphics.endFill(); // Add the graphics to the display list. addChild(box); addChild(stopLight); addChild(cautionLight); addChild(goLight); // Apply a gradient bevel to the traffic light rectangle. var gradientBevel:GradientBevelFilter = new GradientBevelFilter(distance, angleInDegrees, colors, alphas, ratios, blurX, blurY, strength, quality, type, knockout); box.filters = [gradientBevel]; // Create the inner shadow (for lights when off) and glow // (for lights when on). var innerShadow:DropShadowFilter = new DropShadowFilter(5, 45, 0, 0.5, 3, 3, 1, 1, true, false); var redGlow:GlowFilter = new GlowFilter(0xFF0000, 1, 30, 30, 1, 1, false, false); var yellowGlow:GlowFilter = new GlowFilter(0xFF9900, 1, 30, 30, 1, 1, false, false); var greenGlow:GlowFilter = new GlowFilter(0x00CC00, 1, 30, 30, 1, 1, false, false); // Set the starting state of the lights (green on, red/yellow off). stopLight.filters = [innerShadow]; cautionLight.filters = [innerShadow]; goLight.filters = [greenGlow]; // Swap the filters based on the count value. function trafficControl(event:TimerEvent):void
  • 480. 480 Filtering display objects { if (count == 4) { count = 1; } switch (count) { case 1: stopLight.filters = [innerShadow]; cautionLight.filters = [yellowGlow]; goLight.filters = [innerShadow]; break; case 2: stopLight.filters = [redGlow]; cautionLight.filters = [innerShadow]; goLight.filters = [innerShadow]; break; case 3: stopLight.filters = [innerShadow]; cautionLight.filters = [innerShadow]; goLight.filters = [greenGlow]; break; } count++; } // Create a timer to swap the filters at a 3 second interval. var timer:Timer = new Timer(3000, 9); timer.addEventListener(TimerEvent.TIMER, trafficControl); timer.start(); Color matrix filter The ColorMatrixFilter class is used to manipulate the color and alpha values of the filtered object. This allows you to create saturation changes, hue rotation (shifting a palette from one range of colors to another), luminance-to-alpha changes, and other color manipulation effects using values from one color channel and potentially applying them to other channels.
  • 481. Available display filters 481 Conceptually, the filter goes through the pixels in the source image one by one and separates each pixel into its red, green, blue, and alpha components. It then multiplies values provided in the color matrix by each of these values, adding the results together to determine the resulting color value that will be displayed on the screen for that pixel. The matrix property of the filter is an array of 20 numbers that are used in calculating the final color. For details of the specific algorithm used to calculate the color values, see the entry describing the ColorMatrixFilter class’s matrix property in the ActionScript 3.0 Language and Components Reference. Further information and examples of the color matrix filter can be found in the article “Using Matrices for Transformations, Color Adjustments, and Convolution Effects in Flash” on the Adobe Developer Center web site. Convolution filter The ConvolutionFilter class can be used to apply a wide range of imaging transformations to BitmapData objects or display objects, such as blurring, edge detection, sharpening, embossing, and beveling. The convolution filter conceptually goes through each pixel in the source image one by one and determines the final color of that pixel using the value of the pixel and its surrounding pixels. A matrix, specified as an array of numeric values, indicates to what degree the value of each particular neighboring pixel affects the final resulting value. Consider the most commonly used type of matrix, which is a three by three matrix. The matrix includes nine values: N N N N P N N N N When Flash Player is applying the convolution filter to a certain pixel, it will look at the color value of the pixel itself (“P” in the example), as well as the values of the surrounding pixels (labelled “N” in the example). However, by setting values in the matrix, you specify how much priority certain pixels have in affecting the resulting image. For example, the following matrix, applied using a convolution filter, will leave an image exactly as it was: 0 0 0 0 1 0 0 0 0 The reason the image is unchanged is because the original pixel’s value has a relative strength of 1 in determining the final pixel color, while the surrounding pixels’ values have relative strength of 0—meaning their colors don’t affect the final image.
  • 482. 482 Filtering display objects Similarly, this matrix will cause the pixels of an image to shift one pixel to the left: 0 0 0 0 0 1 0 0 0 Notice that in this case, the pixel itself has no effect on the final value of the pixel displayed in that location on the final image—only the value of the pixel to the right is used to determine the pixel’s resulting value. In ActionScript, you create the matrix as a combination of an Array instance containing the values and two properties specifying the number of rows and columns in the matrix. The following example loads an image and, when the image finishes loading, applies a convolution filter to the image using the matrix in the previous listing: // Load an image onto the Stage. var loader:Loader = new Loader(); var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/ images/image1.jpg"); loader.load(url); this.addChild(loader); function applyFilter(event:MouseEvent):void { // Create the convolution matrix. var matrix:Array = [ 0, 0, 0, 0, 0, 1, 0, 0, 0 ]; var convolution:ConvolutionFilter = new ConvolutionFilter(); convolution.matrixX = 3; convolution.matrixY = 3; convolution.matrix = matrix; convolution.divisor = 1; loader.filters = [convolution]; } loader.addEventListener(MouseEvent.CLICK, applyFilter);
  • 483. Available display filters 483 Something that isn’t obvious in this code is the effect of using values other than 1 or 0 in the matrix. For example, the same matrix, with the number 8 instead of 1 in the right-hand position, performs the same action (shifting the pixels to the left). In addition, it affects the colors of the image, making them 8 times brighter. This is because the final pixel color values are calculated by multiplying the matrix values by the original pixel colors, adding the values together, and dividing by the value of the filter’s divisor property. Notice that in the example code, the divisor property is set to 1. As a general rule, if you want the brightness of the colors to stay about the same as in the original image, you should make the divisor equal to the sum of the matrix values. So with a matrix where the values add up to 8, and a divisor of 1, the resulting image is going to be roughly 8 times brighter than the original image. Although the effect of this matrix isn’t very noticeable, other matrix values can be used to create various effects. Here are several standard sets of matrix values for different effects using a three by three matrix: ■ Basic blur (divisor 5): 0 1 0 1 1 1 0 1 0 ■ Sharpening (divisor 1): 0, -1, 0 -1, 5, -1 0, -1, 0 ■ Edge detection (divisor 1): 0, -1, 0 -1, 4, -1 0, -1, 0 ■ Embossing effect (divisor 1): -2, -1, 0 -1, 1, 1 0, 1, 2 Notice that with most of these effects, the divisor is 1. This is because the negative matrix values added to the positive matrix values result in 1 (or 0 in the case of edge detection, but the divisor property’s value cannot be 0).
  • 484. 484 Filtering display objects Displacement map filter The DisplacementMapFilter class uses pixel values from a BitmapData object (known as the displacement map image) to perform a displacement effect on a new object. The displacement map image is typically different than the actual display object or BitmapData instance to which the filter is being applied. A displacement effect involves displacing pixels in the filtered image—in other words, shifting them away from their original location to some extent. This filter can be used to create a shifted, warped, or mottled effect. The location and amount of displacement applied to a given pixel is determined by the color value of the displacement map image. When working with the filter, in addition to specifying the map image, you specify the following values to control how the displacement is calculated from the map image: ■ Map point: The location on the filtered image at which the top-left corner of the displacement filter will be applied. You can use this if you only want to apply the filter to part of an image. ■ X component: Which color channel of the map image affects the x position of pixels. ■ Y component: Which color channel of the map image affects the y position of pixels. ■ X scale: A multiplier value that specifies how strong the x axis displacement is. ■ Y scale: A multiplier value that specifies how strong the y axis displacement is. ■ Filter mode: Determines what Flash Player should do in any empty spaces created by pixels being shifted away. The options, defined as constants in the DisplacementMapFilterMode class, are to display the original pixels (filter mode IGNORE), to wrap the pixels around from the other side of the image (filter mode WRAP, which is the default), to use the nearest shifted pixel (filter mode CLAMP), or to fill in the spaces with a color (filter mode COLOR). To get a basic understanding of how the displacement map filter works, consider a basic example. In the following code, an image is loaded, and when it finishes loading it is centered on the Stage and a displacement map filter is applied to it, causing the pixels in the entire image to shift horizontally to the left. import flash.display.BitmapData; import flash.display.Loader; import flash.events.MouseEvent; import flash.filters.DisplacementMapFilter; import flash.geom.Point; import flash.net.URLRequest;
  • 485. Available display filters 485 // Load an image onto the Stage. var loader:Loader = new Loader(); var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/ images/image3.jpg"); loader.load(url); this.addChild(loader); var mapImage:BitmapData; var displacementMap:DisplacementMapFilter; // This function is called when the image finishes loading. function setupStage(event:Event):void { // Center the loaded image on the Stage. loader.x = (stage.stageWidth - loader.width) / 2; loader.y = (stage.stageHeight - loader.height) / 2; // Create the displacement map image. mapImage = new BitmapData(loader.width, loader.height, false, 0xFF0000); // Create the displacement filter. displacementMap = new DisplacementMapFilter(); displacementMap.mapBitmap = mapImage; displacementMap.mapPoint = new Point(0, 0); displacementMap.componentX = BitmapDataChannel.RED; displacementMap.scaleX = 250; loader.filters = [displacementMap]; } loader.contentLoaderInfo.addEventListener(Event.COMPLETE, setupStage); The properties used to define the displacement are as follows: ■ Map bitmap: The displacement bitmap is a new BitmapData instance created by the code. Its dimensions match the dimensions of the loaded image (so the displacement is applied to the entire image). It is filled with solid red pixels. ■ Map point: This value is set to the point 0, 0—again, causing the displacement to be applied to the entire image. ■ X component: This value is set to the constant BitmapDataChannel.RED, meaning the red value of the map bitmap will determine how much the pixels are displaced (how much they move) along the x axis. ■ X scale: This value is set to 250. The full amount of displacement (from the map image being completely red) only displaces the image by a small amount (roughly one-half of a pixel), so if this value was set to 1 the image would only shift .5 pixels horizontally. By setting it to 250, the image shifts by approximately 125 pixels.
  • 486. 486 Filtering display objects These settings cause the filtered image’s pixels to shift 250 pixels to the left. The direction (left or right) and amount of shift is based on the color value of the pixels in the map image. Conceptually, Flash Player goes through the pixels of the filtered image one by one (at least, the pixels in the region where the filter is applied, which in this case means all the pixels), and does the following with each pixel: 1. It finds the corresponding pixel in the map image. For example, when Flash Player is calculating the displacement amount for the pixel in the top-left corner of the filtered image, it looks at the pixel in the top-left corner of the map image. 2. It determines the value of the specified color channel in the map pixel. In this case, the x component color channel is the red channel, so Flash Player looks to see what the value of the red channel of the map image is at the pixel in question. Since the map image is solid red, the pixel’s red channel is 0xFF, or 255. This is used as the displacement value. 3. It compares the displacement value to the “middle” value (127, which is halfway between 0 and 255). If the displacement value is lower than the middle value, the pixel shifts in a positive direction (to the right for x displacement; down for y displacement). On the other hand, if the displacement value is higher than the middle value (as in this example), the pixel shifts in a negative direction (to the left for x displacement; up for y displacement). To be more precise, Flash Player subtracts the displacement value from 127, and the result (positive or negative) is the relative amount of displacement that is applied. 4. Finally, it determines the actual amount of displacement by determining what percentage of full displacement the relative displacement value represents. In this case, full red means 100% displacement. That percentage is then multiplied by the x scale or y scale value to determine the number of pixels of displacement that will be applied. In this example, 100% times a multiplier of 250 determines the amount of displacement—roughly 125 pixels to the left. Because no values were specified for y component and y scale, the defaults (which cause no displacement) were used—that’s why the image doesn’t shift at all in the vertical direction. The default filter mode setting, WRAP, is used in the example, so as the pixels shift to the left the empty space on the right is filled in by the pixels that shifted off the left edge of the image. You can experiment with this value to see the different effects. For instance, if you add the following line to the portion of code where the displacement properties are being set (before the line loader.filters = [displacementMap]), it will make the image look as though it has been smeared across the Stage: displacementMap.mode = DisplacementMapFilterMode.CLAMP;
  • 487. Available display filters 487 For a more complex example, the following listing uses a displacement map filter to create a magnifying glass effect on an image: import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.GradientType; import flash.display.Loader; import flash.display.Shape; import flash.events.MouseEvent; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.Matrix; import flash.geom.Point; import flash.net.URLRequest; // Create the gradient circles that will together form the // displacement map image var radius:uint = 50; var type:String = GradientType.LINEAR; var redColors:Array = [ 0xFF0000, 0x000000 ]; var blueColors:Array = [ 0x0000FF, 0x000000 ]; var alphas:Array = [ 1, 1 ]; var ratios:Array = [ 0, 255 ]; var xMatrix:Matrix = new Matrix(); xMatrix.createGradientBox(radius * 2, radius * 2); var yMatrix:Matrix = new Matrix(); yMatrix.createGradientBox(radius * 2, radius * 2, Math.PI / 2); var xCircle:Shape = new Shape(); xCircle.graphics.lineStyle(0, 0, 0); xCircle.graphics.beginGradientFill(type, redColors, alphas, ratios, xMatrix); xCircle.graphics.drawCircle(radius, radius, radius); var yCircle:Shape = new Shape(); yCircle.graphics.lineStyle(0, 0, 0); yCircle.graphics.beginGradientFill(type, blueColors, alphas, ratios, yMatrix); yCircle.graphics.drawCircle(radius, radius, radius); // Position the circles at the bottom of the screen, for reference. this.addChild(xCircle); xCircle.y = stage.stageHeight - xCircle.height; this.addChild(yCircle); yCircle.y = stage.stageHeight - yCircle.height; yCircle.x = 200;
  • 488. 488 Filtering display objects // Load an image onto the Stage. var loader:Loader = new Loader(); var url:URLRequest = new URLRequest("http://www.helpexamples.com/flash/ images/image1.jpg"); loader.load(url); this.addChild(loader); // Create the map image by combining the two gradient circles. var map:BitmapData = new BitmapData(xCircle.width, xCircle.height, false, 0x7F7F7F); map.draw(xCircle); var yMap:BitmapData = new BitmapData(yCircle.width, yCircle.height, false, 0x7F7F7F); yMap.draw(yCircle); map.copyChannel(yMap, yMap.rect, new Point(0, 0), BitmapDataChannel.BLUE, BitmapDataChannel.BLUE); yMap.dispose(); // Display the map image on the Stage, for reference. var mapBitmap:Bitmap = new Bitmap(map); this.addChild(mapBitmap); mapBitmap.x = 400; mapBitmap.y = stage.stageHeight - mapBitmap.height; // This function creates the displacement map filter at the mouse location. function magnify():void { // Position the filter. var filterX:Number = (loader.mouseX) - (map.width / 2); var filterY:Number = (loader.mouseY) - (map.height / 2); var pt:Point = new Point(filterX, filterY); var xyFilter:DisplacementMapFilter = new DisplacementMapFilter(); xyFilter.mapBitmap = map; xyFilter.mapPoint = pt; // The red in the map image will control x displacement. xyFilter.componentX = BitmapDataChannel.RED; // The blue in the map image will control y displacement. xyFilter.componentY = BitmapDataChannel.BLUE; xyFilter.scaleX = 35; xyFilter.scaleY = 35; xyFilter.mode = DisplacementMapFilterMode.IGNORE; loader.filters = [xyFilter]; } // This function is called when the mouse moves. If the mouse is // over the loaded image, it applies the filter. function moveMagnifier(event:MouseEvent):void {
  • 489. Available display filters 489 if (loader.hitTestPoint(loader.mouseX, loader.mouseY)) { magnify(); } } loader.addEventListener(MouseEvent.MOUSE_MOVE, moveMagnifier); The code first generates two gradient circles, which are combined together to form the displacement map image. The red circle creates the x axis displacement (xyFilter.componentX = BitmapDataChannel.RED), and the blue circle creates the y axis displacement (xyFilter.componentY = BitmapDataChannel.BLUE). To help you understand what the displacement map image looks like, the code adds the original circles as well as the combined circle that serves as the map image to the bottom of the screen. The code then loads an image and, as the mouse moves, applies the displacement filter to the portion of the image that’s under the mouse. The gradient circles used as the displacement map image causes the displaced region to spread out away from the mouse pointer. Notice that the gray regions of the displacement map image don’t cause any displacement. The gray color is 0x7F7F7F. The blue and red channels of that shade of gray exactly match the middle shade of those color channels, so there is no displacement in a gray area of the map image. Likewise, in the center of the circle there is no displacement. Although the color there isn’t gray, that color’s blue channel and red channel are identical to the blue channel and red channel of medium gray, and since blue and red are the colors that cause displacement, no displacement happens there.
  • 490. 490 Filtering display objects Example: Filter Workbench The Filter Workbench provides a simple user interface to apply different filters to an image and see the resulting code that can be used to generate the same effect by using ActionScript. For a description of this example and to download the source code, see www.adobe.com/go/ learn_fl_filters.
  • 491. 491 16 CHAPTER 16 Working with movie clips The MovieClip class is the core class for animation and movie clip symbols created in Adobe Flash CS3 Professional. It has all the behaviors and functionality of display objects, but with additional properties and methods for controlling a movie clip’s timeline. This chapter explains how to use ActionScript to control movie clip playback and to create a movie clip dynamically. Contents Basics of movie clips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 Controlling movie clip playback. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 Creating MovieClip objects with ActionScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 Loading an external SWF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501 Example: RuntimeAssetsExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503 Basics of movie clips Introduction to working with movie clips Movie clips are a key element for people who create animated content with the Flash authoring tool and want to control that content with ActionScript. Whenever you create a movie clip symbol in Flash, Flash adds the symbol to the library of that Flash document. By default, this symbol becomes an instance of the MovieClip class, and as such has the properties and methods of the MovieClip class. When an instance of a movie clip symbol is placed on the Stage, the movie clip automatically progresses through its timeline (if it has more than one frame) unless its playback is altered using ActionScript. It is this timeline that distinguishes the MovieClip class, allowing you to create animation through motion or shape tweens through the Flash authoring tool. By contrast, with a display object that is an instance of the Sprite class, you can create animation only by programmatically changing the object’s values.
  • 492. 492 Working with movie clips In previous versions of ActionScript, the MovieClip class was the base class of all instances on the Stage. In ActionScript 3.0, a movie clip is only one of many display objects that can appear on the screen. If a timeline is not necessary for the function of a display object, using the Shape class or Sprite class in lieu of the MovieClip class may improve rendering performance. For more information on choosing the appropriate display object for a task, see “Choosing a DisplayObject subclass” on page 393. Common movie clip tasks The following common movie clips tasks are described in this chapter: ■ Making movie clips play and stop ■ Playing movie clips in reverse ■ Moving the playhead to specific points in a movie clip’s timeline ■ Working with frame labels in ActionScript ■ Accessing scene information in ActionScript ■ Creating instances of library movie clip symbols using ActionScript ■ Loading and controlling external SWF files, including files created for previous Flash Player versions ■ Building an ActionScript system for creating graphical assets to be loaded and used at run time Important concepts and terms The following reference list contains important terms used in this chapter: ■ AVM1 SWF: A SWF file created using ActionScript 1.0 or ActionScript 2.0, usually targeting Flash Player 8 or earlier. ■ AVM2 SWF: A SWF file created using ActionScript 3.0 for Adobe Flash Player 9. ■ External SWF: A SWF file that is created separately from the project SWF file and is intended to be loaded into the project SWF file and played back within that SWF file. ■ Frame: The smallest division of time on the timeline. As with a motion picture filmstrip, each frame is like a snapshot of the animation in time, and when frames are played quickly in sequence, the effect of animation is created.
  • 493. Basics of movie clips 493 ■ Timeline: The metaphorical representation of the series of frames that make up a movie clip’s animation sequence. The timeline of a MovieClip object is equivalent to the timeline in the Flash authoring tool. ■ Playhead: A marker identifying the location (frame) in the timeline that is being displayed at a given moment. Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because this chapter is about working with movie clips in ActionScript, essentially all the code listings in this chapter are written with the idea of manipulating a movie clip symbol that has been created and placed on the Stage. Testing the sample will involve viewing the result in Flash Player to see the effects of the code on the symbol. To test the code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Create a movie clip symbol instance on the Stage. For example, draw a shape, select it, choose Modify > Convert to symbol, and give the symbol a name. 5. With the movie clip selected, in the Property inspector, give it an instance name. The name should match the name used for the movie clip in the example code listing—for example, if the code listing manipulates a movie clip named myMovieClip, you should name your movie clip instance myMovieClip as well. 6. Run the program using Control > Test Movie. On the screen you will see the results of the code manipulating the movie clip as specified in the code listing. Other techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63.
  • 494. 494 Working with movie clips Working with MovieClip objects When you publish a SWF file, Flash converts all movie clip symbol instances on the Stage to MovieClip objects. You can make a movie clip symbol available to ActionScript by giving it an instance name in the Instance Name field of the Property inspector. When the SWF file is created, Flash generates the code that creates the MovieClip instance on the Stage and declares a variable using the instance name. If you have named movie clips that are nested inside other named movie clips, those child movie clips are treated like properties of the parent movie clip—you can access the child movie clip using dot syntax. For example, if a movie clip with the instance name childClip is nested within another clip with the instance name parentClip, you can make the child clip’s timeline animation play by calling this code: parentClip.childClip.play() While some legacy methods and properties of the ActionScript 2.0 MovieClip class remain the same, others have changed. All properties prefixed with an underscore have been renamed. For example, _width and _height properties are now accessed as width and height, while _xscale and _yscale are now accessed as scaleX and scaleY. For a complete list of the properties and methods of the MovieClip class, consult the ActionScript 3.0 Language and Components Reference. Controlling movie clip playback Flash uses the metaphor of a timeline to convey animation or a change in state. Any visual element that employs a timeline must be either a MovieClip object or extend from the MovieClip class. While ActionScript can direct any movie clip to stop, play, or go to another point on the timeline, it cannot be used to dynamically create a timeline or add content at specific frames; this is only possible using the Flash authoring tool. When a MovieClip is playing, it progresses along its timeline at a speed dictated by the frame rate of the SWF file. Alternatively, you can override this setting by setting the Stage.frameRate property in ActionScript.
  • 495. Controlling movie clip playback 495 Playing movie clips and stopping playback The play() and stop() methods allow basic control of a movie clip across its timeline. For example, suppose you have a movie clip symbol on the Stage which contains an animation of a bicycle moving across the screen, with its instance name set to bicycle. If the following code is attached to a keyframe on the main timeline, bicycle.stop(); the bicycle will not move (its animation will not play). The bicycle’s movement could start through some other user interaction. For example, if you had a button named startButton, the following code on a keyframe on the main timeline would make it so that clicking the button causes the animation to play: // This function will be called when the button is clicked. It causes the // bicycle animation to play. function playAnimation(event:MouseEvent):void { bicycle.play(); } // Register the function as a listener with the button. startButton.addEventListener(MouseEvent.CLICK, playAnimation); Fast-forwarding and rewinding The play() and stop() methods are not the only way of controlling playback in a movie clip. You can also move the playhead forward or backward along the timeline manually by using the nextFrame() and prevFrame() methods. Calling either of these methods stops playback and moves the playhead one frame forward or backward, respectively. Using the play() method is analogous to calling nextFrame() every time the movie clip object’s enterFrame event is triggered. Along these lines, you could make the bicycle movie clip play backwards by creating an event listener for the enterFrame event and telling bicycle to go to its previous frame in the listener function, as follows: // This function is called when the enterFrame event is triggered, meaning // it’s called once per frame. function everyFrame(event:Event):void { if (bicycle.currentFrame == 1) { bicycle.gotoAndStop(bicycle.totalFrames); } else
  • 496. 496 Working with movie clips { bicycle.prevFrame(); } } bicycle.addEventListener(Event.ENTER_FRAME, everyFrame); In normal playback, if a movie clip contains more than a single frame, it will loop indefinitely when playing; that is, it will return to Frame 1 if it progresses past its final frame. When you use prevFrame() or nextFrame(), this behavior does not happen automatically (calling prevFrame() when the playhead is on Frame 1 doesn’t move the playhead to the last frame). The if condition in the example above checks to see if the playhead has progressed backwards to the first frame, and sets the playhead ahead to its final frame, effectively creating a continuous loop of the movie clip playing backwards. Jumping to a different frame and using frame labels Sending a movie clip to a new frame is a simple affair. Calling either gotoAndPlay() or gotoAndStop() will jump the movie clip to the frame number specified as a parameter. Alternatively, you can pass a string that matches the name of a frame label. Any frame on the timeline can be assigned a label. To do this, select a frame on the timeline and then enter a name in the Frame Label field on the Property inspector. The advantages of using frame labels instead of numbers are particularly evident when creating a complex movie clip. When the number of frames, layers, and tweens in an animation becomes large, consider labeling important frames with explanatory descriptions that represent shifts in the behavior of the movie clip (for example, “off,” “walking,” or “running”). This improves code readability and also provides flexibility, since ActionScript calls that go to a labeled frame are pointers to a single reference—the label—rather than a specific frame number. If later on you decide to move a particular segment of the animation to a different frame, you will not need to change your ActionScript code as long as you keep the same label for the frames in the new location. To represent frame labels in code, ActionScript 3.0 includes the FrameLabel class. Each instance of this class represents a single frame label, and has a name property representing the name of the frame label as specified in the Property inspector, and a frame property representing the frame number of the frame where the label is placed on the timeline. In order to get access to the FrameLabel instances associated with a movie clip instance, the MovieClip class includes two properties that directly return FrameLabel objects. The currentLabels property returns an array that consists of all FrameLabel objects across the entire timeline of a movie clip. The currentLabel property returns a single FrameLabel object representing the frame label encountered most recently along the timeline.
  • 497. Controlling movie clip playback 497 Suppose you were creating a movie clip named robot and had labeled the various states of its animation. You could set up a condition that checks the currentLabel property to access the current state of robot, as in the following code: if (robot.currentLabel.name == "walking" { // do something } Working with scenes In the Flash authoring environment, you can use scenes to demarcate a series of timelines that a SWF file will progress through. Using the second parameter of the gotoAndPlay() or gotoAndStop() methods, you can specify a scene to send the playhead to. All FLA files start with only the initial scene, but you can create new scenes. Using scenes is not always the best approach because scenes have a number of drawbacks. A Flash document that contains multiple scenes can be difficult to maintain, particularly in multiauthor environments. Multiple scenes can also be inefficient in bandwidth, because the publishing process merges all scenes into a single timeline. This causes a progressive download of all scenes, even if they are never played. For these reasons, use of multiple scenes is often discouraged except for organizing lengthy multiple timeline-based animations. The scenes property of the MovieClip class returns an array of Scene objects representing all the scenes in the SWF file. The currentScene property returns a Scene object that represents the scene that is currently playing. The Scene class has several properties that give information about a scene. The labels property returns an array of FrameLabel objects representing the frame labels in that scene. The name property returns the scene’s name as a string. The numFrames property returns an int representing the total number of frames in the scene.
  • 498. 498 Working with movie clips Creating MovieClip objects with ActionScript One way of adding content to the screen in Flash is by dragging assets from the library onto the Stage, but that is not the only workflow. For complex projects, experienced developers commonly prefer to create movie clips programatically. This approach brings several advantages: easier re-use of code, faster compile-time speed, and more sophisticated modifications that are available only to ActionScript. The display list API of ActionScript 3.0 streamlines the process of dynamically creating MovieClip objects. The ability to instantiate a MovieClip instance directly, separate from the process of adding it to the display list, provides flexibility and simplicity without sacrificing control. In ActionScript 3.0, when you create a movie clip (or any other display object) instance programatically, it is not visible on the screen until it is added to the display list by calling the addChild() or the addChildAt() method on a display object container. This allows you to create a movie clip, set its properties, and even call methods before it is rendered to the screen. For more information on working with the display list, see “Working with display object containers” on page 383. Exporting library symbols for ActionScript By default, instances of movie clip symbols in a Flash document’s library cannot be dynamically created (that is, created using only ActionScript). This is because each symbol that is exported for use in ActionScript adds to the size of your SWF file, and it’s recognized that some symbols might not be intended for use on the stage. For this reason, in order to make a symbol available in ActionScript, you must specify that the symbol should be exported for ActionScript. To export a symbol for ActionScript: 1. Select the symbol in the Library panel and open its Symbol Properties dialog box. 2. If necessary, activate the Advanced settings. 3. In the Linkage section, activate the Export for ActionScript checkbox. This will activate the Class and Base Class fields.
  • 499. Creating MovieClip objects with ActionScript 499 By default, the Class field is populated with the symbol name, with spaces removed (for example, a symbol named “Tree House” would become “TreeHouse”). To specify that the symbol should use a custom class for its behavior, enter the full name of the class including its package in this field. If you want to be able to create instances of the symbol in ActionScript, but don’t need to add any additional behavior, you can leave the class name as-is. The Base Class field’s value defaults to flash.display.MovieClip. If you want your symbol to extend the functionality of another customer class, you can specify that class’s name instead, as long as that class extends the Sprite (or MovieClip) class. 4. Press the OK button to save the changes. At this point, if Flash can’t find an external ActionScript file with a definition for the specified class (for instance, if you didn’t need to add additional behavior for the symbol), a warning is displayed: A definition for this class could not be found in the classpath, so one will be automatically generated in the SWF file upon export. You can disregard this warning if your library symbol does not require unique functionality beyond the functionality of the MovieClip class. If you do not provide a class for your symbol, Flash will create a class for your symbol equivalent to this one: package { import flash.display.MovieClip; public class ExampleMovieClip extends MovieClip { public function ExampleMovieClip() { } } } If you do want to add extra ActionScript functionality to your symbol, add the appropriate properties and methods to this structure. For example, suppose you have a movie clip symbol containing a circle of 50 pixels width and 50 pixels height, and the symbol is specified to be exported for ActionScript with a class named Circle. The following code, when placed in a Circle.as file, extends the MovieClip class and provides the symbol with the additional methods getArea() and getCircumference(): package { import flash.display.MovieClip;
  • 500. 500 Working with movie clips public class Circle extends MovieClip { public function Circle() { } public function getArea():Number { // The formula is Pi times the radius squared. return Math.PI * Math.pow((width / 2), 2); } public function getCircumference():Number { // The formula is Pi times the diameter. return Math.PI * width; } } } The following code, placed on a keyframe on Frame 1 of the Flash document, will create an instance of the symbol and display it on the screen: var c:Circle = new Circle(); addChild(c); trace(c.width); trace(c.height); trace(c.getArea()); trace(c.getCircumference()); This code demonstrates ActionScript-based instantiation as an alternative to dragging individual assets onto the Stage. It creates a circle that has all of the properties of a movie clip and also has the custom methods defined in the Circle class. This is a very basic example— your library symbol can specify any number of properties and methods in its class. ActionScript-based instantiation is powerful, because it allows you to dynamically create large quantities of instances that would be tedious to arrange manually. It is also flexible, because you can customize each instance’s properties as it is created. You can get a sense of both of these benefits by using a loop to dynamically create several Circle instances. With the Circle symbol and class described previously in your Flash document’s library, place the following code on a keyframe on Frame 1: import flash.geom.ColorTransform; var totalCircles:uint = 10; var i:uint; for (i = 0; i < totalCircles; i++) {
  • 501. Loading an external SWF file 501 // Create a new Circle instance. var c:Circle = new Circle(); // Place the new Circle at an x coordinate that will space the circles // evenly across the Stage. c.x = (stage.stageWidth / totalCircles) * i; // Place the Circle instance at the vertical center of the Stage. c.y = stage.stageHeight / 2; // Change the Circle instance to a random color c.transform.colorTransform = getRandomColor(); // Add the Circle instance to the current timeline. addChild(c); } function getRandomColor():ColorTransform { // Generate random values for the red, green, and blue color channels. var red:Number = (Math.random() * 512) - 255; var green:Number = (Math.random() * 512) - 255; var blue:Number = (Math.random() * 512) - 255; // Create and return a ColorTransform object with the random colors. return new ColorTransform(1, 1, 1, 1, red, green, blue, 0); } This demonstrates how you can create and customize multiple instances of a symbol quickly using code. Each instance is positioned based on the current count within the loop, and each instance is given a random color by setting its transform property (which Circle inherits by extending the MovieClip class). Loading an external SWF file In ActionScript 3.0, SWF files are loaded using the Loader class. To load an external SWF file, your ActionScript needs to do four things: 1. Create a new URLRequest object with the url of the file. 2. Create a new Loader object. 3. Call the Loader object’s load() method, passing the URLRequest instance as a parameter. 4. Call the addChild() method on a display object container (such as the main timeline of a Flash document) to add the Loader instance to the display list. Ultimately, the code looks like this: var request:URLRequest = new URLRequest(“http://www.[yourdomain].com/ externalSwf.swf”); var loader:Loader = new Loader() loader.load(request); addChild(loader);
  • 502. 502 Working with movie clips This same code can be used to load an external image file such as a JPEG, GIF, or PNG image, by specifying the image file’s url rather than a SWF file’s url. A SWF file, unlike an image file, may contain ActionScript. Thus, although the process of loading a SWF file may be identical to loading an image, when loading an external SWF file both the SWF file doing the loading and the SWF file being loaded must reside in the same security sandbox if you plan to use ActionScript to communicate in any way to the external SWF file. Additionally, if the external SWF file contains classes that share the same namespace as classes in the loading SWF file, you may need to create a new application domain for the loaded SWF file in order to avoid namespace conflicts. For more information on security and application domain considerations, see “Using the ApplicationDomain class” on page 700 and “Loading SWF files and images” on page 776. When the external SWF file is successfully loaded, it can be accessed through the Loader.content property. If the external SWF file is published for ActionScript 3.0, this will be either a movie clip or a sprite, depending on which class it extends. Considerations for loading an older SWF file If the external SWF file has been published with an older version of ActionScript, there are important limitations to consider. Unlike an ActionScript 3.0 SWF file that runs in AVM2 (ActionScript Virtual Machine 2), a SWF file published for ActionScript 1.0 or 2.0 runs in AVM1 (ActionScript Virtual Machine 1). When an AVM1 SWF file is successfully loaded, the loaded object (the Loader.content property) will be an AVM1Movie object. An AVM1Movie instance is not the same as a MovieClip instance. It is a display object, but unlike a movie clip, it does not include timeline-related methods or properties. The parent AVM2 SWF file will not have access to the properties, methods, or objects of the loaded AVM1Movie object. There are additional restrictions on an AVM1 SWF file loaded by an AVM2 SWF file. For details, see the AVM1Movie class listing in the ActionScript 3.0 Language and Components Reference.
  • 503. Example: RuntimeAssetsExplorer 503 Example: RuntimeAssetsExplorer The Export for ActionScript functionality can be especially advantageous for libraries that may be useful across more than one project. Symbols that have been exported to ActionScript are available not only to that SWF file alone but to any SWF file within the same security sandbox that loads it. In this way, a single Flash document can generate a SWF file that is designated for the sole purpose of holding graphical assets. This technique is particularly useful for larger projects where designers working on visual assets can work in parallel with developers who create a “wrapper” SWF file that then loads the graphical assets SWF file at run time. You can use this method to maintain a series of versioned files where graphical assets are not dependent upon the progress of programming development. The RuntimeAssetsExplorer application loads any SWF file that is a subclass of RuntimeAsset and allows you to browse the available assets of that SWF file. The example illustrates the following: ■ Loading an external SWF file using Loader.load() ■ Dynamic creation of a library symbol exported for ActionScript ■ ActionScript control of MovieClip playback Before beginning, note that each of the SWF files must be located in the same security sandbox. For more information, see “Security sandboxes” on page 770. To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The RuntimeAssetsExplorer application files can be found in the folder Samples/RuntimeAssetsExplorer. The application consists of the following files: File Description RuntimeAssetsExample.mxml or RuntimeAssetsExample.fla The user interface for the application for Flex (MXML) or Flash (FLA). GeometricAssets.as An example class that implements the RuntimeAsset interface. GeometricAssets.fla A FLA file linked to the GeometricAssets class (the document class of the FLA) containing symbols that are exported for ActionScript. com/example/programmingas3/ runtimeassetsexplorer/RuntimeLibrary.as An interface that defines the required methods expected of all run-time asset SWF files that will be loaded into the explorer container.
  • 504. 504 Working with movie clips Establishing a run-time library interface In order for the explorer to properly interact with a SWF library, the structure of the run-time asset libraries must be formalized. We will accomplish this by creating an interface, which is similar to a class in that it’s a blueprint of methods that demarcate an expected structure, but unlike a class it includes no method bodies. The interface provides a way for both the run- time library and the explorer to communicate to one another. Each SWF of run-time assets that is loaded in our browser will implement this interface. For more information about interfaces and how they can be useful, see “Interfaces” on page 158. The RuntimeLibrary interface will be very simple—we merely require a function that can provide the explorer with an array of classpaths for the symbols to be exported and available in the run-time library. To this end, the interface has a single method: getAssets(). package com.example.programmingas3.runtimeassetexplorer { public interface RuntimeLibrary { function getAssets():Array; } } Creating the asset library SWF file By defining the RuntimeLibrary interface, it’s possible to create multiple asset library SWF files that can be loaded into another SWF file. Making an individual SWF library of assets involves four tasks: ■ Creating a class for the asset library SWF file ■ Creating classes for individual assets contained in the library ■ Creating the actual graphic assets ■ Associating graphic elements with classes and publishing the library SWF com/example/programmingas3/ runtimeassetsexplorer/AnimatingBox.as The class of the library symbol in the shape of a rotating box. com/example/programmingas3/ runtimeassetsexplorer/AnimatingStar.as The class of the library symbol in the shape of a rotating star. File Description
  • 505. Example: RuntimeAssetsExplorer 505 Creating a class to implement the RuntimeLibrary interface Next, we’ll create the GeometricAssets class that will implement the RuntimeLibrary interface. This will be the document class of the FLA. The code for this class is very similar to the RuntimeLibrary interface—the difference between them is that in the class definition the getAssets() method has a method body. package { import flash.display.Sprite; import com.example.programmingas3.runtimeassetexplorer.RuntimeLibrary; public class GeometricAssets extends Sprite implements RuntimeLibrary { public function GeometricAssets() { } public function getAssets():Array { return [ "com.example.programmingas3.runtimeassetexplorer.AnimatingBox", "com.example.programmingas3.runtimeassetexplorer.AnimatingStar" ]; } } } If we were to create a second run-time library, we could create another FLA based upon another class (for example, AnimationAssets) that provides its own getAssets() implementation. Creating classes for each MovieClip asset For this example, we’ll merely extend the MovieClip class without adding any functionality to the custom assets. The following code for AnimatingStar is analogous to that of AnimatingBox: package com.example.programmingas3.runtimeassetexplorer { import flash.display.MovieClip; public class AnimatingStar extends MovieClip { public function AnimatingStar() { } } }
  • 506. 506 Working with movie clips Publishing the library We’ll now connect the MovieClip-based assets to the new class by creating a new FLA and entering GeometricAssets into the Document Class field of the Property inspector. For the purposes of this example, we’ll create two very basic shapes that use a timeline tween to make one clockwise rotation over 360 frames. Both the animatingBox and animatingStar symbols are set to Export for ActionScript and have the Class field set to the respective classpaths specified in the getAssets() implementation. The default base class of flash.display.MovieClip remains, as we want to subclass the standard MovieClip methods. After setting up your symbol’s export settings, publish the FLA. You now have your first run- time library. This SWF file could be loaded into another AVM2 SWF file and the AnimatingBox and AnimatingStar symbols would be available to the new SWF file. Loading the library into another SWF file The last functional piece to deal with is the user interface for the asset explorer. In this example, the path to the run-time library is hard-coded as a variable named ASSETS_PATH. Alternatively, you could use the FileReference class—for example, to create an interface that browses for a particular SWF file on your hard drive. When the run-time library is successfully loaded, Flash Player calls the runtimeAssetsLoadComplete() method: private function runtimeAssetsLoadComplete(event:Event):void { var rl:* = event.target.content; var assetList:Array = rl.getAssets(); populateDropdown(assetList); stage.frameRate = 60; } In this method, the variable rl represents the loaded SWF file. The code calls the getAssets() method of the loaded SWF file, obtaining the list of assets that are available, and uses them to populate a ComboBox component with a list of available assets by calling the populateDropDown() method. That method in turn stores the full classpath of each asset. Clicking the Add button on the user interface triggers the addAsset() method: private function addAsset():void { var className:String = assetNameCbo.selectedItem.data; var AssetClass:Class = getDefinitionByName(className) as Class; var mc:MovieClip = new AssetClass(); ... }
  • 507. Example: RuntimeAssetsExplorer 507 which gets the classpath of whichever asset is currently selected in the ComboBox (assetNameCbo.selectedItem.data), and uses the getDefinitionByName() function (from the flash.utils package) to obtain an actual reference to the asset’s class in order to create a new instance of that asset.
  • 508. 508 Working with movie clips
  • 509. 509 17 CHAPTER 17 Working with text In ActionScript 3.0, text is usually displayed within a text field, but can occasionally appear as a property of an item on the display list (for example, as the label on a UI component). This chapter explains how to work with the script-defined contents of a text field and with user input, dynamic text from a remote file, or static text defined in Adobe Flash CS3 Professional. As an ActionScript 3.0 programmer, you can establish specific content for text fields, or designate the source for the text, and then set the appearance of that text using styles and formats. You can also respond to user events as the user inputs text or clicks a hyperlink. Contents Basics of working with text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 Displaying text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513 Selecting and manipulating text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 Capturing text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 Restricting text input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Formatting text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520 Advanced text rendering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .525 Working with static text. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .528 Example: Newspaper-style text formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
  • 510. 510 Working with text Basics of working with text Introduction to working with text To display any text on the screen in Adobe Flash Player, you use an instance of the TextField class. The TextField class is the basis for other text-based components, like the TextArea components or the TextInput components, that are provided in the Adobe Flex framework and in the Flash authoring environment. For more information about using text components in the Flash authoring environment, please see “About text controls” in Using Flash. Text field content can be pre-specified in the SWF file, loaded from an external source like a text file or database, or entered by users interacting with your application. Within a text field, the text can appear as rendered HTML content, with images embedded in the rendered HTML. Once you establish an instance of a text field, you can use flash.text package classes, like the TextFormat class and the StyleSheet class, to control the text’s appearance. The flash.text package contains nearly all the classes related to creating, managing, and formatting text in ActionScript. You can format text by defining the formatting with a TextFormat object and assigning that object to the text field. If your text field contains HTML text, you can apply a StyleSheet object to the text field to assign styles to specific pieces of the text field content. The TextFormat object or StyleSheet object contains properties defining the appearance of the text, such as color, size, and weight. The TextFormat object assigns the properties to all the content within a text field or to a range of text. For example, within the same text field, one sentence can be bold red text and the next sentence can be blue italic text. For more information on text formats, see “Assigning text formats” on page 521. For more information on HTML text in text fields, see “Displaying HTML text” on page 514. For more information on style sheets, see “Applying cascading style sheets” on page 521. In addition to the classes in the flash.text package, you can use the flash.events.TextEvent class to respond to user actions related to text.
  • 511. Basics of working with text 511 Common tasks for working with text The following common text-related tasks are described in this chapter: ■ Modifying text field contents ■ Using HTML in text fields ■ Using images in text fields ■ Selecting text and working with user-selected text ■ Capturing text input ■ Restricting text input ■ Applying formatting and CSS styles to text ■ Fine-tuning text display with sharpness, thickness, and anti-aliasing ■ Accessing and working with static text fields from ActionScript Important concepts and terms The following reference list contains important terms that you will encounter in this chapter: ■ Cascading style sheets: A standard syntax for specifying styles and formatting for content that’s structured in XML (or HTML) format. ■ Device font: A font that is installed on the user’s machine. ■ Dynamic text field: A text field whose contents can be changed by ActionScript but not by user input. ■ Embedded font: A font that has data its character outline data stored in the application’s SWF file. ■ HTML text: Text content entered into a text field using ActionScript that includes HTML formatting tags along with actual text content. ■ Input text field: A text field whose contents can be changed either by user input or by ActionScript. ■ Static text field: A text field created in the Flash authoring tool, whose content cannot change when the SWF file is running. ■ Text line metrics: Measurements of the size of various parts of the text content in a text field, such as the baseline of the text, the height of the top of the characters, size of descenders (the part of some lowercase letters that extends below the baseline), and so on.
  • 512. 512 Working with text Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because this chapter is about working with text fields in ActionScript, essentially all the code listings in this chapter involve manipulating a TextField object, either one that has been created and placed on the Stage in the Flash authoring tool, or one that’s created using ActionScript. Testing a sample involves viewing the result in Flash Player to see the effects of the code on the text field. The examples in this chapter fall into two groups. One type of example manipulates a TextField object without creating the object explicitly. To test these code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. Using the Text tool, create a dynamic text field on the Stage. 5. With the text field selected, in the Property inspector, give it an instance name. The name should match the name used for the text field in the example code listing—for example, if the code listing manipulates a text field named myTextField, you should name your text field myTextField as well. 6. Run the program using Control > Test Movie. On the screen you will see the results of the code manipulating the text field as specified in the code listing. The other type of example code listing in this chapter consists of a class definition that is intended to be used as the document class for the SWF. In those listings, a TextField instance is created by the example code, so you will not need to create one separately. To test this type of code listing: 1. Create an empty Flash document and save it to your computer. 2. Create a new ActionScript file and save it in the same directory as the Flash document. The file’s name should match the name of the class in the code listing. For instance, if the code listing defines a class named TextFieldTest, use the name TextFieldTest.as to save the ActionScript file. 3. Copy the code listing into the ActionScript file and save the file. 4. In the Flash document, click a blank part of the Stage or work space to activate the document Property inspector.
  • 513. Displaying text 513 5. In the Property inspector, in the Document Class field, enter the name of the ActionScript class you copied from the text. 6. Run the program using Control > Test Movie. You will see the results of the example displayed on the screen. Other techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63. Displaying text Although authoring tools like Adobe Flex Builder and the Flash authoring tool provide several options for displaying text, including text-related components or text tools, the primary way to display text programmatically is through a text field. Types of text The type of text within a text field is characterized by its source: ■ Dynamic text Dynamic text includes content that is loaded from an external source, such as a text file, an XML file, or even a remote web service. For more information, see “Types of text” on page 513. ■ Input text Input text is any text entered by a user or dynamic text that a user can edit. You can set up a style sheet to format input text, or use the flash.text.TextFormat class to assign properties to the text field for the input content. For more information, see “Capturing text input” on page 518. ■ Static text Static text is created through the Flash authoring tool only. You cannot create a static text instance using ActionScript 3.0. However, you can use ActionScript classes like StaticText and TextSnapshot to manipulate an existing static text instance. For more information, see “Working with static text” on page 528.
  • 514. 514 Working with text Modifying the text field contents You can define dynamic text by assigning a string to the flash.text.TextField.text property. You assign a string directly to the property, as follows: myTextField.text = “Hello World”; You can also assign the text property a value from a variable defined in your script, as in the following example: package { import flash.display.Sprite; import flash.text.*; public class TextWithImage extends Sprite { private var myTextBox:TextField = new TextField(); private var myText:String = "Hello World"; public function TextWithImage() { addChild(myTextBox); myTextBox.text = myText; } } } Alternatively, you can assign the text property a value from a remote variable. You have three options for loading text values from remote sources: ■ The flash.net.URLLoader and flash.net.URLRequest classes load variables for the text from a local or remote location. ■ The FlashVars attribute is embedded in the HTML page hosting the SWF file and can contain values for text variables. ■ The flash.net.SharedObject class manages persistent storage of values. For more information, see “Storing local data” on page 668. Displaying HTML text The flash.text.TextField class has an htmlText property that you can use to identify your text string as one containing HTML tags for formatting the content. As in the following example, you must assign your string value to the htmlText property (not the text property) for Flash Player to render the text as HTML: var myText:String = "<p>This is <b>some</b> content to <i>render</i> as <u>HTML</u> text.</p>"; myTextBox.htmlText = myText;
  • 515. Displaying text 515 Flash Player supports a subset of HTML tags and entities for the htmlText property. The flash.text.TextField.htmlText property description in the ActionScript 3.0 Language and Components Reference provides detailed information about the supported HTML tags and entities. Once you designate your content using the htmlText property, you can use style sheets or the textformat tag to manage the formatting of your content. For more information, see “Formatting text” on page 520. Using images in text fields Another advantage to displaying your content as HTML text is that you can include images in the text field. You can reference an image, local or remote, using the img tag and have it appear within the associated text field. The following example creates a text field named myTextBox and includes a JPG image of an eye, stored in the same directory as the SWF file, within the displayed text: package { import flash.display.Sprite; import flash.text.*; public class TextWithImage extends Sprite { private var myTextBox:TextField; private var myText:String = "<p>This is <b>some</b> content to <i>test</i> and <i>see</i></p><p><img src='eye.jpg' width='20' height='20'></p><p>what can be rendered.</p><p>You should see an eye image and some <u>HTML</u> text.</p>"; public function TextWithImage() { myTextBox.width = 200; myTextBox.height = 200; myTextBox.multiline = true; myTextBox.wordWrap = true; myTextBox.border = true; addChild(myTextBox); myTextBox.htmlText = myText; } } } The img tag supports JPEG, GIF, PNG, and SWF files.
  • 516. 516 Working with text Scrolling text in a text field In many cases, your text will be longer than the text field displaying the text. Or you may have an input field that allows a user to input more text than can be displayed at one time. You can use the scroll-related properties of the flash.text.TextField class to manage lengthy content, either vertically or horizontally. The scroll-related properties include TextField.scrollV, TextField.scrollH and maxScrollV and maxScrollH. Use these properties to respond to events, like a mouse click or a keypress. The following example creates a text field that is a set size and contains more text than the field can display at one time. As the user clicks on the text field, the text scrolls vertically. package { import flash.display.Sprite; import flash.text.*; import flash.events.MouseEvent; public class TextScrollExample extends Sprite { private var myTextBox:TextField = new TextField(); private var myText:String = "Hello world and welcome to the show. It's really nice to meet you. Take your coat off and stay a while. OK, show is over. Hope you had fun. You can go home now. Don't forget to tip your waiter. There are mints in the bowl by the door. Thank you. Please come again."; public function TextScrollExample() { myTextBox.text = myText; myTextBox.width = 200; myTextBox.height = 50; myTextBox.multiline = true; myTextBox.wordWrap = true; myTextBox.background = true; myTextBox.border = true; var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0xFF0000; format.size = 10; myTextBox.defaultTextFormat = format; addChild(myTextBox); myTextBox.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownScroll); }
  • 517. Selecting and manipulating text 517 public function mouseDownScroll(event:MouseEvent):void { myTextBox.scrollV++; } } } Selecting and manipulating text You can select dynamic or input text. Since the text selection properties and methods of the TextField class use index positions to set the range of text to manipulate, you can programmatically select dynamic or input text even if you don’t know the content. Selecting text The flash.text.TextField.selectable property is true by default, and you can programmatically select text using the setSelection() method. For example, you can set specific text within a text field to be selected when the user clicks on the text field: var myTextField:TextField = new TextField(); myTextField.text = "No matter where you click on this text field the TEXT IN ALL CAPS is selected."; myTextField.autoSize = TextFieldAutoSize.LEFT; addChild(myTextField); addEventListener(MouseEvent.CLICK, selectText); function selectText(event:MouseEvent):void { myTextField.setSelection(49, 65); } Similarly, if you want text within a text field to be selected as the text is initially displayed, create an event handler function that is called as the text field is added to the display list. NOTE In the Flash authoring tool, if you choose the selectable option on a static text field, the text field that is exported and placed on the display list is a regular, dynamic text field.
  • 518. 518 Working with text Capturing user-selected text The TextField class’s selectionBeginIndex and selectionEndIndex properties, which are “read-only” so they can’t be set to programmatically select text, can be used to capture whatever the user has currently selected. Additionally, input text fields can use the caretIndex property. For example, the following code traces the index values of user-selected text: var myTextField:TextField = new TextField(); myTextField.text = "Please select the TEXT IN ALL CAPS to see the index values for the first and last letters."; myTextField.autoSize = TextFieldAutoSize.LEFT; addChild(myTextField); addEventListener(MouseEvent.MOUSE_UP, selectText); function selectText(event:MouseEvent):void { trace("First letter index position: " + myTextField.selectionBeginIndex); trace("Last letter index position: " + myTextField.selectionEndIndex); } You can apply a collection of TextFormat object properties to the selection to change the text’s appearance. For more information about applying a collection of TextFormat properties to selected text, see “Formatting ranges of text within a text field” on page 524. Capturing text input By default, a text field’s type property is set to dynamic. If you set the type property to input using the TextFieldType class, you can collect user input and save the value for use in other parts of your application. Input text fields are useful for forms and any application that wants the user to define a text value for use elsewhere in the program. For example, the following code creates an input text field called myTextBox. As the user enters text in the field, the textInput event is triggered. An event handler called textInputCapture captures the string of text entered and assigns it a variable. Flash Player displays the new text in another text field, called myOutputBox. package { import flash.display.Sprite; import flash.display.Stage; import flash.text.*; import flash.events.*; public class CaptureUserInput extends Sprite {
  • 519. Capturing text input 519 private var myTextBox:TextField = new TextField(); private var myOutputBox:TextField = new TextField(); private var myText:String = "Type your text here."; public function CaptureUserInput() { captureText(); } public function captureText():void { myTextBox.type = TextFieldType.INPUT; myTextBox.background = true; addChild(myTextBox); myTextBox.text = myText; myTextBox.addEventListener(TextEvent.TEXT_INPUT, textInputCapture); } public function textInputCapture(event:TextEvent):void { var str:String = myTextBox.text; createOutputBox(str); } public function createOutputBox(str:String):void { myOutputBox.background = true; myOutputBox.x = 200; addChild(myOutputBox); myOutputBox.text = str; } } }
  • 520. 520 Working with text Restricting text input Since input text fields are often used for forms or dialog boxes in applications, you may want to limit the types of characters a user can enter in a text field, or even keep the text hidden — for example, for a password. The flash.text.TextField class has a displayAsPassword property and a restrict property that you can set to control user input. The displayAsPassword property simply hides the text (displaying it as a series of asterisks) as the user types it. When displayAsPassword is set to true, the Cut and Copy commands and their corresponding keyboard shortcuts will not function. As the following example shows, you assign the displayAsPassword property just as you would other properties, such as background and color: myTextBox.type = TextFieldType.INPUT; myTextBox.background = true; myTextBox.displayAsPassword = true; addChild(myTextBox); The restrict property is a little more complicated since you need to specify what characters the user is allowed to type in an input text field. You can allow specific letters, numbers, or ranges of letters, numbers, and characters. The following code allows the user to enter only uppercase letters (and not numbers or special characters) in the text field: myTextBox.restrict = “A-Z”; ActionScript 3.0 uses hyphens to define ranges, and carets to define excluded characters. For more information about defining what is restricted in an input text field, see the flash.text.TextField.restrict property entry in the ActionScript 3.0 Language and Components Reference. Formatting text You have several options for programmatically formatting the display of text. You can set properties directly on the TextField instance—for example, the TextFIeld.thickness, TextField.textColor, and TextField.textHeight properties. Or you can designate the content of the text field using the htmlText property and use the supported HTML tags, such as b, i, and u. But you can also apply TextFormat objects to text fields containing plain text, or StyleSheet objects to text fields containing the htmlText property. Using TextFormat and StyleSheet objects provides the most control and consistency over the appearance of text throughout your application. You can define a TextFormat or StyleSheet object and apply it to many or all text fields in your application.
  • 521. Formatting text 521 Assigning text formats You can use the TextFormat class to set a number of different text display properties and to apply them to the entire contents of a TextField object, or to a range of text. The following example applies one TextFormat object to an entire TextField object and applies a second TextFormat object to a range of text within that TextField object: var tf:TextField = new TextField(); tf.text = "Hello Hello"; var format1:TextFormat = new TextFormat(); format1.color = 0xFF0000; var format2:TextFormat = new TextFormat(); format2.font = "Courier"; tf.setTextFormat(format1); var startRange:uint = 6; tf.setTextFormat(format2, startRange); addChild(tf); The TextField.setTextFormat() method only affects text that is already displayed in the text field. If the content in the TextField changes, your application might need to call the TextField.setTextFormat() method again to reapply the formatting. You can also set the TextField object’s defaultTextFormat property to specify the format to be used for user- entered text. Applying cascading style sheets Text fields can contain either plain text or HTML-formatted text. Plain text is stored in the text property of the instance, and HTML text is stored in the htmlText property. You can use CSS style declarations to define text styles that you can apply to many different text fields. CSS style declarations can be created in your application code or loaded in at run time from an external CSS file. The flash.text.StyleSheet class handles CSS styles. The StyleSheet class recognizes a limited set of CSS properties. For a detailed list of the style properties that the StyleSheet class supports, see the flash.textStylesheet entry in the ActionScript 3.0 Language and Components Reference.
  • 522. 522 Working with text As the following example shows, you can create CSS in your code and apply those styles to HTML text by using a StyleSheet object: var style:StyleSheet = new StyleSheet(); var styleObj:Object = new Object(); styleObj.fontSize = "bold"; styleObj.color = "#FF0000"; style.setStyle(".darkRed", styleObj); var tf:TextField = new TextField(); tf.styleSheet = style; tf.htmlText = "<span class = 'darkRed'>Red</span> apple"; addChild(tf); After creating a StyleSheet object, the example code creates a simple object to hold a set of style declaration properties. Then it calls the StyleSheet.setStyle() method, which adds the new style to the stylesheet with the name “.darkred”. Next, it applies the stylesheet formatting by assigning the StyleSheet object to the TextField object’s styleSheet property. For CSS styles to take effect, the stylesheet should be applied to the a TextField object before the htmlText property is set. By design, a text field with a style sheet is not editable. If you have an input text field and assign a style sheet to it, the text field shows the style sheet’s properties, but the text field will not allow users to enter new text into it. Also, you cannot use the following ActionScript APIs on a text field with an assigned style sheet: ■ The TextField.replaceText() method ■ The TextField.replaceSelectedText() method ■ The TextField.defaultTextFormat property ■ The TextField.setTextFormat() method If a text field has a style sheet assigned to it, but later the TextField.styleSheet property is set to null, the contents of both TextField.text and TextField.htmlText properties will add tags and attributes to their content to incorporate the formatting from the previously assigned style sheet. To preserve the original htmlText property, save it in a variable before setting the style sheet to null.
  • 523. Formatting text 523 Loading an external CSS file The CSS approach to formatting is more powerful when you can load CSS information from an external file at run time. When the CSS data is external to the application itself, you can change the visual style of text in your application without having to change your ActionScript 3.0 source code. After your application has been deployed, you can change an external CSS file to change the look of the application, without having to redeploy the application’s SWF file. The StyleSheet.parseCSS() method converts a string that contains CSS data into style declarations in the StyleSheet object. The following example shows how to read an external CSS file and apply its style declarations to a TextField object. First, here is the content of the CSS file to be loaded, which will be named example.css: p { font-family: Times New Roman, Times, _serif; font-size: 14; } h1 { font-family: Arial, Helvetica, _sans; font-size: 20; font-weight: bold; } .bluetext { color: #0000CC; } Next is the ActionScript code for a class that loads the example.css file and applies the styles to TextField content: package { import flash.display.Sprite; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; import flash.text.StyleSheet; import flash.text.TextField; import flash.text.TextFieldAutoSize;
  • 524. 524 Working with text public class CSSFormattingExample extends Sprite { var loader:URLLoader; var field:TextField; var exampleText:String = "<h1>This is a headline</h1>” + “<p>This is a line of text. <span class='bluetext'>” + “This line of text is colored blue.</span></p>"; public function CSSFormattingExample():void { field = new TextField(); field.width = 300; field.autoSize = TextFieldAutoSize.LEFT; field.wordWrap = true; addChild(field); var req:URLRequest = new URLRequest("example.css"); loader = new URLLoader(); loader.addEventListener(Event.COMPLETE, onCSSFileLoaded); loader.load(req); } public function onCSSFileLoaded(event:Event):void { var sheet:StyleSheet = new StyleSheet(); sheet.parseCSS(loader.data); field.styleSheet = sheet; field.htmlText = exampleText; } } } When the CSS data is loaded, the onCSSFileLoaded() method executes and calls the StyleSheet.parseCSS() method to transfer the style declarations to the StyleSheet object. Formatting ranges of text within a text field A particularly useful method of the flash.text.TextField class is the setTextFormat() method. Using setTextFormat(), you can assign specific properties to part of a text field’s contents to respond to user input, such as forms that need to remind users that certain entries are required or to change the emphasis of a subsection of a passage of text within a text field as a user selects parts of the text.
  • 525. Advanced text rendering 525 The following example uses TextField.setTextFormat() on a range of characters to change the appearance of part of the content of myTextField when the user clicks on the text field: var myTextField:TextField = new TextField(); myTextField.text = "No matter where you click on this text field the TEXT IN ALL CAPS changes format."; myTextField.autoSize = TextFieldAutoSize.LEFT; addChild(myTextField); addEventListener(MouseEvent.CLICK, changeText); var myformat:TextFormat = new TextFormat(); myformat.color = 0xFF0000; myformat.size = 18; myformat.underline = true; function changeText(event:MouseEvent):void { myTextField.setTextFormat(myformat, 49, 65); } Advanced text rendering ActionScript 3.0 provides a variety of classes in the flash.text package to control the properties of displayed text, including embedded fonts, anti-aliasing settings, alpha channel control, and other specific settings. The ActionScript 3.0 Language and Components Reference provides detailed descriptions of these classes and properties, including the CSMSettings, Font, and TextRenderer classes. Using embedded fonts When you specify a specific font for a TextField in your application, Flash Player will look for a device font (a font that resides on the user’s computer) with the same name. If it doesn’t find that font on the user’s system, or if the user has a slightly different version of a font with that name, the text display could look very different from what you intend. To make sure the user sees exactly the right font, you can embed that font in your application’s SWF file. Embedded fonts have a number of benefits: ■ Embedded font characters are anti-aliased, making their edges appear smoother, especially for larger text. ■ You can rotate text that uses embedded fonts. ■ Embedded font text can be made transparent or semitransparent. ■ You can use the kerning CSS style with embedded fonts.
  • 526. 526 Working with text The biggest limitation to using embedded fonts is that they increase the file size or download size of your application. The exact method of embedding a sound file into your application’s SWF file varies according to your development environment. Once you have embedded a font you can make sure a TextField uses the correct embedded font: ■ Set the embedFonts property of the TextField to true. ■ Create a TextFormat object, set its fontFamily property to the name of the embedded font, and apply the TextFormat object to the TextField. When specifying an embedded font, the fontFamily property should only contain a single name; it cannot use a comma- delimited list of multiple font names. ■ If using CSS styles to set fonts for TextFields or components, set the font-family CSS property to the name of the embedded font. The font-family property must contain a single name and not a list of names if you want to specify an embedded font. Embedding a font in Flash The Flash authoring tool lets you embed almost any font you have installed on your system, including TrueType fonts and Type 1 Postscript fonts. There are many ways to embed fonts in a Flash application, including: ■ Setting the font and style properties of a TextField on the Stage and clicking the Embed Fonts checkbox ■ Creating and referencing a font symbol ■ Creating and using a run-time shared library containing embedded font symbols For more details about how to embed fonts in Flash applications, see “Embedded fonts for dynamic or input text fields” in Using Flash.
  • 527. Advanced text rendering 527 Controlling sharpness, thickness, and anti-aliasing By default, Flash Player determines the settings for text display controls like sharpness, thickness, and anti-aliasing as text resizes, changes color, or is displayed on various backgrounds. In some cases, like when you have very small or very large text, or text on a variety of unique backgrounds, you may want to maintain your own control over these settings. You can override the Flash Player settings using the flash.text.TextRenderer class and its associated classes, like the CSMSettings class. These classes give you precise control over the rendering quality of embedded text. For more information about embedded fonts, see “Using embedded fonts” on page 525. The following example applies custom continuous stroke modulation (CSM) properties and formatting to displayed text using an embedded font called myFont. When the user clicks on the displayed text, Flash Player applies the custom settings: var format:TextFormat = new TextFormat(); format.color = 0x336699; format.size = 48; format.font = "myFont"; var myText:TextField = new TextField(); myText.embedFonts = true; myText.autoSize = TextFieldAutoSize.LEFT; myText.antiAliasType = AntiAliasType.ADVANCED; myText.defaultTextFormat = format; myText.selectable = false; myText.mouseEnabled = true; myText.text = "Hello World"; addChild(myText); myText.addEventListener(MouseEvent.CLICK, clickHandler); function clickHandler(event:Event):void { var myAntiAliasSettings = new CSMSettings(48, 0.8, -0.8); var myAliasTable:Array = new Array(myAntiAliasSettings); TextRenderer.setAdvancedAntiAliasingTable("myFont", FontStyle.ITALIC, TextColorType.DARK_COLOR, myAliasTable); } NOTE The flash.text.TextField.antiAliasType property must have the value AntiAliasType.ADVANCED (which is the default value) in order for you to set the sharpness, thickness, or the gridFitType property, or to use the TextRenderer.setAdvancedAntiAliasingTable() method.
  • 528. 528 Working with text Working with static text Static text is created only within the Flash authoring tool. You cannot programmatically instantiate static text using ActionScript. Static text is useful if the text is very short and is not intended to change (as dynamic text can). Think of static text as a sort of graphic element like a circle or square drawn on the Stage in the Flash authoring tool. While static text is more limited than dynamic text, ActionScript 3.0 does support the ability to read the property values of static text using the flash.text.StaticText class. In addition, you can use the flash.text.TextSnapshot class to read values out of the static text. Accessing static text fields with the StaticText class Typically, you use the flash.text.StaticText class in the Actions panel of the Flash authoring tool to interact with a static text instance placed on the Stage. You may also work in ActionScript files that interact with a SWF file containing static text. In either case, you can’t instantiate a static text instance programmatically. Static text is created in the Flash CS3 authoring tool. To create a reference to an existing static text field in ActionScript 3.0, you can iterate over the items in the display list and assign a variable. For example: for (var i = 0; i < this.numChildren; i++) { var displayitem:DisplayObject = this.getChildAt(i); if (displayitem instanceof StaticText) { trace("a static text field is item " + i + " on the display list"); var myFieldLabel:StaticText = StaticText(displayitem); trace("and contains the text: " + myFieldLabel.text); } } Once you have a reference to a static text field, you can use the properties of that field in ActionScript 3.0. The following code is attached to a frame in the Timeline, and assumes a variable named myFieldLabel assigned to a static text reference. In the example, a dynamic text field named myField is positioned relative to the x and y values of myFieldLabel and displays the value of myFieldLabel again. var myField:TextField = new TextField(); addChild(myField); myField.x = myFieldLabel.x; myField.y = myFieldLabel.y + 20; myField.autoSize = TextFieldAutoSize.LEFT; myField.text = "and " + myFieldLabel.text
  • 529. Example: Newspaper-style text formatting 529 Using the TextSnapshot class If you want to programmatically work with an existing static text instance, you can use the flash.text.TextSnapshot class to work with the textSnapshot property of a flash.display.DisplayObjectContainer. In other words, you create a TextSnapshot instance from the DisplayObjectContainer.textSnapshot property. You can then apply methods to that instance to retrieve values or select parts of the static text. For example, place a static text field that contains the text "TextSnapshot Example" on the Stage. Add the following ActionScript to Frame 1 of the Timeline: var mySnap:TextSnapshot = this.getTextSnapshot(); var count:Number = mySnap.getCount(); mySnap.setSelected(0, 4, true); mySnap.setSelected(1, 2, false); var myText:String = mySnap.getSelectedText(false); trace(myText); The TextSnapshot class is useful for getting the text out of static text fields in a loaded SWF, in case you want to use the text as a value in another part of an application. Example: Newspaper-style text formatting The News Layout example formats text to look something like a story in a printed newspaper. The input text can contain a headline, a subtitle, and the body of the story. Given a display width and height, this News Layout example will format the headline and the subtitle to take the full width of the display area. The story text will be distributed across two or more columns. This example illustrates the following ActionScript programming techniques: ■ Extending the TextField class ■ Loading and applying an external CSS file ■ Converting CSS styles into TextFormat objects ■ Using the TextLineMetrics class to get information about text display size
  • 530. 530 Working with text To get the application files for this sample, see www.adobe.com/go/ learn_programmingAS3samples_flash. The News Layout application files can be found in the folder Samples/NewsLayout. The application consists of the following files: Reading the external CSS file The News Layout application starts by reading story text from a local XML file. Then it reads an external CSS file that provides the formatting information for the headline, subtitle, and main text. The CSS file defines three styles, a standard paragraph style for the story, and the h1 and h2 styles for the headline and subtitle respectively. p { font-family: Georgia, Times New Roman, Times, _serif; font-size: 12; leading: 2; text-align: justify; } h1 { font-family: Verdana, Arial, Helvetica, _sans; font-size: 20; font-weight: bold; color: #000099; text-align: left; } File Description NewsLayout.mxml or NewsLayout.fla The user interface for the application for Flex (MXML) or Flash (FLA). StoryLayout.as The main ActionScript class that arranges all the components of a news story for display. FormattedTextField.as A subclass of the TextField class that manages its own TextFormat object. HeadlineTextField.as A subclass of the FormattedTextField class that adjusts font sizes to fit a desired width. MultiColumnTextField.as An ActionScript class that splits text across two or more columns. story.css A CSS file that defines text styles for the layout. newsconfig.xml An XML file containing story content.
  • 531. Example: Newspaper-style text formatting 531 h2 { font-family: Verdana, Arial, Helvetica, _sans; font-size: 16; font-weight: normal; text-align: left; } The technique used to read the external CSS file is that same as the technique described in “Loading an external CSS file” on page 523. When the CSS file has been loaded the application executes the onCSSFileLoaded() method, shown below. public function onCSSFileLoaded(event:Event):void { this.sheet = new StyleSheet(); this.sheet.parseCSS(loader.data); h1Format = getTextStyle("h1", this.sheet); if (h1Format == null) { h1Format = getDefaultHeadFormat(); } h2Format = getTextStyle("h2", this.sheet); if (h2Format == null) { h2Format = getDefaultHeadFormat(); h2Format.size = 16; } displayStory(); } The onCSSFileLoaded() method creates a new StyleSheet object and has it parse the input CSS data. The main text for the story will be displayed in a MultiColumnTextField object, which can use a StyleSheet object directly. However, the headline fields use the HeadlineTextField class, which uses a TextFormat object for its formatting. The onCSSFileLoaded() method calls the getTextStyle() method twice to convert a CSS style declaration into a TextFormat object for use with each of the two HeadlineTextField objects. The getTextStyle() method is shown below: public function getTextStyle(styleName:String, ss:StyleSheet):TextFormat { var format:TextFormat = null; var style:Object = ss.getStyle(styleName); if (style != null) {
  • 532. 532 Working with text var colorStr:String = style.color; if (colorStr != null && colorStr.indexOf("#") == 0) { style.color = colorStr.substr(1); } format = new TextFormat(style.fontFamily, style.fontSize, style.color, (style.fontWeight == "bold"), (style.fontStyle == "italic"), (style.textDecoration == "underline"), style.url, style.target, style.textAlign, style.marginLeft, style.marginRight, style.textIndent, style.leading); if (style.hasOwnProperty("letterSpacing")) { format.letterSpacing = style.letterSpacing; } } return format; } The property names and the meaning of the property values differ between CSS style declarations and TextFormat objects. The getTextStyle() method translates CSS property values into the values expected by the TextFormat object. Arranging story elements on the page The StoryLayout class formats and lays out the headline, subtitle, and main text fields into a newspaper-style arrangement. The displayText() method initially creates and places the various fields. public function displayText():void { headlineTxt = new HeadlineTextField(h1Format); headlineTxt.wordWrap = true; this.addChild(headlineTxt); headlineTxt.width = 600; headlineTxt.height = 100; headlineTxt.fitText(this.headline, 1, true);
  • 533. Example: Newspaper-style text formatting 533 subtitleTxt = new HeadlineTextField(h2Format); subtitleTxt.wordWrap = true; subtitleTxt.y = headlineTxt.y + headlineTxt.height; this.addChild(subtitleTxt); subtitleTxt.width = 600; subtitleTxt.height = 100; subtitleTxt.fitText(this.subtitle, 1, false); storyTxt = new MultiColumnTextField(2, 10, 600, 200); storyTxt.y = subtitleTxt.y + subtitleTxt.height + 4; this.addChild(storyTxt); storyTxt.styleSheet = this.sheet; storyTxt.htmlText = loremIpsum; } Each field is simply placed below the previous field by setting its y property equal to the y property of the previous field plus the height of the previous field. This dynamic placement calculation is needed because HeadlineTextField objects and MultiColumnTextField objects can change their height to fit their contents. Altering font size to fit the field size Given a width in pixels and a maximum number of lines to display, the HeadlineTextField alters the font size to make the text fit the field. If the text is short the font size will be very large, creating a tabloid-style headline. If the text is long the font size will of course be smaller. The HeadlineTextField.fitText() method shown below does the font sizing work: public function fitText(msg:String, maxLines:uint = 1, toUpper:Boolean = false, targetWidth:Number = -1):uint { this.text = toUpper ? msg.toUpperCase() : msg; if (targetWidth == -1) { targetWidth = this.width; } var pixelsPerChar:Number = targetWidth / msg.length; var pointSize:Number = Math.min(MAX_POINT_SIZE, Math.round(pixelsPerChar * 1.8 * maxLines)); if (pointSize < 6) {
  • 534. 534 Working with text // the point size is too small return pointSize; } this.changeSize(pointSize); if (this.numLines > maxLines) { return shrinkText(--pointSize, maxLines); } else { return growText(pointSize, maxLines); } } public function growText(pointSize:Number, maxLines:uint = 1):Number { if (pointSize >= MAX_POINT_SIZE) { return pointSize; } this.changeSize(pointSize + 1); if (this.numLines > maxLines) { // set it back to the last size this.changeSize(pointSize); return pointSize; } else { return growText(pointSize + 1, maxLines); } } public function shrinkText(pointSize:Number, maxLines:uint=1):Number { if (pointSize <= MIN_POINT_SIZE) { return pointSize; }
  • 535. Example: Newspaper-style text formatting 535 this.changeSize(pointSize); if (this.numLines > maxLines) { return shrinkText(pointSize - 1, maxLines); } else { return pointSize; } } The HeadlineTextField.fitText() method uses a simple recursive technique to size the font. First it guesses an average number of pixels per character in the text and from there calculates a starting point size. Then it changes the text field’s font size and checks whether the text has word wrapped to create more than the maximum number of text lines. If there are too many lines it calls the shrinkText() method to decrease the font size and try again. If there are not too many lines it calls the growText() method to increase the font size and try again. The process stops at the point where incrementing the font size by one more point would create too many lines. Splitting text across multiple columns The MultiColumnTextField class spreads text among multiple TextField objects which are then arranged like newspaper columns. The MultiColumnTextField() constructor first creates an array of TextField objects, one for each column, as shown here: for (var i:int = 0; i < cols; i++) { var field:TextField = new TextField(); field.autoSize = TextFieldAutoSize.NONE; field.wordWrap = true; field.styleSheet = this.styleSheet; this.fieldArray.push(field); this.addChild(field); } Each TextField object is added to the array and added to the display list with the addChild() method. Whenever the StoryLayout object’s text property or styleSheet property changes, it calls the layoutColumns() method to redisplay the text. The layoutColumns() method calls the getOptimalHeight() method, shown below, to figure out the correct pixel height needed to fit all of the text within the given layout width.
  • 536. 536 Working with text public function getOptimalHeight(str:String):int { if (fieldArray.length == 0 || str == "" || str == null) { return this.preferredHeight; } else { var colWidth:int = Math.floor( (this.preferredWidth - ((this.numColumns - 1) * gutter)) / this.numColumns); var field:TextField = fieldArray[0] as TextField; field.width = colWidth; field.htmlText = str; var linesPerCol:int = Math.ceil(field.numLines / this.numColumns); var metrics:TextLineMetrics = field.getLineMetrics(0); var prefHeight:int = linesPerCol * metrics.height; return prefHeight + 4; } } First the getOptimalHeight() method calculates the width of each column. Then it sets the width and htmlText property of the first TextField object in the array. The getOptimalHeight() method uses that first TextField object to discover the total number of word-wrapped lines in the text, and from that it identifies how many lines should be in each column. Next it calls the TextField.getLineMetrics() method to retrieve a TextLineMetrics object that contains details about size of the text in the first line. The TextLineMetrics.height property represents the full height of a line of text, in pixels, including the ascent, descent, and leading. The optimal height for the MultiColumnTextField object is then the line height multiplied by the number of lines per column, plus 4 to account for the 2 pixel border at the top and the bottom of a TextField object. Here is the code for the full layoutColumns() method: public function layoutColumns():void { if (this._text == "" || this._text == null) { return; } if (this.fitToText) { this.preferredHeight = this.getOptimalHeight(this._text); } var colWidth:int = Math.floor( (this.preferredWidth - ((numColumns - 1) * gutter)) / numColumns);
  • 537. Example: Newspaper-style text formatting 537 var field:TextField; var remainder:String = this._text; var fieldText:String = ""; for (var i:int = 0; i < fieldArray.length; i++) { field = this.fieldArray[i] as TextField; field.width = colWidth; field.height = this.preferredHeight; field.x = i * (colWidth + gutter); field.y = 0; field.htmlText = "<p>" + remainder + "</p>"; remainder = ""; fieldText = ""; var linesRemaining:int = field.numLines; var linesVisible:int = field.numLines - field.maxScrollV + 1; for (var j:int = 0; j < linesRemaining; j++) { if (j < linesVisible) { fieldText += field.getLineText(j); } else { remainder += field.getLineText(j); } } field.htmlText = "<p>" + fieldText + "</p>"; } } After the preferredHeight property has been set by calling the getOptimalHeight() method, the layoutColumns() method iterates through the TextField objects, setting the height of each to the preferredHeight value. The layoutColumns() method then distributes just enough lines of text to each field so that no scrolling occurs in any individual field, and the text in each successive field begins where the text in the previous field ended.
  • 539. 539 18 CHAPTER 18 Working with bitmaps In addition to its vector drawing capabilities, ActionScript 3.0 includes the ability to create bitmap images or manipulate the pixel data of external bitmap images that are loaded into a SWF. With the ability to access and change individual pixel values, you can create your own filter-like image effects and use the built-in noise functions to create textures and random noise. All of these techniques are described in this chapter. Contents Basics of working with bitmaps. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540 The Bitmap and BitmapData classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .543 Manipulating pixels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545 Copying bitmap data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549 Making textures with noise functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550 Scrolling bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .552 Example: Animating sprites using an offscreen bitmap . . . . . . . . . . . . . . . . . . . . . . .553
  • 540. 540 Working with bitmaps Basics of working with bitmaps Introduction to working with bitmaps When you work with digital images, you’re likely to encounter two main types of graphics: bitmap and vector. Bitmap graphics, also known as raster graphics, are composed of tiny squares (pixels) that are arranged in a rectangular grid formation. Vector graphics are composed of mathematically generated geometric shapes such as lines, curves, and polygons. Bitmap images are defined by the width and height of the image, measured in pixels, and the number of bits contained in each pixel, which represents the number of colors a pixel can contain. In the case of a bitmap image that utilizes the RGB color model, the pixels are made up of three bytes: red, green, and blue. Each of these bytes contains a value ranging from 0 to 255. When the bytes are combined within the pixel, they produce a color similar to an artist mixing paint colors. For example, a pixel containing byte values of red-255, green-102 and blue-0 would produce a vibrant orange color. The quality of a bitmap image is determined by combining the resolution of the image with its color depth bit value. Resolution relates to the number of pixels contained within an image. The greater the number of pixels, the higher the resolution and the finer the image appears. Color depth relates to the amount of information a pixel can contain. For example, an image that has a color depth value of 16 bits per pixel cannot represent the same number of colors as an image that has a color depth of 48 bits. As a result, the 48-bit image will have smoother degrees of shading than its 16-bit counterpart. Because bitmap graphics are resolution-dependent, they don’t scale very well. This is most noticeable when bitmap images are scaled up in size. Scaling up a bitmap usually results in a loss of detail and quality. Bitmap file formats Bitmap images are grouped into a number of common file formats. These formats use different types of compression algorithms to reduce file size, as well as optimize image quality based on the end purpose of the image. The bitmap image formats supported by Adobe Flash Player are GIF, JPG, and PNG.
  • 541. Basics of working with bitmaps 541 GIF The Graphics Interchange Format (GIF) was originally developed by CompuServe in 1987 as a means to transmit images with 256 colors (8-bit color). The format provides small file sizes and is ideal for web-based images. Because of this format’s limited color palette, GIF images are generally not suitable for photographs, which typically require high degrees of shading and color gradients. GIF images permit single-bit transparency, which allows colors to be mapped as clear (or transparent). This results in the background color of a web page showing through the image where the transparency has been mapped. JPEG Developed by the Joint Photographic Experts Group (JPEG), the JPEG (often written JPG) image format uses a lossy compression algorithm to allow 24-bit color depth with a small file size. Lossy compression means that each time the image is saved, the image loses quality and data but results in a smaller file size. The JPEG format is ideal for photographs because it is capable of displaying millions of colors. The ability to control the degree of compression applied to an image allows you to manipulate image quality and file size. PNG The Portable Network Graphics (PNG) format was produced as an open-source alternative to the patented GIF file format. PNGs support up to 64-bit color depth, allowing for up to 16 million colors. Because PNG is a relatively new format, some older browsers don’t support PNG files. Unlike JPGs, PNGs use lossless compression, which means that none of the image data is lost when the image is saved. PNG files also support alpha transparency, which allows for up to 256 levels of transparency. Transparent bitmaps and opaque bitmaps Bitmap images that use either the GIF or PNG formats can have an extra byte (alpha channel) added to each pixel. This extra pixel byte represents the transparency value of the pixel. GIF images allow single-bit transparency, which means that you can specify a single color, from a 256-color palette, to be transparent. PNG images, on the other hand, can have up to 256 levels of transparency. This function is especially beneficial when images or text are required to blend into backgrounds. ActionScript 3.0 replicates this extra transparency pixel byte within the BitmapData class. Similar to the PNG transparency model, the BitmapDataChannel.ALPHA constant offers up to 256 levels of transparency.
  • 542. 542 Working with bitmaps Common tasks for working with bitmaps The following list describes several tasks you may want to perform when working with bitmap images in ActionScript: ■ Displaying bitmaps on the screen ■ Retrieving and setting pixel color values ■ Copying bitmap data: ■ Creating an exact copy of a bitmap ■ Copying data from one color channel of a bitmap into one color channel of another bitmap ■ Copying a snapshot of a screen display object into a bitmap ■ Creating noise and textures in bitmap images ■ Scrolling bitmaps Important concepts and terms The following list contains important terms that you will encounter in this chapter: ■ Alpha: The level of transparency (or more accurately, opacity) in a color or an image. The amount of alpha is often described as the alpha channel value. ■ ARGB color: A color scheme where each pixel’s color is a mixture of red, green, and blue color values, and its transparency is specified as an alpha value. ■ Color channel: Commonly, colors are represented as a mixture of a few basic colors— usually (for computer graphics) red, green, and blue. Each basic color is considered a color channel; the amount of color in each color channel, mixed together, determines the final color. ■ Color depth: Also known as bit depth, this refers to the amount of computer memory that is devoted to each pixel, which in turn determines the number of possible colors that can be represented in the image. ■ Pixel: The smallest unit of information in a bitmap image—essentially a dot of color. ■ Resolution: The pixel dimensions of an image, which determines the level of fine-grained detail contained in the image. Resolution is often expressed in terms of width and height in number of pixels. ■ RGB color: A color scheme where each pixel’s color is represented as a mixture of red, green, and blue color values.
  • 543. The Bitmap and BitmapData classes 543 Working through in-chapter examples While you’re working through the chapter, you may want to test the example code. Because this chapter deals with creating and manipulating visual content, testing the code involves running the code and viewing the results in the SWF that’s created. To test the code examples in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code into the Script pane. 4. Run the program using Control > Test Movie. You see the results of the code in the SWF file that’s created. Nearly all the example code includes code that creates a bitmap image, so you can just test the code directly without needing to provide any bitmap content. Alternatively, if you want to test the code on your own image, you can import that image into Adobe Flash CS3 Professional or load the external image into the test SWF and use its bitmap data with the example code. For instructions on loading external images, see “Loading display content dynamically” on page 418. The Bitmap and BitmapData classes The main ActionScript 3.0 classes for working with bitmap images are the Bitmap class, which is used to display bitmap images on the screen, and the BitmapData class, which is used to access and manipulate the raw image data of a bitmap. Understanding the Bitmap class As a subclass of the DisplayObject class, the Bitmap class is the main ActionScript 3.0 class used for displaying bitmap images. These images may have been loaded into Flash via the flash.display.Loader class or created dynamically using the Bitmap() constructor. When loading an image from an external source, a Bitmap object can only use GIF, JPEG, or PNG format images. Once instantiated, the Bitmap instance can be considered a wrapper for a BitmapData object that needs to be rendered to the Stage. Because a Bitmap instance is a display object, all the characteristics and functionality of display objects can be used to manipulate a Bitmap instance as well. For more information about working with display objects, see Chapter 12, “Display programming,” on page 371.
  • 544. 544 Working with bitmaps Pixel snapping and smoothing In addition to the functionality common to all display objects, the Bitmap class provides some additional features that are specific to bitmap images. Similar to the snap-to-pixel feature found in the Flash authoring tool, the pixelSnapping property of the Bitmap class determines whether or not a Bitmap object snaps to its nearest pixel. This property accepts one of three constants defined in the PixelSnapping class: ALWAYS, AUTO, and NEVER. The syntax for applying pixel snapping is as follows: myBitmap.pixelSnapping = PixelSnapping.ALWAYS; Often, when bitmap images are scaled, they become blurred and distorted. To help reduce this distortion, use the smoothing property of the BitmapData class. This Boolean property, when set to true, smooths, or anti-aliases, the pixels within the image when it is scaled. This gives the image a clearer and more natural appearance. Understanding the BitmapData class The BitmapData class, which is in the flash.display package, can be likened to a photographic snapshot of the pixels contained within a loaded or dynamically created bitmap image. This snapshot is represented by an array of pixel data within the object. The BitmapData class also contains a series of built-in methods that are useful for creation and manipulation of pixel data. To instantiate a BitmapData object, use the following code: var myBitmap:BitmapData = new BitmapData(width:Number, height:Number, transparent:Boolean, fillColor:uinit); The width and height parameters specify the size of the bitmap; the maximum value of both is 2880 pixels. The transparent parameter specifies whether the bitmap data includes an alpha channel (true) or not (false). The fillColor parameter is a 32-bit color value that specifies the background color, as well as the transparency value (if it has been set to true). The following example creates a BitmapData object with an orange background that is 50 percent transparent: var myBitmap:BitmapData = new BitmapData(150, 150, true, 0x80FF3300);
  • 545. Manipulating pixels 545 To render a newly created BitmapData object to the screen, assign it to or wrap it in a Bitmap instance. To do this, you can either pass the BitmapData object as a parameter of the Bitmap object’s constructor, or you can assign it to the bitmapData property of an existing Bitmap instance. You must also add the Bitmap instance to the display list by calling the addChild() or addChildAt() methods of the display object container that will contain the Bitmap instance. For more information on working with the display list, see “Adding display objects to the display list” on page 383. The following example creates a BitmapData object with a red fill, and displays it in a Bitmap instance: var myBitmapDataObject:BitmapData = new BitmapData(150, 150, false, 0xFF0000); var myImage:Bitmap = new Bitmap(myBitmapDataObject); addChild(myImage); Manipulating pixels The BitmapData class contains a set of methods that allow you to manipulate pixel data values. Manipulating individual pixels When changing the appearance of a bitmap image at a pixel level, you first need to get the color values of the pixels contained within the area you wish to manipulate. You use the getPixel() method to read these pixel values. The getPixel() method retrieves an RGB value from a set of x, y (pixel) coordinates that are passed as a parameter. If any of the pixels that you want to manipulate include transparency (alpha channel) information, you need to use the getPixel32() method. This method also retrieves an RGB value, but unlike with getPixel(), the value returned by getPixel32() contains additional data that represents the alpha channel (transparency) value of the selected pixel. Alternatively, if you simply want to change the color or transparency of a pixel contained within a bitmap, you can use the setPixel() or setPixel32() method. To set a pixel’s color, simply pass in the x, y coordinates and the color value to one of these methods. The following example uses setPixel() to draw a cross on a green BitmapData background. It then uses getPixel() to retrieve the color value from the pixel at the coordinate 50, 50 and traces the returned value.
  • 546. 546 Working with bitmaps import flash.display.Bitmap; import flash.display.BitmapData; var myBitmapData:BitmapData = new BitmapData(100, 100, false, 0x009900); for (var i:uint = 0; i < 100; i++) { var red:uint = 0xFF0000; myBitmapData.setPixel(50, i, red); myBitmapData.setPixel(i, 50, red); } var myBitmapImage:Bitmap = new Bitmap(myBitmapData); addChild(myBitmapImage); var pixelValue:uint = myBitmapData.getPixel(50, 50); trace(pixelValue.toString(16)); If you want to read the value of a group of pixels, as opposed to a single pixel, use the getPixels() method. This method generates a byte array from a rectangular region of pixel data that is passed as a parameter. Each of the elements of the byte array (in other words, the pixel values) are unsigned integers—32-bit, unmultiplied pixel values. Conversely, in order to change (or set) the value of a group of pixels, use the setPixels() method. This method expects two parameters (rect and inputByteArray), which are combined to output a rectangular region (rect) of pixel data (inputByteArray). As data is read (and written) out of the inputByteArray, the ByteArray.readUnsignedInt() method is called for each of the pixels in the array. If, for some reason, the inputByteArray doesn't contain a full rectangle worth of pixel data, the method stops processing the image data at that point. It's important to remember that, for both getting and setting pixel data, the byte array expects 32-bit alpha, red, green, blue (ARGB) pixel values. The following example uses the getPixels() and setPixels() methods to copy a group of pixels from one BitmapData object to another: import flash.display.Bitmap; import flash.display.BitmapData; import flash.utils.ByteArray; import flash.geom.Rectangle; var bitmapDataObject1:BitmapData = new BitmapData(100, 100, false, 0x006666FF); var bitmapDataObject2:BitmapData = new BitmapData(100, 100, false, 0x00FF0000); var rect:Rectangle = new Rectangle(0, 0, 100, 100); var bytes:ByteArray = bitmapDataObject1.getPixels(rect);
  • 547. Manipulating pixels 547 bytes.position = 0; bitmapDataObject2.setPixels(rect, bytes); var bitmapImage1:Bitmap = new Bitmap(bitmapDataObject1); addChild(bitmapImage1); var bitmapImage2:Bitmap = new Bitmap(bitmapDataObject2); addChild(bitmapImage2); bitmapImage2.x = 110; Pixel-level collision detection The BitmapData.hitTest() method performs pixel-level collision detection between the bitmap data and another object or point. The BitmapData.hitTest() method accepts five parameters: ■ firstPoint (Point): This parameter refers to the pixel position of the upper-left corner of the first BitmapData upon which the hit test is being performed. ■ firstAlphaThreshold (uint): This parameter specifies the highest alpha channel value that is considered opaque for this hit test. ■ secondObject (Object): This parameter represents the area of impact. The secondObject object can be a Rectangle, Point, Bitmap, or BitmapData object. This object represents the hit area on which the collision detection is being performed. ■ secondBitmapDataPoint (Point): This optional parameter is used to define a pixel location in the second BitmapData object. This parameter is used only when the value of secondObject is a BitmapData object. The default is null. ■ secondAlphaThreshold (uint): This optional parameter represents the highest alpha channel value that is considered opaque in the second BitmapData object. The default value is 1. This parameter is only used when the value of secondObject is a BitmapData object and both BitmapData objects are transparent. When performing collision detection on opaque images, keep in mind that ActionScript treats the image as though it were a fully opaque rectangle (or bounding box). Alternatively, when performing pixel-level hit testing on images that are transparent, both of the images are required to be transparent. In addition to this, ActionScript uses the alpha threshold parameters to determine at what point the pixels change from being transparent to opaque.
  • 548. 548 Working with bitmaps The following example creates three bitmap images and checks for pixel collision using two different collision points (one returns false, the other true): import flash.display.Bitmap; import flash.display.BitmapData; import flash.geom.Point; var bmd1:BitmapData = new BitmapData(100, 100, false, 0x000000FF); var bmd2:BitmapData = new BitmapData(20, 20, false, 0x00FF3300); var bm1:Bitmap = new Bitmap(bmd1); this.addChild(bm1); // Create a red square. var redSquare1:Bitmap = new Bitmap(bmd2); this.addChild(redSquare1); redSquare1.x = 0; // Create a second red square. var redSquare2:Bitmap = new Bitmap(bmd2); this.addChild(redSquare2); redSquare2.x = 150; redSquare2.y = 150; // Define the point at the top-left corner of the bitmap. var pt1:Point = new Point(0, 0); // Define the point at the center of redSquare1. var pt2:Point = new Point(20, 20); // Define the point at the center of redSquare2. var pt3:Point = new Point(160, 160); trace(bmd1.hitTest(pt1, 0xFF, pt2)); // true trace(bmd1.hitTest(pt1, 0xFF, pt3)); // false
  • 549. Copying bitmap data 549 Copying bitmap data To copy bitmap data from one image to another, you can use several methods: clone(), copyPixels(), copyChannel(), and draw(). As its name suggests, the clone() method lets you clone, or sample, bitmap data from one BitmapData object to another. When called, the method returns a new BitmapData object that is an exact clone of the original instance it was copied from. The following example clones a copy of an orange (parent) square and places the clone beside the original parent square: import flash.display.Bitmap; import flash.display.BitmapData; var myParentSquareBitmap:BitmapData = new BitmapData(100, 100, false, 0x00ff3300); var myClonedChild:BitmapData = myParentSquareBitmap.clone(); var myParentSquareContainer:Bitmap = new Bitmap(myParentSquareBitmap); this.addChild(myParentSquareContainer); var myClonedChildContainer:Bitmap = new Bitmap(myClonedChild); this.addChild(myClonedChildContainer); myClonedChildContainer.x = 110; The copyPixels() method is a quick and easy way of copying pixels from one BitmapData object to another. The method takes a rectangular snapshot (defined by the sourceRect parameter) of the source image and copies it to another rectangular area (of equal size). The location of the newly “pasted” rectangle is defined within the destPoint parameter. The copyChannel() method samples a predefined color channel value (alpha, red, green, or blue) from a source BitmapData object and copies it into a channel of a destination BitmapData object. Calling this method does not affect the other channels in the destination BitmapData object. The draw() method draws, or renders, the graphical content from a source sprite, movie clip, or other display object on to a new bitmap. Using the matrix, colorTransform, blendMode, and destination clipRect parameters, you can modify the way in which the new bitmap is rendered. This method uses the Flash Player vector renderer to generate the data.
  • 550. 550 Working with bitmaps When you call draw(), you pass the source object (sprite, movie clip, or other display object) as the first parameter, as demonstrated here: myBitmap.draw(movieClip); If the source object has had any transformations (color, matrix, and so forth) applied to it after it was originally loaded, these transformations are not copied across to the new object. If you want to copy the transformations to the new bitmap, then you need to copy the value of the transform property from the original object to the transform property of the Bitmap object that uses the new BitmapData object. Making textures with noise functions To modify the appearance of a bitmap, you can apply a noise effect to it, using either the noise() method or the perlinNoise() methods. A noise effect can be likened to the static that appears on an untuned television screen. To apply a noise effect to a a bitmap, use the noise() method. This method applies a random color value to pixels within a specified area of a bitmap image. This method accepts five parameters: ■ randomSeed (int): The random seed number that determines the pattern. Despite its name, this number actually creates the same results if the same number is passed. In order to get a true random result, use the Math.random() method to pass a random number for this parameter. ■ low (uint): This parameter refers to the lowest value to be generated for each pixel (0 to 255). The default value is 0. Setting this value lower results in a darker noise pattern, while setting it to a higher value results in a brighter pattern. ■ high (uint): This parameter refers to the highest value to be generated for each pixel (0 to 255). The default value is 255. Setting this value lower results in a darker noise pattern, while setting it to a higher value results in a brighter pattern. ■ channelOptions (uint): This parameter specifies to which color channel of the bitmap object the noise pattern will be applied. The number can be a combination of any of the four color channel ARGB values. The default value is 7. ■ grayScale (Boolean): When set to true, this parameter applies the randomSeed value to the bitmap pixels, effectively washing all color out of the image. The alpha channel is not affected by this parameter. The default value is false.
  • 551. Making textures with noise functions 551 The following example creates a bitmap image and applies a blue noise pattern to it: import flash.display.Bitmap; import flash.display.BitmapData; var myBitmap:BitmapData = new BitmapData(250, 250,false, 0xff000000); myBitmap.noise(500, 0, 255, BitmapDataChannel.BLUE,false); var image:Bitmap = new Bitmap(myBitmap); addChild(image); If you want to create a more organic-looking texture, use the perlinNoise() method. The perlinNoise() method produces realistic, organic textures that are ideal for smoke, clouds, water, fire, or even explosions. Because it is generated by an algorithm, the perlinNoise() method uses less memory than bitmap-based textures. However, it can still have an impact on processor usage, slowing down your Flash content and causing the screen to be redrawn more slowly than the frame rate, especially on older computers. This is mainly due to the floating-point calculations that need to occur in order to process the perlin noise algorithms. The method accepts nine parameters (the first six are required): ■ baseX (Number): Determines the x (size) value of patterns created. ■ baseY (Number): Determines the y (size) value of the patterns created. ■ numOctaves (uint): Number of octaves or individual noise functions to combine to create this noise. Larger numbers of octaves create images with greater detail but also require more processing time. ■ randomSeed (int): The random seed number works exactly the same way as it does in the noise() function. To get a true random result, use the Math.random() method to pass a random number for this parameter. ■ stitch (Boolean): If set to true, this method attempts to stitch (or smooth) the transition edges of the image to create seamless textures for tiling as a bitmap fill. ■ fractalNoise (Boolean): This parameter relates to the edges of the gradients being generated by the method. If set to true, the method generates fractal noise that smooths the edges of the effect. If set to false, it generates turbulence. An image with turbulence has visible discontinuities in the gradient that can make it better approximate sharper visual effects, like flames and ocean waves. ■ channelOptions (uint): The channelOptions parameter works exactly the same way as it does in the noise() method. It specifies to which color channel (of the bitmap) the noise pattern is applied. The number can be a combination of any of the four color channel ARGB values. The default value is 7.
  • 552. 552 Working with bitmaps ■ grayScale (Boolean): The grayScale parameter works exactly the same way as it does in the noise() method. If set to true, it applies the randomSeed value to the bitmap pixels, effectively washing all color out of the image. The default value is false. ■ offsets (Array): An array of points that correspond to x and y offsets for each octave. By manipulating the offset values, you can smoothly scroll the layers of the image. Each point in the offset array affects a specific octave noise function. The default value is null. The following example creates a 150 x 150 pixel BitmapData object that calls the perlinNoise() method to generate a green and blue cloud effect: import flash.display.Bitmap; import flash.display.BitmapData; var myBitmapDataObject:BitmapData = new BitmapData(150, 150, false, 0x00FF0000); var seed:Number = Math.floor(Math.random() * 100); var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE myBitmapDataObject.perlinNoise(100, 80, 6, seed, false, true, channels, false, null); var myBitmap:Bitmap = new Bitmap(myBitmapDataObject); addChild(myBitmap); Scrolling bitmaps Imagine you have created a street mapping application where each time the user moves the map you are required to update the view (even if the map has been moved by just a few pixels). One way to create this functionality would be to re-render a new image containing the updated map view each time the user moves the map. Alternatively, you could create a large single image and the scroll() method. The scroll() method copies an on-screen bitmap and then pastes it to a new offset location—specified by (x, y) parameters. If a portion of the bitmap happens to reside off- stage, this gives the effect that the image has shifted. When combined with a timer function (or an enterFrame event), you can make the image appear to be animating or scrolling.
  • 553. Example: Animating sprites using an offscreen bitmap 553 The following example takes the previous perlin noise example and generates a larger bitmap image (three-fourths of which is rendered off-stage). The scroll() method is then applied, along with an enterFrame event listener that offsets the image by one pixel in a diagonally downward direction. This method is called each time the frame is entered and as a result, the offscreen portions of the image are rendered to the Stage as the image scrolls down. import flash.display.Bitmap; import flash.display.BitmapData; var myBitmapDataObject:BitmapData = new BitmapData(1000, 1000, false, 0x00FF0000); var seed:Number = Math.floor(Math.random() * 100); var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE; myBitmapDataObject.perlinNoise(100, 80, 6, seed, false, true, channels, false, null); var myBitmap:Bitmap = new Bitmap(myBitmapDataObject); myBitmap.x = -750; myBitmap.y = -750; addChild(myBitmap); addEventListener(Event.ENTER_FRAME, scrollBitmap); function scrollBitmap(event:Event):void { myBitmapDataObject.scroll(1, 1); } Example: Animating sprites using an offscreen bitmap Many Flash games involve hundreds of images animating on the screen at once. This bitmap animation example draws several hundred small bitmaps or sprites on to a big offscreen bitmap, then writes that one bitmap to the screen, which greatly speeds up the animation. For a description of this example and to download the source code, go to www.adobe.com/go/ learn_fl_bitmaps.
  • 554. 554 Working with bitmaps
  • 555. 555 19 CHAPTER 19 Working with video Flash video is one of the standout technologies on the Internet. However, the traditional presentation of video—in a rectangular screen with a progress bar and some control buttons underneath—is only one possible use of video in a Flash application. Through ActionScript, you have fine-tuned access to and control over video loading, presentation, and playback. Contents Basics of video . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .556 Understanding the Flash Video (FLV) format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559 Understanding the Video class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 Loading video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561 Controlling video playback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .562 Streaming video files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .564 Understanding cue points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .565 Writing callback methods for onCuePoint and onMetaData . . . . . . . . . . . . . . . . . .566 Using cue points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .572 Using video metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .573 Capturing camera input. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 Advanced topics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .585 Example: Video Jukebox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .587
  • 556. 556 Working with video Basics of video Introduction to working with video One important capability of Adobe Flash Player is the ability to display and manipulate video information with ActionScript in the same way that you can manipulate other visual content such as images, animation, text, and so on. When you create a Flash Video (FLV) file in Adobe Flash CS3 Professional, you have the option to select a skin for your video including common playback controls. However, there is no reason you need to limit yourself to the options available. Using ActionScript, you have fine-tuned control over loading, displaying, and controlling playback of video—meaning you could create your own video player skin, or use your video in any less traditional way that you want. Working with video in ActionScript involves working with a combination of several classes: ■ Video class: The actual video content box on the Stage is an instance of the Video class. The Video class is a display object, so it can be manipulated using the same techniques that can be applied to other display objects, such as positioning, applying transformations, applying filters and blending modes, and so forth. ■ NetStream class: When you’re loading a video file to be controlled by ActionScript, a NetStream instance is used to represent the source of the video content—in this case, a stream of video data. Using a NetStream instance also involves using a NetConnection object, which is the connection to the video file—like the tunnel that the video data is fed through. ■ Camera class: When you’re working with video data from a camera connected to the user’s computer, a Camera instance represents the source of the video content—the user’s camera and the video data it makes available. When you’re loading external video, you can load the file from a standard web server for progressive download playback, or you can work with streaming video delivered by a specialized server such as Adobe’s Macromedia® Flash® Media Server.
  • 557. Basics of video 557 Common video tasks This chapter describes the following video-related tasks that you will likely want to perform: ■ Displaying and controlling video on the screen ■ Loading external FLV files ■ Handling metadata and cue point information in a video file ■ Capturing and displaying video input from a user’s camera Important concepts and terms ■ Cue point: A marker that can be placed at a specific moment in time in a video file, for example to act like a bookmark for locating that point in time, or to provide additional data that is associated with that moment in time. ■ Encoding: The process of taking video data in one format and converting it to another video data format; for example, taking a high resolution source video and converting it to a format that’s suitable for Internet delivery. ■ Frame: A single segment of video information; each frame is like a still image representing a snapshot of a moment in time. By playing frames in sequence at high speed, the illusion of motion is created. ■ Keyframe: A video frame which contains the full information for the frame. Other frames that follow a keyframe only contain information about how they differ from the keyframe, rather than containing the full frame’s worth of information. ■ Metadata: Information about a video file that can be embedded within the video file and retrieved when the video has loaded. ■ Progressive download: When a video file is delivered from a standard web server, the video data is loaded using progressive download, meaning the video information loads in sequence. This has the benefit that the video can begin playing before the entire file is downloaded; however, it prevents you from jumping ahead to a part of the video that hasn’t loaded. ■ Streaming: As an alternative to progressive download, a special video server can be used to deliver video over the Internet using a technique known as streaming (sometimes called “true streaming”). With streaming, the viewer’s computer never downloads the entire video at one time. To speed up download times, at any moment the computer only needs a portion of the total video information. Because a special server controls the delivery of the video content, any part of the video can be accessed at any time, rather than needing to wait for it to download before accessing it.
  • 558. 558 Working with video Working through in-chapter examples As you’re working through the chapter, you may want to test some of the example code listings for yourself. Because this chapter is about working with video in ActionScript, many of the code listings in this chapter involve working with a video object, which could be one that has been created and placed on the Stage in Flash authoring, or one that’s created using ActionScript. Testing a sample will involve viewing the result in Flash Player to see the effects of the code on the video. Most of the example code listings manipulate a Video object without creating the object explicitly. To test these code listings in this chapter: 1. Create an empty Flash document. 2. Select a keyframe in the Timeline. 3. Open the Actions panel and copy the code listing into the Script pane. 4. If necessary, open the Library panel. 5. From the Library panel menu, choose New Video. 6. In the Video Properties dialog box, enter a name for the new video symbol, and choose Video (ActionScript-controlled) in the Type field. Click OK to create the Video symbol. 7. Drag an instance of your video symbol from the Library panel onto the Stage. 8. With the video instance selected, in the Property inspector, give it an instance name. The name should match the name used for the Video instance in the example code listing—for example, if the code listing manipulates a Video object named vid, you should name your Stage instance vid as well. 9. Run the program using Control > Test Movie. On the screen you will see the results of the code manipulating the video as specified in the code listing. Some example code listings in this chapter include a class definition in addition to the example code. In those listings, in addition to the previous steps, and before testing the SWF, you will need to create the class that’s used in the example. To create a class defined in an example code listing: 1. Make sure you have saved the FLA file that will be used for testing. 2. From the main menu choose File > New. 3. In the New Document dialog, in the Type section, choose ActionScript File. Click OK to create the new ActionScript file. 4. Copy the class definition code from the example into the ActionScript document.
  • 559. Understanding the Flash Video (FLV) format 559 5. From the main menu, choose File > Save. Save the file in the same directory as the Flash document. The file’s name should match the name of the class in the code listing. For instance, if the code listing defines a class named “VideoTest,” save the ActionScript file as “VideoTest.as”. 6. Return to the Flash document. 7. Run the program using Control > Test Movie. You will see the results of the example displayed on the screen. Other techniques for testing example code listings are explained in more detail in “Testing in- chapter example code listings” on page 63. Understanding the Flash Video (FLV) format The FLV file format contains encoded audio and video data for delivery by using Flash Player. For example, if you have a QuickTime or Windows Media video file, you use an encoder (such as Flash Video Encoder, or Sorenson™ Squeeze) to convert that file to an FLV file. You can create FLV files by importing video into the Flash authoring tool and exporting it as an FLV file. You can use the FLV Export plug-in to export FLV files from supported video- editing applications. Using external FLV files provides certain capabilities that are not available when you use imported video: ■ Longer video clips can be used in your Flash documents without slowing down playback. External FLV files play using cached memory, which means that large files are stored in small pieces and accessed dynamically, requiring less memory than embedded video files. ■ An external FLV file can have a different frame rate than the Flash document in which it plays. For example, you can set the Flash document frame rate to 30 frames per second (fps) and the video frame rate to 21 fps. This setting gives you better control of the video than embedded video, to ensure smooth video playback. It also allows you to play FLV files at different frame rates without the need to alter existing Flash content.
  • 560. 560 Working with video ■ With external FLV files, Flash document playback does not have to be interrupted while the video file is loading. Imported video files can sometimes interrupt document playback to perform certain functions, such as accessing a CD-ROM drive. FLV files can perform functions independently of the Flash document, which does not interrupt playback. ■ Captioning video content is easier with external FLV files because you can access the video’s metadata using event handlers. Understanding the Video class The Video class enables you to display live streaming video in an application without embedding it in your SWF file. You can capture and play live video using the Camera.getCamera() method. You can also use the Video class to play back FLV files over HTTP or from the local file system. There are several different ways to use Video in your projects: ■ Load an FLV dynamically using the NetConnection and NetStream classes and display the video in a Video object. ■ Capture input from the user’s camera. ■ Use the FLVPlayback component. Even though the Video class is in the flash.media package, it inherits from the flash.display.DisplayObject class, therefore all display object functionality (such as matrix transformations and filters) also applies to Video instances. For more information see “Manipulating display objects” on page 395, “Working with geometry” on page 431, and “Filtering display objects” on page 465. TIP To load FLV files from a web server, you might need to register the file extension and MIME type with your web server; check your web server documentation. The MIME type for FLV files is video/x-flv. For more information, see “About configuring FLV files for hosting on a server” on page 585. NOTE Instances of a Video object on the Stage are instances of the Video class.
  • 561. Loading video files 561 Loading video files Loading videos using the NetStream and NetConnection classes is a multistep process: 1. The first step is to create a NetConnection object. The NetConnection class lets you play streaming FLV files from either an HTTP address or a local drive by passing the value null to the connect() method, if you are connecting to a local FLV file that is not using a server such as Adobe's Flash Media Server 2 or Adobe Flex. var nc:NetConnection = new NetConnection(); nc.connect(null); 2. The second step is to create a NetStream object which takes a NetConnection object as a parameter and specify which FLV file you want to load. The following snippet connects a NetStream object to the specified NetConnection instance and loads an FLV named video.flv in the same directory as the SWF file: var ns:NetStream = new NetStream(nc); ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); ns.play("video.flv"); function asyncErrorHandler(event:AsyncErrorEvent):void { // ignore error } 3. The third step is to create a new Video object and attach the previously created NetStream object using the Video class’s attachNetStream() method. Then you can add the video object to the display list using the addChild() method, as seen in the following snippet: var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); After entering the previous code, Flash Player will attempt to load the video.flv video file in the same directory as your SWF file. TIP To load FLV files from a web server, you might need to register the file extension and MIME type with your web server; check your web server documentation. The MIME type for FLV files is video/x-flv. For more information, see “About configuring FLV files for hosting on a server” on page 585.
  • 562. 562 Working with video Controlling video playback The NetStream class offers four main methods for controlling video playback: pause(): Pauses playback of a video stream. If the video is already paused, calling this method does nothing. resume(): Resumes playback of a video stream that is paused. If the video is already playing, calling this method does nothing. seek(): Seeks the keyframe closest to the specified location (an offset, in seconds, from the beginning of the stream). togglePause(): Pauses or resumes playback of a stream. The following example demonstrates how to control a video using several different buttons. To run the following example, create a new document and add four button instances to your workspace (pauseBtn, playBtn, stopBtn, and togglePauseBtn): var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); ns.play("video.flv"); function asyncErrorHandler(event:AsyncErrorEvent):void { // ignore error } var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); pauseBtn.addEventListener(MouseEvent.CLICK, pauseHandler); playBtn.addEventListener(MouseEvent.CLICK, playHandler); stopBtn.addEventListener(MouseEvent.CLICK, stopHandler); togglePauseBtn.addEventListener(MouseEvent.CLICK, togglePauseHandler); function pauseHandler(event:MouseEvent):void { ns.pause(); } NOTE There is no stop() method. In order to stop a stream you must pause playback and seek to the beginning of the video stream. NOTE The play() method does not resume playback, it is used for loading video files.
  • 563. Controlling video playback 563 function playHandler(event:MouseEvent):void { ns.resume(); } function stopHandler(event:MouseEvent):void { // Pause the stream and move the playhead back to // the beginning of the stream. ns.pause(); ns.seek(0); } function togglePauseHandler(event:MouseEvent):void { ns.togglePause(); } Clicking on the pauseBtn button instance while the video is playing causes the video file to pause. If the video is already paused, clicking this button has no effect. Clicking on the playBtn button instance resumes video playback if playback was previously paused, otherwise the button has no effect if the video was already playing. Detecting the end of a video stream In order to listen for the beginning and end of a video stream, you need to add an event listener to the NetStream instance to listen for the netStatus event. The following code demonstrates how to listen for the various codes throughout the video’s playback: ns.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); function statusHandler(event:NetStatusEvent):void { trace(event.info.code) } The previous code generates the following output: NetStream.Play.Start NetStream.Buffer.Empty NetStream.Buffer.Full NetStream.Buffer.Empty NetStream.Buffer.Full NetStream.Buffer.Empty NetStream.Buffer.Full NetStream.Buffer.Flush NetStream.Play.Stop NetStream.Buffer.Empty NetStream.Buffer.Flush
  • 564. 564 Working with video The two codes that you want to specifically listen for are “NetStream.Play.Start” and “NetStream.Play.Stop” which signal the beginning and end of the video’s playback. The following snippet uses a switch statement to filter these two codes and trace a message: function statusHandler(event:NetStatusEvent):void { switch (event.info.code) { case "NetStream.Play.Start": trace("Start [" + ns.time.toFixed(3) + " seconds]"); break; case "NetStream.Play.Stop": trace("Stop [" + ns.time.toFixed(3) + " seconds]"); break; } } By listening for the netStatus event (NetStatusEvent.NET_STATUS), you can build a video player which loads the next video in a playlist once the current video has finished playing. Streaming video files To steam files from Flash Media Server, you can use the NetConnection and NetStream classes to connect to a remote server instance and play a specified stream. To specify a Real- Time Messaging Protocal (RTMP) server, you pass the desired RTMP URL, such as “rtmp:// localhost/appName/appInstance”, to the NetConnection.connect() method instead of passing null. To play a specific live or recorded stream from the specified Flash Media Server, you pass an identifying name for live data published by NetStream.publish(), or a recorded filename for playback to the NetStream.play() method. For more information, see the Flash Media Server documentation.
  • 565. Understanding cue points 565 Understanding cue points Not all FLV files contain cue points. Cue points are typically embedded in an FLV file during FLV encoding, although tools exist for embedding cue points in existing FLV files. You can use several different kinds of cue points with Flash Video. You can use ActionScript to interact with cue points that you embed in an FLV file (when you create the FLV file), or that you create by using ActionScript. ■ Navigation cue points: You embed navigation cue points in the FLV stream and FLV metadata packet when you encode the FLV file. You use navigation cue points to let users seek to a specified part of a file. ■ Event cue points: You embed event cue points in the FLV stream and FLV metadata packet when you encode the FLV file. You can write code to handle the events that are triggered at specified points during FLV playback. ■ ActionScript cue points: External cue points that you create by using ActionScript code. You can write code to trigger these cue points in relation to the video's playback. These cue points are less accurate than embedded cue points (up to a tenth of a second), because the video player tracks them separately. Navigation cue points create a keyframe at the specified cue point location, so you can use code to move a video player's playhead to that location. You can set particular points in an FLV file where you might want users to seek. For example, your video might have multiple chapters or segments, and you can control the video by embedding navigation cue points in the video file. If you plan to create an application in which you want users to navigate to a cue point, you should create and embed cue points when you encode the file instead of using ActionScript cue points. You should embed the cue points in the FLV file, because they are more accurate to work with. For more information on encoding FLV files with cue points, see “Embed cue points” in Using Flash. You can access cue point parameters by writing ActionScript. Cue point parameters are a part of the event object received from the onCuePoint callback handler. To trigger certain actions in your code when a video reaches a specific cue point, you use the NetStream.onCuePoint event handler. For more information, see “Writing callback methods for onCuePoint and onMetaData” on page 566.
  • 566. 566 Working with video Writing callback methods for onCuePoint and onMetaData You can trigger actions in your application when specific cue points are reached or specific metadata is received by the player. To trigger such actions, you use the onCuePoint and onMetaData event handlers. You must write callback methods for these handlers, or Flash Player may throw errors. For example, the following code plays an FLV file named video.flv in the same folder as your SWF document: var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler); ns.play("video.flv"); function asyncErrorHandler(event:AsyncErrorEvent):void { trace(event.text); } var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); The previous code loads a local FLV file named video.flv and listens for the asyncError (AsyncErrorEvent.ASYNC_ERROR) to be dispatched. This event is dispatched when an exception is thrown from native asynchronous code. In this case, it is dispatched when an FLV contains metadata or cue point information, and the appropriate listeners have not been defined. The previous code handles the asyncError event and ignores the error if you are not interested in the video file’s metadata or cue point information. If you had an FLV with metadata and several cue points, the following information would be traced: Error #2095: flash.net.NetStream was unable to invoke callback onMetaData. Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint. Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint. Error #2095: flash.net.NetStream was unable to invoke callback onCuePoint.
  • 567. Writing callback methods for onCuePoint and onMetaData 567 The error occurs because the NetStream object was unable to find an onMetaData or onCuePoint callback method. There are several ways to define these callback methods within your applications: ■ Set the NetStream object’s client property to an Object ■ Create a custom class and define methods to handle the callback methods ■ Extend the NetStream class and add methods to handle the callback methods ■ Extend the NetStream class and make it dynamic ■ Set the NetStream object’s client property to this Set the NetStream object’s client property to an Object By setting the client property to either an Object or a subclass of NetStream, you can reroute the onMetaData and onCuePoint callback methods or ignore them completely. The following example demonstrates how you can use an empty Object to ignore the callback methods without listening for the asyncError event: var nc:NetConnection = new NetConnection(); nc.connect(null); var customClient:Object = new Object(); var ns:NetStream = new NetStream(nc); ns.client = customClient; ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); If you wanted to listen for either the onMetaData or onCuePoint callback methods, you would need to define methods to handle those callback methods, as shown in the following snippet: var customClient:Object = new Object(); customClient.onMetaData = metaDataHandler; function metaDataHandler(infoObject:Object):void { trace("metadata"); }
  • 568. 568 Working with video The previous code listens for the onMetaData callback method and calls the metaDataHandler() method, which traces a string. If Flash Player encountered a cue point, no errors would be generated even though no onCuePoint callback method is defined. Create a custom class and define methods to handle the callback methods The following code sets the NetStream object’s client property to a custom class, CustomClient, which defines handlers for the callback methods: var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.client = new CustomClient(); ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); The CustomClient class is as follows: package { public class CustomClient { public function onMetaData(infoObject:Object):void { trace("metadata"); } } } The CustomClient class defines a handler for the onMetaData callback handler. If a cue point was encountered and the onCuePoint callback handler was called, an asyncError event (AsyncErrorEvent.ASYNC_ERROR) would be dispatched saying “flash.net.NetStream was unable to invoke callback onCuePoint.” To prevent this error, you would either need to define an onCuePoint callback method in your CustomClient class, or define an event handler for the asyncError event.
  • 569. Writing callback methods for onCuePoint and onMetaData 569 Extend the NetStream class and add methods to handle the callback methods The following code creates an instance of the CustomNetStream class, which is defined in a later code listing: var ns:CustomNetStream = new CustomNetStream(); ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); The following code listing defines the CustomNetStream class that extends the NetStream class, handles the creation of the necessary NetConnection object, and handles the onMetaData and onCuePoint callback handler methods: package { import flash.net.NetConnection; import flash.net.NetStream; public class CustomNetStream extends NetStream { private var nc:NetConnection; public function CustomNetStream() { nc = new NetConnection(); nc.connect(null); super(nc); } public function onMetaData(infoObject:Object):void { trace("metadata"); } public function onCuePoint(infoObject:Object):void { trace("cue point"); } } }
  • 570. 570 Working with video If you want to rename the onMetaData() and onCuePoint() methods in the CustomNetStream class, you could use the following code: package { import flash.net.NetConnection; import flash.net.NetStream; public class CustomNetStream extends NetStream { private var nc:NetConnection; public var onMetaData:Function; public var onCuePoint:Function; public function CustomNetStream() { onMetaData = metaDataHandler; onCuePoint = cuePointHandler; nc = new NetConnection(); nc.connect(null); super(nc); } private function metaDataHandler(infoObject:Object):void { trace("metadata"); } private function cuePointHandler(infoObject:Object):void { trace("cue point"); } } } Extend the NetStream class and make it dynamic You can extend the NetStream class and make the subclass dynamic so that onCuePoint and onMetaData callback handlers can be added dynamically. This is demonstrated in the following listing: var ns:DynamicCustomNetStream = new DynamicCustomNetStream(); ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid);
  • 571. Writing callback methods for onCuePoint and onMetaData 571 The DynamicCustomNetStream class is as follows: package { import flash.net.NetConnection; import flash.net.NetStream; public dynamic class DynamicCustomNetStream extends NetStream { private var nc:NetConnection; public function DynamicCustomNetStream() { nc = new NetConnection(); nc.connect(null); super(nc); } } } Even with no handlers for the onMetaData and onCuePoint callback handlers, no errors are thrown since the DynamicCustomNetStream class is dynamic. If you want to define methods for the onMetaData and onCuePoint callback handlers, you could use the following code: var ns:DynamicCustomNetStream = new DynamicCustomNetStream(); ns.onMetaData = metaDataHandler; ns.onCuePoint = cuePointHandler; ns.play("http://www.helpexamples.com/flash/video/cuepoints.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); function metaDataHandler(infoObject:Object):void { trace("metadata"); } function cuePointHandler(infoObject:Object):void { trace("cue point"); }
  • 572. 572 Working with video Set the NetStream object’s client property to this By setting the client property to this, Flash Player looks in the current scope for onMetaData() and onCuePoint() methods. You can see this in the following example: var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.client = this; ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); If the onMetaData or onCuePoint callback handlers are called and no methods exist to handle the callback, no errors are generated. To handle these callback handlers, create an onMetaData() and onCuePoint() method in your code, as seen in the following snippet: function onMetaData(infoObject:Object):void { trace("metadata"); } function onCuePoint(infoObject:Object):void { trace("cue point"); } Using cue points The following example uses a simple for..in loop to iterate over each of the properties in the onCuePoint callback handler’s infoObject parameter and trace a message when cue point data is received: var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.client = this; ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid);
  • 573. Using video metadata 573 function onCuePoint(infoObject:Object):void { var key:String; for (key in infoObject) { trace(key + ": " + infoObject[key]); } } The following output appears: parameters: name: point1 time: 0.418 type: navigation This code used one of several techniques to set the object on which the callback method was invoked. You can use other techniques; for more information, see Writing callback methods for onCuePoint and onMetaData. Using video metadata You can use the onMetaData callback handler to view the metadata information in your FLV file. Metadata includes information about your FLV file, such as duration, width, height, and frame rate. The metadata information that is added to your FLV file depends on the software you use to encode your FLV file or the software you use to add metadata information. var nc:NetConnection = new NetConnection(); nc.connect(null); var ns:NetStream = new NetStream(nc); ns.client = this; ns.play("video.flv"); var vid:Video = new Video(); vid.attachNetStream(ns); addChild(vid); function onMetaData(infoObject:Object):void { var key:String; for (key in infoObject) { trace(key + ": " + infoObject[key]); } }
  • 574. 574 Working with video The previous code generates code similar to the following, assuming your FLV file contains cue points and audio: width: 320 audiodelay: 0.038 canSeekToEnd: true height: 213 cuePoints: ,, audiodatarate: 96 duration: 16.334 videodatarate: 400 framerate: 15 videocodecid: 4 audiocodecid: 2 In the previous code, the cue point information was not displaying. In order to display the cue point metadata, you can use the following function which recursively displays the items in an Object: function traceObject(obj:Object, indent:uint = 0):void { var indentString:String = ""; var i:uint; var prop:String; var val:*; for (i = 0; i < indent; i++) { indentString += "t"; } for (prop in obj) { val = obj[prop]; if (typeof(val) == "object") { trace(indentString + " " + j + ": [Object]"); traceObject(val, indent + 1); } else { trace(indentString + " " + prop + ": " + val); } } } TIP If your video does not have audio, the audio-related metadata information (such as audiodat