diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e52a704
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+.vscode/
+asconfig.json
+.code-workspace
+
+.actionScriptProperties
+.flexProperties
+.flexLibProperties
+.metadata/
+.settings/
+.project
+
+*.iml
+.idea/
+
+.as3proj
+
+.DS_Store
+Thumbs.db
+
+.swf
+.swc
+.swz
\ No newline at end of file
diff --git a/ASCIIArt/AsciiArtApp.fla b/ASCIIArt/AsciiArtApp.fla
new file mode 100644
index 0000000..71e8405
Binary files /dev/null and b/ASCIIArt/AsciiArtApp.fla differ
diff --git a/ASCIIArt/AsciiArtApp.mxml b/ASCIIArt/AsciiArtApp.mxml
new file mode 100644
index 0000000..8429f6d
--- /dev/null
+++ b/ASCIIArt/AsciiArtApp.mxml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ASCIIArt/com/example/programmingas3/asciiArt/AsciiArtBuilder.as b/ASCIIArt/com/example/programmingas3/asciiArt/AsciiArtBuilder.as
new file mode 100644
index 0000000..5c9b641
--- /dev/null
+++ b/ASCIIArt/com/example/programmingas3/asciiArt/AsciiArtBuilder.as
@@ -0,0 +1,226 @@
+package com.example.programmingas3.asciiArt
+{
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.net.URLLoader;
+ import flash.net.URLRequest;
+ import com.example.programmingas3.asciiArt.BitmapToAsciiConverter;
+ import com.example.programmingas3.asciiArt.Image;
+ import com.example.programmingas3.asciiArt.ImageInfo;
+
+ /**
+ * Provides appication-level functionality for the AsciiArt sample.
+ */
+ public class AsciiArtBuilder extends EventDispatcher
+ {
+ // ------- Private vars -------
+
+ private const DATA_TARGET:String = "txt/ImageData.txt";
+ private var _imageInfoLoader:URLLoader;
+ private var _imageStack:Array;
+ private var _currentImageIndex:uint;
+
+
+ // ------- Constructor -------
+
+ public function AsciiArtBuilder()
+ {
+ _imageStack = new Array();
+ var request:URLRequest = new URLRequest(DATA_TARGET);
+ _imageInfoLoader = new URLLoader();
+ _imageInfoLoader.addEventListener(Event.COMPLETE, imageInfoCompleteHandler);
+ _imageInfoLoader.load(request);
+ }
+
+
+ // ------- Public Properties -------
+
+ public static const IMAGE_PATH:String = "image/";
+
+ public var asciiArtText:String = "";
+
+ public function get currentImage():Image
+ {
+ return _imageStack[_currentImageIndex];
+ }
+
+
+ // ------- Event Handlers -------
+
+ /**
+ * Called when the image info text file is loaded.
+ */
+ private function imageInfoCompleteHandler(event:Event):void
+ {
+ var allImageInfo:Array = parseImageInfo();
+
+ buildImageStack(allImageInfo);
+ }
+
+
+ /**
+ * Called when the first image is loaded.
+ */
+ private function imageCompleteHandler(event:Event):void
+ {
+ // move to the first image in the stack
+ next();
+ // notify any listeners that the application has finished its initial loading
+ var readyEvent:Event = new Event("ready");
+ dispatchEvent(readyEvent);
+ }
+
+
+
+ // ------- Public Methods -------
+
+ /**
+ * Advances the image stack to the next image, and populates the asciiArtText property
+ * with that image's ASCII Art representation.
+ */
+ public function next():void
+ {
+ // Advance the "current image" index (or set it back to 0 if we're on the last one)
+ _currentImageIndex++;
+ if (_currentImageIndex == _imageStack.length)
+ {
+ _currentImageIndex = 0;
+ }
+ // generate the ASCII version of the new "current" image
+ var imageConverter:BitmapToAsciiConverter = new BitmapToAsciiConverter(this.currentImage);
+ this.asciiArtText = imageConverter.parseBitmapData();
+ }
+
+
+ // ------- Private Methods -------
+
+ /**
+ * Parses the contents of the loaded text file which contains information about the images
+ * to load, and creates an Array of ImageInfo instances from that data.
+ *
+ * @return An Array of ImageInfo instances.
+ */
+ private function parseImageInfo():Array
+ {
+ var result:Array = new Array();
+
+ /* Parse the contents of the text file, and put its contents into an Array of ImageInfo
+ * instances.
+ * Each line of text contains info about one image, separated by tab (\t) characters,
+ * in this order:
+ * - file name
+ * - title
+ * - white threshold
+ * - black threshold
+ *
+ * Loop through the individual lines in the text file.
+ * Note that we skip the first line, since it only contains column headers.
+ */
+ 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];
+ // trim leading or trailing white space from the current line
+ imageInfoRaw = imageInfoRaw.replace(/^ *(.*) *$/, "$1");
+ 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);
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Capitalizes the first letter of each word in a String, unless the word
+ * is one of the English words which are not commonly capitalized
+ *
+ * @param str The String to "normalize"
+ * @return The String with the words capitalized
+ */
+ 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(" ");
+ }
+
+
+
+ /**
+ * Capitalizes the first letter of a single word, unless it's one of
+ * a set of words which are normally not capitalized in English.
+ *
+ * @param word The word to capitalize
+ * @return The capitalized word
+ */
+ private function capitalizeFirstLetter(word:String):String
+ {
+ switch (word)
+ {
+ 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;
+ }
+
+
+ /**
+ * Using an Array of ImageInfo instances, populates the image stack with Image instances.
+ *
+ * @param imageInfo An array of ImageInfo instances, containing the data about the
+ * image files to be loaded into the image stack.
+ */
+ private function buildImageStack(imageInfo:Array):void
+ {
+ var image:Image;
+ var oneImageInfo:ImageInfo;
+ var listenerAdded:Boolean = false;
+ var numImages:uint = imageInfo.length;
+ for (var i:uint = 0; i < numImages; i++)
+ {
+ _currentImageIndex = 0;
+ oneImageInfo = imageInfo[i];
+ image = new Image(oneImageInfo);
+ _imageStack.push(image);
+ if(!listenerAdded)
+ {
+ image.addEventListener(Event.COMPLETE, imageCompleteHandler);
+ listenerAdded = true;
+ }
+ image.load();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ASCIIArt/com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as b/ASCIIArt/com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as
new file mode 100644
index 0000000..056533a
--- /dev/null
+++ b/ASCIIArt/com/example/programmingas3/asciiArt/BitmapToAsciiConverter.as
@@ -0,0 +1,119 @@
+package com.example.programmingas3.asciiArt
+{
+ import flash.display.BitmapData;
+
+ /*
+ * Provides functionality for converting a bitmap image to an "ASCII Art" representation
+ * of that image.
+ */
+ public class BitmapToAsciiConverter
+ {
+ // ------- Private vars -------
+
+ private static const _resolution:Number = .025;
+ private var _data:BitmapData;
+ private var _whiteThreshold:Number;
+ private var _blackThreshold:Number;
+
+ /* The characters in this string become increasingly darker.
+ * This set of characters are the "grayscale values" which are used to create the image.
+ * There are 64 characters, meaning each character is used to represent four values in a common
+ * 256-value grayscale color palette (which has color values in the 0-255 range).
+ * The characters are in order from darkest to lightest, so that their position (index)
+ * in the string corresponds to a relative color value (with 0 = black).
+ */
+ private static const palette:String = "@#$%&8BMW*mwqpdbkhaoQ0OZXYUJCLtfjzxnuvcr[]{}1()|/?Il!i><+_~-;,. ";
+
+
+ // ------- Constructor -------
+
+ public function BitmapToAsciiConverter(image:Image)
+ {
+ this._data = image.getBitmapData();
+ this._whiteThreshold = image.info.whiteThreshold;
+ this._blackThreshold = image.info.blackThreshold;
+ }
+
+
+ // ------- Public Methods -------
+
+ /*
+ * Parses the bitmap data associated with this instance and converts it to its "ASCII Art"
+ * (String) representation.
+ * @return The String representation of the bitmap image.
+ */
+ public function parseBitmapData():String
+ {
+ var rgbVal:uint;
+ var redVal:uint;
+ var greenVal:uint;
+ var blueVal:uint;
+ var grayVal:uint;
+ var index:uint;
+
+ /*
+ * Determine the "resolution" (dimensions) of the resulting "image" (the number
+ * of rows of text, and the number of characters per row):
+ */
+ var verticalResolution:uint = Math.floor(_data.height * _resolution);
+ /*
+ * Since the "pixels" (characters) aren't square, multiply by 0.45 to maintain the
+ * scale of the original image:
+ */
+ var horizontalResolution:uint = Math.floor(_data.width * _resolution * 0.45);
+
+ var result:String = "";
+
+ // Loop through the rows of pixels top to bottom:
+ for (var y:uint = 0; y < _data.height; y += verticalResolution)
+ {
+ // Eithin each row, loop through the individual pixels left to right:
+ for (var x:uint = 0; x < _data.width; x += horizontalResolution)
+ {
+ // Extract individual red, green, and blue values for the pixel:
+ rgbVal = _data.getPixel(x, y);
+ redVal = (rgbVal & 0xFF0000) >> 16;
+ greenVal = (rgbVal & 0x00FF00) >> 8;
+ blueVal = rgbVal & 0x0000FF;
+
+ /*
+ * Calculate the gray value of the pixel.
+ * The formula for grayscale conversion: (Y = gray): Y = 0.3*R + 0.59*G + 0.11*B
+ */
+ grayVal = Math.floor(0.3 * redVal + 0.59 * greenVal + 0.11 * blueVal);
+
+ /*
+ * The white threshold and black threshold values (read from the "images.txt" file)
+ * determine the grayscale values that are the cut-off limits for white and black.
+ * Values outside the threshold will be "rounded" to pure white or black.
+ */
+ if (grayVal > _whiteThreshold)
+ {
+ grayVal = 0xFF;
+ }
+ else if (grayVal < _blackThreshold)
+ {
+ grayVal = 0x00;
+ }
+ else
+ {
+ /* Normalize the grayscale value along the relative scale between the white threshold
+ * and the black threshold. In other words, adjust the palette so that
+ * the black threshold acts as the "0x00" value and the white threshold acts as the "0xFF" value,
+ * then determine where the gray value falls along that scale:
+ */
+ grayVal = Math.floor(0xFF * ((grayVal - _blackThreshold) / (_whiteThreshold - _blackThreshold)));
+ }
+ /* The value is now a gray value in the 0-255 range, which needs to be converted
+ * to a value in the 0-64 range (since that's the number of available "shades of gray"
+ * in the set of available characters):
+ */
+ index = Math.floor(grayVal / 4);
+ result += palette.charAt(index);
+ }
+ result += "\n";
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ASCIIArt/com/example/programmingas3/asciiArt/Image.as b/ASCIIArt/com/example/programmingas3/asciiArt/Image.as
new file mode 100644
index 0000000..60e9daa
--- /dev/null
+++ b/ASCIIArt/com/example/programmingas3/asciiArt/Image.as
@@ -0,0 +1,66 @@
+package com.example.programmingas3.asciiArt
+{
+ import flash.events.Event;
+ import flash.display.Loader;
+ import flash.net.URLRequest;
+ import flash.display.Bitmap;
+ import flash.display.BitmapData;
+ import flash.events.EventDispatcher;
+ import com.example.programmingas3.asciiArt.AsciiArtBuilder;
+
+ /**
+ * Represents a loadable image and metadata about that image.
+ */
+ public class Image extends EventDispatcher
+ {
+ // ------- Private vars -------
+
+ private var _loader:Loader;
+
+
+ //
+ // ------- Constructor -------
+ //
+ public function Image(imageInfo:ImageInfo)
+ {
+ this.info = imageInfo;
+ _loader = new Loader();
+ _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
+ }
+
+
+ // ------- Public Properties -------
+
+ public var info:ImageInfo;
+
+
+ // ------- Public Methods -------
+
+ public function getBitmapData():BitmapData
+ {
+ return Bitmap(_loader.content).bitmapData;
+ }
+
+
+ /**
+ * Loads the image file associated with this instance.
+ */
+ public function load():void
+ {
+ var request:URLRequest = new URLRequest(AsciiArtBuilder.IMAGE_PATH + info.fileName);
+ _loader.load(request);
+ }
+
+
+ // ------- Event Handling -------
+
+ /**
+ * Called when the image associated with this instance has completely loaded. In essence
+ * it passes the event along to any listeners which have subscribed with this instance.
+ */
+ private function completeHandler(event:Event):void
+ {
+ dispatchEvent(event);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ASCIIArt/com/example/programmingas3/asciiArt/ImageInfo.as b/ASCIIArt/com/example/programmingas3/asciiArt/ImageInfo.as
new file mode 100644
index 0000000..2257212
--- /dev/null
+++ b/ASCIIArt/com/example/programmingas3/asciiArt/ImageInfo.as
@@ -0,0 +1,23 @@
+package com.example.programmingas3.asciiArt
+{
+
+ /**
+ * Represents metadata about a single image.
+ */
+ public class ImageInfo
+ {
+ // ------- Constructor -------
+
+ public function ImageInfo()
+ {
+ }
+
+
+ // ------- Public Properties -------
+
+ public var fileName:String;
+ public var title:String;
+ public var whiteThreshold:uint;
+ public var blackThreshold:uint;
+ }
+}
\ No newline at end of file
diff --git a/ASCIIArt/image/Apple.jpg b/ASCIIArt/image/Apple.jpg
new file mode 100644
index 0000000..f2e510b
Binary files /dev/null and b/ASCIIArt/image/Apple.jpg differ
diff --git a/ASCIIArt/image/Banana.jpg b/ASCIIArt/image/Banana.jpg
new file mode 100644
index 0000000..f15fd7e
Binary files /dev/null and b/ASCIIArt/image/Banana.jpg differ
diff --git a/ASCIIArt/image/FruitBasket.jpg b/ASCIIArt/image/FruitBasket.jpg
new file mode 100644
index 0000000..5abd929
Binary files /dev/null and b/ASCIIArt/image/FruitBasket.jpg differ
diff --git a/ASCIIArt/image/Orange.jpg b/ASCIIArt/image/Orange.jpg
new file mode 100644
index 0000000..3dfe9c3
Binary files /dev/null and b/ASCIIArt/image/Orange.jpg differ
diff --git a/ASCIIArt/image/Pear.jpg b/ASCIIArt/image/Pear.jpg
new file mode 100644
index 0000000..c9f5cdf
Binary files /dev/null and b/ASCIIArt/image/Pear.jpg differ
diff --git a/ASCIIArt/txt/ImageData.txt b/ASCIIArt/txt/ImageData.txt
new file mode 100644
index 0000000..acac96b
--- /dev/null
+++ b/ASCIIArt/txt/ImageData.txt
@@ -0,0 +1,6 @@
+FILENAME TITLE WHITE_THRESHHOLD BLACK_THRESHHOLD
+FruitBasket.jpg Pear, apple, orange, and banana d8 10
+Banana.jpg A picture of a banana C8 20
+Orange.jpg orange FF 20
+Apple.jpg picture of an apple 6E 10
+Pear.jpg pear 0xBB 0x30
\ No newline at end of file
diff --git a/AlarmClock/flash pro version/AlarmClockApp.fla b/AlarmClock/flash pro version/AlarmClockApp.fla
new file mode 100644
index 0000000..a146563
Binary files /dev/null and b/AlarmClock/flash pro version/AlarmClockApp.fla differ
diff --git a/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmClock.as b/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmClock.as
new file mode 100644
index 0000000..191d319
--- /dev/null
+++ b/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmClock.as
@@ -0,0 +1,76 @@
+package com.example.programmingas3.clock
+{
+ public class AlarmClock extends SimpleClock
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ public var alarmTime:Date;
+
+ public var alarmMessage:String;
+
+ /**
+ * The Timer that will be used for the alarm.
+ */
+ public var alarmTimer:Timer;
+
+ public static var MILLISECONDS_PER_DAY:Number = 1000 * 60 * 60 * 24;
+
+ /**
+ * 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);
+ }
+
+
+ /**
+ * 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);
+ }
+
+ // reset 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();
+
+ return alarmTime;
+ }
+
+
+ /**
+ * Called when the Timer event is received
+ */
+ public function onAlarm(event:TimerEvent):void
+ {
+ trace("Alarm!");
+ var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage);
+ this.dispatchEvent(alarm);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmEvent.as b/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmEvent.as
new file mode 100644
index 0000000..3b3347a
--- /dev/null
+++ b/AlarmClock/flash pro version/com/example/programmingas3/clock/AlarmEvent.as
@@ -0,0 +1,53 @@
+package com.example.programmingas3.clock
+{
+ 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;
+
+ /**
+ * Constructor.
+ * @param message The text to display when the alarm goes off.
+ */
+ public function AlarmEvent(message:String = "ALARM!")
+ {
+ super(ALARM);
+
+ this.message = message;
+ }
+
+
+ /**
+ * 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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flash pro version/com/example/programmingas3/clock/AnalogClockFace.as b/AlarmClock/flash pro version/com/example/programmingas3/clock/AnalogClockFace.as
new file mode 100644
index 0000000..0618317
--- /dev/null
+++ b/AlarmClock/flash pro version/com/example/programmingas3/clock/AnalogClockFace.as
@@ -0,0 +1,212 @@
+package com.example.programmingas3.clock
+{
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.text.StaticText;
+ import flash.events.*;
+ import flash.text.TextField;
+ import flash.text.TextFormat;
+
+ import flash.display.Sprite;
+
+ /**
+ * Displays a round clock face with an hour hand, a minute hand, and a second hand.
+ */
+ public class AnalogClockFace extends Sprite
+ {
+ /**
+ * The desired width of this component, as opposed to the .width
+ * property which represents tha actual width.
+ */
+ public var w:uint = 200;
+
+ /**
+ * The desired height of this component, as opposed to the .height
+ * property which represents tha actual height.
+ */
+ public var h:uint = 200;
+
+ /**
+ * The radius from the center of the clock to the
+ * outer edge of the circular face outline.
+ */
+ public var radius:uint;
+
+ /**
+ * The coordinates of the center of the face.
+ */
+ public var centerX:int;
+ public var centerY:int;
+
+ /**
+ * The three hands of the clock.
+ */
+ public var hourHand:Shape;
+ public var minuteHand:Shape;
+ public var secondHand:Shape;
+
+ /**
+ * The colors of the background and each hand.
+ * These could be set using parameters or
+ * styles in the future.
+ */
+ public var bgColor:uint = 0xEEEEFF;
+ public var hourHandColor:uint = 0x003366;
+ public var minuteHandColor:uint = 0x000099;
+ public var secondHandColor:uint = 0xCC0033;
+
+ /**
+ * Stores a snapshot of the current time, so it
+ * doesn't change while in the middle of drawing the
+ * three hands.
+ */
+ public var currentTime:Date;
+
+ /**
+ * Contructs a new clock face. The width and
+ * height will be equal.
+ */
+ public function AnalogClockFace(w:uint)
+ {
+ this.w = w;
+ this.h = w;
+
+ // Rounds to the nearest pixel
+ this.radius = Math.round(this.w / 2);
+
+ // The face is always square now, so the
+ // distance to the center is the same
+ // horizontally and vertically
+ this.centerX = this.radius;
+ this.centerY = this.radius;
+ }
+
+ /**
+ * Creates the outline, hour labels, and clock hands.
+ */
+ public function init():void
+ {
+ // draws the circular clock outline
+ drawBorder();
+
+ // draws the hour numbers
+ drawLabels();
+
+ // creates the three clock hands
+ createHands();
+ }
+
+ /**
+ * Draws a circular border.
+ */
+ public function drawBorder():void
+ {
+ graphics.lineStyle(0.5, 0x999999);
+ graphics.beginFill(bgColor);
+ graphics.drawCircle(centerX, centerY, radius);
+ graphics.endFill();
+ }
+
+ /**
+ * Puts numeric labels at the hour points.
+ */
+ public function drawLabels():void
+ {
+ for (var i:Number = 1; i <= 12; i++)
+ {
+ // Creates a new TextField showing the hour number
+ var label:TextField = new TextField();
+ label.text = i.toString();
+
+ // Places hour labels around the clock face.
+ // The sin() and cos() functions both operate on angles in radians.
+ var angleInRadians:Number = i * 30 * (Math.PI/180)
+
+ // Place the label using the sin() and cos() functions to get the x,y coordinates.
+ // The multiplier value of 0.9 puts the labels inside the outline of the clock face.
+ // The integer value at the end of the equation adjusts the label position,
+ // since the x,y coordinate is in the upper left corner.
+ label.x = centerX + (0.9 * radius * Math.sin( angleInRadians )) - 5;
+ label.y = centerY - (0.9 * radius * Math.cos( angleInRadians )) - 9;
+
+ // Formats the label text.
+ var tf:TextFormat = new TextFormat();
+ tf.font = "Arial";
+ tf.bold = "true";
+ tf.size = 12;
+ label.setTextFormat(tf);
+
+ // Adds the label to the clock face display list.
+ addChild(label);
+ }
+ }
+
+ /**
+ * Creates hour, minute, and second hands using the 2D drawing API.
+ */
+ public function createHands():void
+ {
+ // Uses a Shape since it's the simplest component that supports
+ // the 2D drawing API.
+ var hourHandShape:Shape = new Shape();
+ drawHand(hourHandShape, Math.round(radius * 0.5), hourHandColor, 3.0);
+ this.hourHand = Shape(addChild(hourHandShape));
+ this.hourHand.x = centerX;
+ this.hourHand.y = centerY;
+
+ var minuteHandShape:Shape = new Shape();
+ drawHand(minuteHandShape, Math.round(radius * 0.8), minuteHandColor, 2.0);
+ this.minuteHand = Shape(addChild(minuteHandShape));
+ this.minuteHand.x = centerX;
+ this.minuteHand.y = centerY;
+
+ var secondHandShape:Shape = new Shape();
+ drawHand(secondHandShape, Math.round(radius * 0.9), secondHandColor, 0.5);
+ this.secondHand = Shape(addChild(secondHandShape));
+ this.secondHand.x = centerX;
+ this.secondHand.y = centerY;
+ }
+
+ /**
+ * Draws a clock hand with a given size, color, and thickness.
+ */
+ public function drawHand(hand:Shape, distance:uint, color:uint, thickness:Number):void
+ {
+ hand.graphics.lineStyle(thickness, color);
+ hand.graphics.moveTo(0, distance);
+ hand.graphics.lineTo(0, 0);
+ }
+
+ /**
+ * Called by the parent container when the display is being drawn.
+ */
+ public function draw():void
+ {
+ // Stores the current date and time in an instance variable
+ currentTime = new Date();
+ showTime(currentTime);
+ }
+
+ /**
+ * 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();
+
+ // Multiplies by 6 to get degrees
+ this.secondHand.rotation = 180 + (seconds * 6);
+ this.minuteHand.rotation = 180 + (minutes * 6);
+
+ // Multiplies by 30 to get basic degrees, and then
+ // adds up to 29.5 degrees (59 * 0.5) to account
+ // for the minutes
+ this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flash pro version/com/example/programmingas3/clock/SimpleClock.as b/AlarmClock/flash pro version/com/example/programmingas3/clock/SimpleClock.as
new file mode 100644
index 0000000..e23d52f
--- /dev/null
+++ b/AlarmClock/flash pro version/com/example/programmingas3/clock/SimpleClock.as
@@ -0,0 +1,66 @@
+package com.example.programmingas3.clock {
+ import flash.display.Sprite;
+
+ public class SimpleClock extends Sprite
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ /**
+ * The time display component.
+ */
+ public var face:AnalogClockFace;
+
+ /**
+ * The Timer that acts like a heartbeat for the application.
+ */
+ public var ticker:Timer;
+
+ public static const millisecondsPerMinute:int = 1000 * 60;
+ public static const millisecondsPerHour:int = 1000 * 60 * 60;
+ public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+
+ /**
+ * Sets up a SimpleClock instance.
+ */
+ public function initClock(faceSize:Number = 200):void
+ {
+ // sets the invoice date to today’s date
+ var invoiceDate:Date = new Date();
+
+ // adds 30 days to get the due date
+ var millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+ var dueDate:Date = new Date(invoiceDate.getTime() + (30 * millisecondsPerDay));
+
+ var oneHourFromNow:Date = new Date(); // starts at the current time
+ oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour);
+
+ // 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();
+
+ // 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();
+ }
+
+ /**
+ * Called once per second when the Timer event is received.
+ */
+ public function onTick(evt:TimerEvent):void
+ {
+ // Updates the clock display.
+ face.draw();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flex_flashbuilder version/AlarmClockApp.mxml b/AlarmClock/flex_flashbuilder version/AlarmClockApp.mxml
new file mode 100644
index 0000000..24de385
--- /dev/null
+++ b/AlarmClock/flex_flashbuilder version/AlarmClockApp.mxml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmClock.as b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmClock.as
new file mode 100644
index 0000000..191d319
--- /dev/null
+++ b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmClock.as
@@ -0,0 +1,76 @@
+package com.example.programmingas3.clock
+{
+ public class AlarmClock extends SimpleClock
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ public var alarmTime:Date;
+
+ public var alarmMessage:String;
+
+ /**
+ * The Timer that will be used for the alarm.
+ */
+ public var alarmTimer:Timer;
+
+ public static var MILLISECONDS_PER_DAY:Number = 1000 * 60 * 60 * 24;
+
+ /**
+ * 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);
+ }
+
+
+ /**
+ * 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);
+ }
+
+ // reset 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();
+
+ return alarmTime;
+ }
+
+
+ /**
+ * Called when the Timer event is received
+ */
+ public function onAlarm(event:TimerEvent):void
+ {
+ trace("Alarm!");
+ var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage);
+ this.dispatchEvent(alarm);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmEvent.as b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmEvent.as
new file mode 100644
index 0000000..3b3347a
--- /dev/null
+++ b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AlarmEvent.as
@@ -0,0 +1,53 @@
+package com.example.programmingas3.clock
+{
+ 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;
+
+ /**
+ * Constructor.
+ * @param message The text to display when the alarm goes off.
+ */
+ public function AlarmEvent(message:String = "ALARM!")
+ {
+ super(ALARM);
+
+ this.message = message;
+ }
+
+
+ /**
+ * 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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AnalogClockFace.as b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AnalogClockFace.as
new file mode 100644
index 0000000..4594f81
--- /dev/null
+++ b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/AnalogClockFace.as
@@ -0,0 +1,212 @@
+package com.example.programmingas3.clock
+{
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.text.StaticText;
+ import flash.events.*;
+ import flash.text.TextField;
+ import flash.text.TextFormat;
+
+ import mx.core.UIComponent;
+
+ /**
+ * Displays a round clock face with an hour hand, a minute hand, and a second hand.
+ */
+ public class AnalogClockFace extends UIComponent
+ {
+ /**
+ * The desired width of this component, as opposed to the .width
+ * property which represents tha actual width.
+ */
+ public var w:uint = 200;
+
+ /**
+ * The desired height of this component, as opposed to the .height
+ * property which represents tha actual height.
+ */
+ public var h:uint = 200;
+
+ /**
+ * The radius from the center of the clock to the
+ * outer edge of the circular face outline.
+ */
+ public var radius:uint;
+
+ /**
+ * The coordinates of the center of the face.
+ */
+ public var centerX:int;
+ public var centerY:int;
+
+ /**
+ * The three hands of the clock.
+ */
+ public var hourHand:Shape;
+ public var minuteHand:Shape;
+ public var secondHand:Shape;
+
+ /**
+ * The colors of the background and each hand.
+ * These could be set using parameters or
+ * styles in the future.
+ */
+ public var bgColor:uint = 0xEEEEFF;
+ public var hourHandColor:uint = 0x003366;
+ public var minuteHandColor:uint = 0x000099;
+ public var secondHandColor:uint = 0xCC0033;
+
+ /**
+ * Stores a snapshot of the current time, so it
+ * doesn't change while in the middle of drawing the
+ * three hands.
+ */
+ public var currentTime:Date;
+
+ /**
+ * Contructs a new clock face. The width and
+ * height will be equal.
+ */
+ public function AnalogClockFace(w:uint)
+ {
+ this.w = w;
+ this.h = w;
+
+ // Rounds to the nearest pixel
+ this.radius = Math.round(this.w / 2);
+
+ // The face is always square now, so the
+ // distance to the center is the same
+ // horizontally and vertically
+ this.centerX = this.radius;
+ this.centerY = this.radius;
+ }
+
+ /**
+ * Creates the outline, hour labels, and clock hands.
+ */
+ public function init():void
+ {
+ // draws the circular clock outline
+ drawBorder();
+
+ // draws the hour numbers
+ drawLabels();
+
+ // creates the three clock hands
+ createHands();
+ }
+
+ /**
+ * Draws a circular border.
+ */
+ public function drawBorder():void
+ {
+ graphics.lineStyle(0.5, 0x999999);
+ graphics.beginFill(bgColor);
+ graphics.drawCircle(centerX, centerY, radius);
+ graphics.endFill();
+ }
+
+ /**
+ * Puts numeric labels at the hour points.
+ */
+ public function drawLabels():void
+ {
+ for (var i:Number = 1; i <= 12; i++)
+ {
+ // Creates a new TextField showing the hour number
+ var label:TextField = new TextField();
+ label.text = i.toString();
+
+ // Places hour labels around the clock face.
+ // The sin() and cos() functions both operate on angles in radians.
+ var angleInRadians:Number = i * 30 * (Math.PI/180)
+
+ // Place the label using the sin() and cos() functions to get the x,y coordinates.
+ // The multiplier value of 0.9 puts the labels inside the outline of the clock face.
+ // The integer value at the end of the equation adjusts the label position,
+ // since the x,y coordinate is in the upper left corner.
+ label.x = centerX + (0.9 * radius * Math.sin( angleInRadians )) - 5;
+ label.y = centerY - (0.9 * radius * Math.cos( angleInRadians )) - 9;
+
+ // Formats the label text.
+ var tf:TextFormat = new TextFormat();
+ tf.font = "Arial";
+ tf.bold = "true";
+ tf.size = 12;
+ label.setTextFormat(tf);
+
+ // Adds the label to the clock face display list.
+ addChild(label);
+ }
+ }
+
+ /**
+ * Creates hour, minute, and second hands using the 2D drawing API.
+ */
+ public function createHands():void
+ {
+ // Uses a Shape since it's the simplest component that supports
+ // the 2D drawing API.
+ var hourHandShape:Shape = new Shape();
+ drawHand(hourHandShape, Math.round(radius * 0.5), hourHandColor, 3.0);
+ this.hourHand = Shape(addChild(hourHandShape));
+ this.hourHand.x = centerX;
+ this.hourHand.y = centerY;
+
+ var minuteHandShape:Shape = new Shape();
+ drawHand(minuteHandShape, Math.round(radius * 0.8), minuteHandColor, 2.0);
+ this.minuteHand = Shape(addChild(minuteHandShape));
+ this.minuteHand.x = centerX;
+ this.minuteHand.y = centerY;
+
+ var secondHandShape:Shape = new Shape();
+ drawHand(secondHandShape, Math.round(radius * 0.9), secondHandColor, 0.5);
+ this.secondHand = Shape(addChild(secondHandShape));
+ this.secondHand.x = centerX;
+ this.secondHand.y = centerY;
+ }
+
+ /**
+ * Draws a clock hand with a given size, color, and thickness.
+ */
+ public function drawHand(hand:Shape, distance:uint, color:uint, thickness:Number):void
+ {
+ hand.graphics.lineStyle(thickness, color);
+ hand.graphics.moveTo(0, distance);
+ hand.graphics.lineTo(0, 0);
+ }
+
+ /**
+ * Called by the parent container when the display is being drawn.
+ */
+ public function draw():void
+ {
+ // Stores the current date and time in an instance variable
+ currentTime = new Date();
+ showTime(currentTime);
+ }
+
+ /**
+ * 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();
+
+ // Multiplies by 6 to get degrees
+ this.secondHand.rotation = 180 + (seconds * 6);
+ this.minuteHand.rotation = 180 + (minutes * 6);
+
+ // Multiplies by 30 to get basic degrees, and then
+ // adds up to 29.5 degrees (59 * 0.5) to account
+ // for the minutes
+ this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/SimpleClock.as b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/SimpleClock.as
new file mode 100644
index 0000000..95e06c8
--- /dev/null
+++ b/AlarmClock/flex_flashbuilder version/com/example/programmingas3/clock/SimpleClock.as
@@ -0,0 +1,66 @@
+package com.example.programmingas3.clock {
+ import mx.core.UIComponent;
+
+ public class SimpleClock extends UIComponent
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ /**
+ * The time display component.
+ */
+ public var face:AnalogClockFace;
+
+ /**
+ * The Timer that acts like a heartbeat for the application.
+ */
+ public var ticker:Timer;
+
+ public static const millisecondsPerMinute:int = 1000 * 60;
+ public static const millisecondsPerHour:int = 1000 * 60 * 60;
+ public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+
+ /**
+ * Sets up a SimpleClock instance.
+ */
+ public function initClock(faceSize:Number = 200):void
+ {
+ // sets the invoice date to today’s date
+ var invoiceDate:Date = new Date();
+
+ // adds 30 days to get the due date
+ var millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+ var dueDate:Date = new Date(invoiceDate.getTime() + (30 * millisecondsPerDay));
+
+ var oneHourFromNow:Date = new Date(); // starts at the current time
+ oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour);
+
+ // 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();
+
+ // 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();
+ }
+
+ /**
+ * Called once per second when the Timer event is received.
+ */
+ public function onTick(evt:TimerEvent):void
+ {
+ // Updates the clock display.
+ face.draw();
+ }
+ }
+}
\ No newline at end of file
diff --git a/AlgorithmicVisualGenerator/flash pro version/AlgorithmicVisualGenerator.fla b/AlgorithmicVisualGenerator/flash pro version/AlgorithmicVisualGenerator.fla
new file mode 100644
index 0000000..b8d07d3
Binary files /dev/null and b/AlgorithmicVisualGenerator/flash pro version/AlgorithmicVisualGenerator.fla differ
diff --git a/AlgorithmicVisualGenerator/flash pro version/com/example/programmingas3/algorithmic/AlgorithmicVisualGenerator.as b/AlgorithmicVisualGenerator/flash pro version/com/example/programmingas3/algorithmic/AlgorithmicVisualGenerator.as
new file mode 100644
index 0000000..b22be5d
--- /dev/null
+++ b/AlgorithmicVisualGenerator/flash pro version/com/example/programmingas3/algorithmic/AlgorithmicVisualGenerator.as
@@ -0,0 +1 @@
+package com.example.programmingas3.algorithmic
{
import fl.controls.Button;
import flash.display.Sprite;
import flash.display.Shape;
import flash.events.Event;
import flash.geom.ColorTransform;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import com.example.programmingas3.algorithmic.Satellite;
import com.example.programmingas3.algorithmic.ControlPanel;
public class AlgorithmicVisualGenerator extends Sprite {
private var satellites:Array;
private var container:Sprite;
private var bg:Shape;
public function AlgorithmicVisualGenerator() {
controlPanel.addEventListener("rebuild",build);
addEventListener(Event.ADDED_TO_STAGE, setStageListener);
addEventListener(Event.ENTER_FRAME, doEveryFrame);
build();
}
private function setStageListener(e:Event):void {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE,build);
}
private function clear() {
satellites = new Array()
if(container) {
swapChildren(controlPanel,container); // send control panel back to previous depth
removeChild(container);
}
}
private function build(e:Event = null):void {
clear();
container = new Sprite();
container.y = 120;
addChild(container);
swapChildren(controlPanel,container); // always place controls above animation;
bg = new Shape();
bg.graphics.beginFill(0xEEEEEE);
bg.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
container.addChild(bg);
var i:uint;
for(i=0; i
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AlgorithmicVisualGenerator/flex_flashbuilder version/com/example/programmingas3/algorithmic/Satellite.as b/AlgorithmicVisualGenerator/flex_flashbuilder version/com/example/programmingas3/algorithmic/Satellite.as
new file mode 100644
index 0000000..19c0ba1
--- /dev/null
+++ b/AlgorithmicVisualGenerator/flex_flashbuilder version/com/example/programmingas3/algorithmic/Satellite.as
@@ -0,0 +1,32 @@
+package com.example.programmingas3.algorithmic
+{
+ import flash.display.Shape;
+
+ public class Satellite extends Shape {
+ public var position:Number;
+ public var color:uint;
+ public var radius:Number;
+ public var orbitRadius:Number;
+
+ public function Satellite(position:Number,color:uint) {
+ this.position = position;
+ this.color = color;
+ }
+ public function draw(useAlphaEffect:Boolean = false):void {
+ var radians:Number = getRadians(position);
+ var posX:Number = Math.sin(radians) * orbitRadius;
+ var posY:Number = Math.cos(radians) * orbitRadius;
+ graphics.clear();
+ graphics.beginFill(color);
+ graphics.drawCircle(posX, posY, radius);
+
+ alpha = useAlphaEffect ? Math.abs(Math.sin(radians)) : 1;
+ }
+ private function getRadians(degrees:Number):Number {
+ return degrees * Math.PI / 180;
+ }
+ private function getDegrees(radians:Number):uint {
+ return Math.round(180 * radians / Math.PI)
+ }
+ }
+}
\ No newline at end of file
diff --git a/CapabilitiesExplorer/CapabilitiesExplorer.fla b/CapabilitiesExplorer/CapabilitiesExplorer.fla
new file mode 100644
index 0000000..9c86f59
Binary files /dev/null and b/CapabilitiesExplorer/CapabilitiesExplorer.fla differ
diff --git a/CapabilitiesExplorer/CapabilitiesExplorer.mxml b/CapabilitiesExplorer/CapabilitiesExplorer.mxml
new file mode 100644
index 0000000..b29916e
--- /dev/null
+++ b/CapabilitiesExplorer/CapabilitiesExplorer.mxml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CapabilitiesExplorer/capabilities.html b/CapabilitiesExplorer/capabilities.html
new file mode 100644
index 0000000..c28f4cf
--- /dev/null
+++ b/CapabilitiesExplorer/capabilities.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CapabilitiesExplorer/com/example/programmingas3/capabilities/CapabilitiesGrabber.as b/CapabilitiesExplorer/com/example/programmingas3/capabilities/CapabilitiesGrabber.as
new file mode 100644
index 0000000..239d487
--- /dev/null
+++ b/CapabilitiesExplorer/com/example/programmingas3/capabilities/CapabilitiesGrabber.as
@@ -0,0 +1,60 @@
+package com.example.programmingas3.capabilities {
+ import flash.system.Capabilities;
+ import flash.external.ExternalInterface;
+ import flash.net.URLVariables;
+ import flash.display.Sprite;
+
+ public class CapabilitiesGrabber extends Sprite {
+ public static function getCapabilities():Array {
+ var capDP:Array = new Array();
+ capDP.push({name:"Capabilities.avHardwareDisable", value:Capabilities.avHardwareDisable});
+ capDP.push({name:"Capabilities.hasAccessibility", value:Capabilities.hasAccessibility});
+ capDP.push({name:"Capabilities.hasAudio", value:Capabilities.hasAudio});
+ capDP.push({name:"Capabilities.hasAudioEncoder", value:Capabilities.hasAudioEncoder});
+ capDP.push({name:"Capabilities.hasEmbeddedVideo", value:Capabilities.hasEmbeddedVideo});
+ capDP.push({name:"Capabilities.hasIME", value:Capabilities.hasIME});
+ capDP.push({name:"Capabilities.hasMP3", value:Capabilities.hasMP3});
+ capDP.push({name:"Capabilities.hasPrinting", value:Capabilities.hasPrinting});
+ capDP.push({name:"Capabilities.hasScreenBroadcast", value:Capabilities.hasScreenBroadcast});
+ capDP.push({name:"Capabilities.hasScreenPlayback", value:Capabilities.hasScreenPlayback});
+ capDP.push({name:"Capabilities.hasStreamingAudio", value:Capabilities.hasStreamingAudio});
+ capDP.push({name:"Capabilities.hasStreamingVideo", value:Capabilities.hasStreamingVideo});
+ capDP.push({name:"Capabilities.hasTLS", value:Capabilities.hasTLS});
+ capDP.push({name:"Capabilities.hasVideoEncoder", value:Capabilities.hasVideoEncoder});
+ capDP.push({name:"Capabilities.isDebugger", value:Capabilities.isDebugger});
+ capDP.push({name:"Capabilities.language", value:Capabilities.language});
+ capDP.push({name:"Capabilities.localFileReadDisable", value:Capabilities.localFileReadDisable});
+ capDP.push({name:"Capabilities.manufacturer", value:Capabilities.manufacturer});
+ capDP.push({name:"Capabilities.os", value:Capabilities.os});
+ capDP.push({name:"Capabilities.pixelAspectRatio", value:Capabilities.pixelAspectRatio});
+ capDP.push({name:"Capabilities.playerType", value:Capabilities.playerType});
+ capDP.push({name:"Capabilities.screenColor", value:Capabilities.screenColor});
+ capDP.push({name:"Capabilities.screenDPI", value:Capabilities.screenDPI});
+ capDP.push({name:"Capabilities.screenResolutionX", value:Capabilities.screenResolutionX});
+ capDP.push({name:"Capabilities.screenResolutionY", value:Capabilities.screenResolutionY});
+ capDP.push({name:"Capabilities.version", value:Capabilities.version});
+ var navArr:Array = CapabilitiesGrabber.getBrowserObjects();
+ if (navArr.length > 0) {
+ capDP = capDP.concat(navArr);
+ }
+ capDP.sortOn("name", Array.CASEINSENSITIVE);
+ return capDP;
+ }
+ private static function getBrowserObjects():Array {
+ var itemArr:Array = new Array();
+ var itemVars:URLVariables;
+ if (ExternalInterface.available) {
+ try {
+ var tempStr:String = ExternalInterface.call("JS_getBrowserObjects");
+ itemVars = new URLVariables(tempStr);
+ for (var i:String in itemVars) {
+ itemArr.push({name:i, value:itemVars[i]});
+ }
+ } catch (error:SecurityError) {
+ // ignore
+ }
+ }
+ return itemArr;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomErrors/CustomErrors.fla b/CustomErrors/CustomErrors.fla
new file mode 100644
index 0000000..4431719
Binary files /dev/null and b/CustomErrors/CustomErrors.fla differ
diff --git a/CustomErrors/CustomErrors.mxml b/CustomErrors/CustomErrors.mxml
new file mode 100644
index 0000000..7976e12
--- /dev/null
+++ b/CustomErrors/CustomErrors.mxml
@@ -0,0 +1,115 @@
+
+
+
+
+
+ John
+ Doe
+ 12345
+ 67890
+ ;
+
+ statusText.text = "Click the Validate button to check the XML";
+ hideButtons();
+ }
+
+ public function validateData():void
+ {
+ try
+ {
+ var tempXML:XML = XML(xmlText.text);
+
+ Validator.validateEmployeeXML(tempXML);
+
+ statusText.text = "The XML was successfully validated.";
+ }
+ catch (error:FatalError)
+ {
+ showFatalError(error);
+ }
+ catch (error:WarningError)
+ {
+ showWarningError(error);
+ }
+ catch (error:Error)
+ {
+ showGenericError(error);
+ }
+ }
+
+ public function showFatalError(error:FatalError):void
+ {
+ var message:String = error.message + "\n\n";
+ var title:String = error.getTitle();
+ statusText.text = message + " " + title + "\n\nThis application has ended.";
+ this.xmlText.enabled = false;
+ this.validateBtn.enabled = false;
+ hideButtons();
+ }
+
+ public function showWarningError(error:WarningError):void
+ {
+ var message:String = error.message + "\n\n" + "Do you want to exit this application?";
+ showButtons();
+ var title:String = error.getTitle();
+ statusText.text = message;
+ }
+
+ public function showGenericError(error:Error):void
+ {
+ statusText.text = error.message + "\n\nEncountered an unknown error: " + error.name;
+ }
+
+ private function showButtons():void
+ {
+ yesButton.visible = true;
+ noButton.visible = true;
+ }
+
+ private function hideButtons():void
+ {
+ yesButton.visible = false;
+ noButton.visible = false;
+ }
+
+ private function closeHandler(event:MouseEvent):void
+ {
+ switch (event.target)
+ {
+ case yesButton:
+ showFatalError(new FatalError(9999));
+ break;
+ case noButton:
+ statusText.text = "";
+ hideButtons();
+ break;
+ }
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CustomErrors/com/example/programmingas3/errors/ApplicationError.as b/CustomErrors/com/example/programmingas3/errors/ApplicationError.as
new file mode 100644
index 0000000..e25ba3e
--- /dev/null
+++ b/CustomErrors/com/example/programmingas3/errors/ApplicationError.as
@@ -0,0 +1,33 @@
+package com.example.programmingas3.errors
+{
+ public class ApplicationError extends Error
+ {
+ internal static const WARNING:int = 0;
+ internal static const FATAL:int = 1;
+ public var id:int;
+ public var severity:int;
+ private static var messages:XML;
+
+ public function ApplicationError() {
+ messages =
+
+
+
+
+ ;
+ }
+
+ public function getMessageText(id:int):String {
+ var message:XMLList = messages.error.(@code == id);
+ return message[0].text();
+ }
+
+ public function getTitle():String {
+ return "Error #" + id;
+ }
+
+ public function toString():String {
+ return "[APPLICATION ERROR #" + id + "] " + message;
+ }
+ }
+}
diff --git a/CustomErrors/com/example/programmingas3/errors/FatalError.as b/CustomErrors/com/example/programmingas3/errors/FatalError.as
new file mode 100644
index 0000000..21013e0
--- /dev/null
+++ b/CustomErrors/com/example/programmingas3/errors/FatalError.as
@@ -0,0 +1,20 @@
+package com.example.programmingas3.errors
+{
+ public class FatalError extends ApplicationError
+ {
+ public function FatalError(errorID:int)
+ {
+ id = errorID;
+ severity = ApplicationError.FATAL
+ message = getMessageText(errorID);
+ }
+
+ public override function getTitle():String {
+ return "Error #" + id + " -- FATAL";
+ }
+
+ public override function toString():String {
+ return "[FATAL ERROR #" + id + "] " + message;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomErrors/com/example/programmingas3/errors/Validator.as b/CustomErrors/com/example/programmingas3/errors/Validator.as
new file mode 100644
index 0000000..a7bfbfc
--- /dev/null
+++ b/CustomErrors/com/example/programmingas3/errors/Validator.as
@@ -0,0 +1,22 @@
+package com.example.programmingas3.errors
+{
+ public class Validator
+ {
+
+ 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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomErrors/com/example/programmingas3/errors/WarningError.as b/CustomErrors/com/example/programmingas3/errors/WarningError.as
new file mode 100644
index 0000000..acb00b3
--- /dev/null
+++ b/CustomErrors/com/example/programmingas3/errors/WarningError.as
@@ -0,0 +1,21 @@
+package com.example.programmingas3.errors {
+
+ public class WarningError extends ApplicationError {
+
+ public function WarningError(errorID:int)
+ {
+ id = errorID;
+ severity = ApplicationError.WARNING
+ message = super.getMessageText(errorID);
+ }
+
+ public override function getTitle():String {
+ return "Error #" + id + " -- Warning"
+ }
+
+ public override function toString():String {
+ return "[WARNING ERROR #" + id + "] " + message;
+ }
+
+ }
+}
diff --git a/DisplayObjectTransformer/DisplayObjectTransformer.fla b/DisplayObjectTransformer/DisplayObjectTransformer.fla
new file mode 100644
index 0000000..dae6a14
Binary files /dev/null and b/DisplayObjectTransformer/DisplayObjectTransformer.fla differ
diff --git a/DisplayObjectTransformer/DisplayObjectTransformer.mxml b/DisplayObjectTransformer/DisplayObjectTransformer.mxml
new file mode 100644
index 0000000..5cde33a
--- /dev/null
+++ b/DisplayObjectTransformer/DisplayObjectTransformer.mxml
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DisplayObjectTransformer/com/example/programmingas3/geometry/MatrixTransformer.as b/DisplayObjectTransformer/com/example/programmingas3/geometry/MatrixTransformer.as
new file mode 100644
index 0000000..891218b
--- /dev/null
+++ b/DisplayObjectTransformer/com/example/programmingas3/geometry/MatrixTransformer.as
@@ -0,0 +1,109 @@
+package com.example.programmingas3.geometry
+{
+ import flash.geom.Matrix;
+
+ /**
+ * Supports simple matrix transformations: scaling, translation, rotation, and skewing.
+ */
+
+ public class MatrixTransformer
+ {
+ /**
+ * Calls the appropriate method of the MatrixTransformer class, based on the radio buttons
+ * selected. Then updates the transformation matrix based on the results of the method.
+ *
+ * Runs the same transformation matrix through successive transformations and then applies
+ * the matrix to the image at the very end, which is more efficient than applying after
+ * each individual transformation.
+ */
+ public static function transform(sourceMatrix:Matrix,
+ xScale:Number=100,
+ yScale:Number=100,
+ dx:Number=0,
+ dy:Number=0,
+ rotation:Number=0,
+ skew:Number=0,
+ skewType:String="right"):Matrix
+ {
+ // skew:
+ sourceMatrix = MatrixTransformer.skew(sourceMatrix, skew, skewType);
+
+ // scale:
+ sourceMatrix = MatrixTransformer.scale(sourceMatrix, xScale, yScale);
+
+ // translate:
+ sourceMatrix = MatrixTransformer.translate(sourceMatrix, dx, dy);
+
+ // rotate:
+ sourceMatrix = MatrixTransformer.rotate(sourceMatrix, rotation, "degrees");
+
+ return sourceMatrix;
+ }
+
+ /**
+ * Scales a matrix and returns the result. The percent parameter lets the user specify scale
+ * factors (xScale and yScale) as percentages (such as 33) instead of absolute values (such as 0.33).
+ */
+ public static function scale(sourceMatrix:Matrix, xScale:Number, yScale:Number, percent:Boolean = true):Matrix
+ {
+ if (percent)
+ {
+ xScale = xScale / 100;
+ yScale = yScale / 100;
+ }
+ sourceMatrix.scale(xScale, yScale)
+ return sourceMatrix;
+ }
+
+ /**
+ * Translates a matrix and returns the result.
+ */
+ public static function translate(sourceMatrix:Matrix, dx:Number, dy:Number):Matrix {
+ sourceMatrix.translate(dx, dy);
+ return sourceMatrix;
+ }
+
+ /**
+ * Rotates a matrix and returns the result. The unit parameter lets the user specify "degrees",
+ * "gradients", or "radians".
+ */
+ public static function rotate(sourceMatrix:Matrix, angle:Number, unit:String = "radians"):Matrix {
+ if (unit == "degrees")
+ {
+ angle = Math.PI * 2 * angle / 360;
+ }
+ if (unit == "gradients")
+ {
+ angle = Math.PI * 2 * angle / 100;
+ }
+ sourceMatrix.rotate(angle)
+ return sourceMatrix;
+ }
+
+ /**
+ * Scales a matrix and returns the result. The skewSide parameter lets the user
+ * determine which side to skew (right or bottom).The unit parameter lets the user
+ * specify "degrees", "gradients", or "radians".
+ */
+ public static function skew(sourceMatrix:Matrix, angle:Number, skewSide:String = "right", unit:String = "degrees"):Matrix {
+ if (unit == "degrees")
+ {
+ angle = Math.PI * 2 * angle / 360;
+ }
+ if (unit == "gradients")
+ {
+ angle = Math.PI * 2 * angle / 100;
+ }
+ var skewMatrix:Matrix = new Matrix();
+ if (skewSide == "right")
+ {
+ skewMatrix.b = Math.tan(angle);
+ } else { // skewSide == "bottom"
+ skewMatrix.c = Math.tan(angle);
+ }
+ sourceMatrix.concat(skewMatrix)
+ return sourceMatrix;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/DisplayObjectTransformer/img/Banana.jpg b/DisplayObjectTransformer/img/Banana.jpg
new file mode 100644
index 0000000..f15fd7e
Binary files /dev/null and b/DisplayObjectTransformer/img/Banana.jpg differ
diff --git a/FileIO/flash pro version/FileIO.fla b/FileIO/flash pro version/FileIO.fla
new file mode 100644
index 0000000..4f82fde
Binary files /dev/null and b/FileIO/flash pro version/FileIO.fla differ
diff --git a/FileIO/flash pro version/com/example/programmingas3/fileio/FileDownload.as b/FileIO/flash pro version/com/example/programmingas3/fileio/FileDownload.as
new file mode 100644
index 0000000..d146dfb
--- /dev/null
+++ b/FileIO/flash pro version/com/example/programmingas3/fileio/FileDownload.as
@@ -0,0 +1,81 @@
+package com.example.programmingas3.fileio {
+ import flash.events.*;
+ import flash.net.FileReference;
+ import flash.net.URLRequest;
+
+ import fl.controls.Button;
+ import fl.controls.ProgressBar;
+
+ public class FileDownload {
+ // Hard-code the URL of file to download to user's computer.
+ private const DOWNLOAD_URL:String = "http://www.yourdomain.com/file_to_download.zip";
+ private var fr:FileReference;
+ // Define reference to the download ProgressBar component.
+ private var pb:ProgressBar;
+ // Define reference to the "Cancel" button which will immediately stop the download in progress.
+ private var btn:Button;
+
+ public function FileDownload() {
+ }
+
+ /**
+ * Set references to the components, and add listeners for the OPEN,
+ * PROGRESS, and COMPLETE events.
+ */
+ public function init(pb:ProgressBar, btn:Button):void {
+ // Set up the references to the progress bar and cancel button, which are passed from the calling script.
+ this.pb = pb;
+ this.btn = btn;
+
+ fr = new FileReference();
+ fr.addEventListener(Event.OPEN, openHandler);
+ fr.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ fr.addEventListener(Event.COMPLETE, completeHandler);
+ }
+
+ /**
+ * Immediately cancel the download in progress and disable the cancel button.
+ */
+ public function cancelDownload(e:MouseEvent):void {
+ fr.cancel();
+// pb.label = "DOWNLOAD CANCELLED";
+ btn.enabled = false;
+ }
+
+ /**
+ * Begin downloading the file specified in the DOWNLOAD_URL constant.
+ */
+ public function startDownload(e:MouseEvent):void {
+ var request:URLRequest = new URLRequest();
+ request.url = DOWNLOAD_URL;
+ fr.download(request);
+ }
+
+ /**
+ * When the OPEN event has dispatched, change the progress bar's label
+ * and enable the "Cancel" button, which allows the user to abort the
+ * download operation.
+ */
+ private function openHandler(event:Event):void {
+// pb.label = "DOWNLOADING %3%%";
+ btn.enabled = true;
+ }
+
+ /**
+ * While the file is downloading, update the progress bar's status and label.
+ */
+ private function progressHandler(event:ProgressEvent):void {
+ pb.setProgress(event.bytesLoaded, event.bytesTotal);
+ }
+
+ /**
+ * Once the download has completed, change the progress bar's label and
+ * disable the "Cancel" button since the download is already completed.
+ */
+ private function completeHandler(event:Event):void {
+// pb.label = "DOWNLOAD COMPLETE";
+ pb.setProgress(0, 100);
+ btn.enabled = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FileIO/flash pro version/com/example/programmingas3/fileio/FileUpload.as b/FileIO/flash pro version/com/example/programmingas3/fileio/FileUpload.as
new file mode 100644
index 0000000..7391dd9
--- /dev/null
+++ b/FileIO/flash pro version/com/example/programmingas3/fileio/FileUpload.as
@@ -0,0 +1,90 @@
+package com.example.programmingas3.fileio {
+ import flash.events.*;
+ import flash.net.FileReference;
+ import flash.net.URLRequest;
+
+ import fl.controls.Button;
+ import fl.controls.ProgressBar;
+
+ public class FileUpload {
+ // Hard-code the URL of the remote upload script.
+ private const UPLOAD_URL:String = "http://www.yourdomain.com/upload_script.cfm";
+ private var fr:FileReference;
+ // Define reference to the upload ProgressBar component.
+ private var pb:ProgressBar;
+ // Define reference to the "Cancel" button which will immediately stop the upload in progress.
+ private var btn:Button;
+
+ public function FileUpload() {
+ }
+
+ /**
+ * Set references to the components, and add listeners for the SELECT,
+ * OPEN, PROGRESS, and COMPLETE events.
+ */
+ public function init(pb:ProgressBar, btn:Button):void {
+ // Set up the references to the progress bar and cancel button, which are passed from the calling script.
+ this.pb = pb;
+ this.btn = btn;
+
+ fr = new FileReference();
+ fr.addEventListener(Event.SELECT, selectHandler);
+ fr.addEventListener(Event.OPEN, openHandler);
+ fr.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ fr.addEventListener(Event.COMPLETE, completeHandler);
+ }
+
+ /**
+ * Immediately cancel the upload in progress and disable the cancel button.
+ */
+ public function cancelUpload(e:MouseEvent):void {
+ fr.cancel();
+// pb.label = "UPLOAD CANCELLED";
+ btn.enabled = false;
+ }
+
+ /**
+ * Launch the browse dialog box which allows the user to select a file to upload to the server.
+ */
+ public function startUpload(e:MouseEvent):void {
+ fr.browse();
+ }
+
+ /**
+ * Begin uploading the file specified in the UPLOAD_URL constant.
+ */
+ private function selectHandler(event:Event):void {
+ var request:URLRequest = new URLRequest();
+ request.url = UPLOAD_URL;
+ fr.upload(request);
+ }
+
+ /**
+ * When the OPEN event has dispatched, change the progress bar's label
+ * and enable the "Cancel" button, which allows the user to abort the
+ * upload operation.
+ */
+ private function openHandler(event:Event):void {
+// pb.label = "UPLOADING";
+ btn.enabled = true;
+ }
+
+ /**
+ * While the file is uploading, update the progress bar's status and label.
+ */
+ private function progressHandler(event:ProgressEvent):void {
+// pb.label = "UPLOADING %3%%";
+ pb.setProgress(event.bytesLoaded, event.bytesTotal);
+ }
+
+ /**
+ * Once the upload has completed, change the progress bar's label and
+ * disable the "Cancel" button since the upload is already completed.
+ */
+ private function completeHandler(event:Event):void {
+// pb.label = "UPLOADING COMPLETE";
+ pb.setProgress(0, 100);
+ btn.enabled = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FileIO/flex_flashbuilder version/FileIO.mxml b/FileIO/flex_flashbuilder version/FileIO.mxml
new file mode 100644
index 0000000..56fedec
--- /dev/null
+++ b/FileIO/flex_flashbuilder version/FileIO.mxml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileDownload.as b/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileDownload.as
new file mode 100644
index 0000000..4071c30
--- /dev/null
+++ b/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileDownload.as
@@ -0,0 +1,82 @@
+package com.example.programmingas3.fileio {
+ import flash.events.*;
+ import flash.net.FileReference;
+ import flash.net.URLRequest;
+
+ import mx.controls.Button;
+ import mx.controls.ProgressBar;
+ import mx.core.UIComponent;
+
+ public class FileDownload extends UIComponent {
+ // Hard-code the URL of file to download to user's computer.
+ private const DOWNLOAD_URL:String = "http://www.yourdomain.com/file_to_download.zip";
+ private var fr:FileReference;
+ // Define reference to the download ProgressBar component.
+ private var pb:ProgressBar;
+ // Define reference to the "Cancel" button which will immediately stop the download in progress.
+ private var btn:Button;
+
+ public function FileDownload() {
+ }
+
+ /**
+ * Set references to the components, and add listeners for the OPEN,
+ * PROGRESS, and COMPLETE events.
+ */
+ public function init(pb:ProgressBar, btn:Button):void {
+ // Set up the references to the progress bar and cancel button, which are passed from the calling script.
+ this.pb = pb;
+ this.btn = btn;
+
+ fr = new FileReference();
+ fr.addEventListener(Event.OPEN, openHandler);
+ fr.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ fr.addEventListener(Event.COMPLETE, completeHandler);
+ }
+
+ /**
+ * Immediately cancel the download in progress and disable the cancel button.
+ */
+ public function cancelDownload():void {
+ fr.cancel();
+ pb.label = "DOWNLOAD CANCELLED";
+ btn.enabled = false;
+ }
+
+ /**
+ * Begin downloading the file specified in the DOWNLOAD_URL constant.
+ */
+ public function startDownload():void {
+ var request:URLRequest = new URLRequest();
+ request.url = DOWNLOAD_URL;
+ fr.download(request);
+ }
+
+ /**
+ * When the OPEN event has dispatched, change the progress bar's label
+ * and enable the "Cancel" button, which allows the user to abort the
+ * download operation.
+ */
+ private function openHandler(event:Event):void {
+ pb.label = "DOWNLOADING %3%%";
+ btn.enabled = true;
+ }
+
+ /**
+ * While the file is downloading, update the progress bar's status and label.
+ */
+ private function progressHandler(event:ProgressEvent):void {
+ pb.setProgress(event.bytesLoaded, event.bytesTotal);
+ }
+
+ /**
+ * Once the download has completed, change the progress bar's label and
+ * disable the "Cancel" button since the download is already completed.
+ */
+ private function completeHandler(event:Event):void {
+ pb.label = "DOWNLOAD COMPLETE";
+ pb.setProgress(0, 100);
+ btn.enabled = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileUpload.as b/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileUpload.as
new file mode 100644
index 0000000..2c8f96d
--- /dev/null
+++ b/FileIO/flex_flashbuilder version/com/example/programmingas3/fileio/FileUpload.as
@@ -0,0 +1,91 @@
+package com.example.programmingas3.fileio {
+ import flash.events.*;
+ import flash.net.FileReference;
+ import flash.net.URLRequest;
+
+ import mx.controls.Button;
+ import mx.controls.ProgressBar;
+ import mx.core.UIComponent;
+
+ public class FileUpload extends UIComponent {
+ // Hard-code the URL of the remote upload script.
+ private const UPLOAD_URL:String = "http://www.yourdomain.com/upload_script.cfm";
+ private var fr:FileReference;
+ // Define reference to the upload ProgressBar component.
+ private var pb:ProgressBar;
+ // Define reference to the "Cancel" button which will immediately stop the upload in progress.
+ private var btn:Button;
+
+ public function FileUpload() {
+ }
+
+ /**
+ * Set references to the components, and add listeners for the SELECT,
+ * OPEN, PROGRESS, and COMPLETE events.
+ */
+ public function init(pb:ProgressBar, btn:Button):void {
+ // Set up the references to the progress bar and cancel button, which are passed from the calling script.
+ this.pb = pb;
+ this.btn = btn;
+
+ fr = new FileReference();
+ fr.addEventListener(Event.SELECT, selectHandler);
+ fr.addEventListener(Event.OPEN, openHandler);
+ fr.addEventListener(ProgressEvent.PROGRESS, progressHandler);
+ fr.addEventListener(Event.COMPLETE, completeHandler);
+ }
+
+ /**
+ * Immediately cancel the upload in progress and disable the cancel button.
+ */
+ public function cancelUpload():void {
+ fr.cancel();
+ pb.label = "UPLOAD CANCELLED";
+ btn.enabled = false;
+ }
+
+ /**
+ * Launch the browse dialog box which allows the user to select a file to upload to the server.
+ */
+ public function startUpload():void {
+ fr.browse();
+ }
+
+ /**
+ * Begin uploading the file specified in the UPLOAD_URL constant.
+ */
+ private function selectHandler(event:Event):void {
+ var request:URLRequest = new URLRequest();
+ request.url = UPLOAD_URL;
+ fr.upload(request);
+ }
+
+ /**
+ * When the OPEN event has dispatched, change the progress bar's label
+ * and enable the "Cancel" button, which allows the user to abort the
+ * upload operation.
+ */
+ private function openHandler(event:Event):void {
+ pb.label = "UPLOADING";
+ btn.enabled = true;
+ }
+
+ /**
+ * While the file is uploading, update the progress bar's status and label.
+ */
+ private function progressHandler(event:ProgressEvent):void {
+ pb.label = "UPLOADING %3%%";
+ pb.setProgress(event.bytesLoaded, event.bytesTotal);
+ }
+
+ /**
+ * Once the upload has completed, change the progress bar's label and
+ * disable the "Cancel" button since the upload is already completed.
+ */
+ private function completeHandler(event:Event):void {
+ pb.label = "UPLOADING COMPLETE";
+ pb.setProgress(0, 100);
+ btn.enabled = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/FilterWorkbench.fla b/FilterWorkbench/FilterWorkbench.fla
new file mode 100644
index 0000000..28934bd
Binary files /dev/null and b/FilterWorkbench/FilterWorkbench.fla differ
diff --git a/FilterWorkbench/FilterWorkbench.mxml b/FilterWorkbench/FilterWorkbench.mxml
new file mode 100644
index 0000000..758820f
--- /dev/null
+++ b/FilterWorkbench/FilterWorkbench.mxml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+ Button
+ {
+ font-weight: normal;
+ }
+ ImageContainer
+ {
+ background-color: #ffffff;
+ border: 1px solid #000000;
+ }
+
+ .code
+ {
+ font-family: "Andale Mono", Monaco, "Courier New", Courier, "_typewriter";
+ }
+ .panelLabel
+ {
+ font-family: Arial;
+ font-size: 14px;
+ font-weight: bold;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/BevelFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/BevelFactory.as
new file mode 100644
index 0000000..ec93952
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/BevelFactory.as
@@ -0,0 +1,135 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.BevelFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class BevelFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:BevelFilter;
+ private var _paramString:String;
+
+
+ // ------- Constructor -------
+ public function BevelFactory(distance:Number = 4,
+ angle:Number = 45,
+ highlightColor:uint = 0xFFFFFF,
+ highlightAlpha:Number = 1,
+ shadowColor:uint = 0x000000,
+ shadowAlpha:Number = 1,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false)
+ {
+ _filter = new BevelFilter(distance, angle, highlightColor, highlightAlpha, shadowColor, shadowAlpha, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, highlightColor, highlightAlpha, shadowColor, shadowAlpha, blurX, blurY, strength, quality, type, knockout);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BevelFilter;\n";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.BitmapFilterType;\n";
+ result += "\n";
+ result += "var bevel:BevelFilter;\n";
+ result += "bevel = new BevelFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [bevel];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(distance:Number = 4,
+ angle:Number = 45,
+ highlightColor:uint = 0xFFFFFF,
+ highlightAlpha:Number = 1,
+ shadowColor:uint = 0x000000,
+ shadowAlpha:Number = 1,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false):void
+ {
+ _filter = new BevelFilter(distance, angle, highlightColor, highlightAlpha, shadowColor, shadowAlpha, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, highlightColor, highlightAlpha, shadowColor, shadowAlpha, blurX, blurY, strength, quality, type, knockout);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function buildParamString(distance:Number,
+ angle:Number,
+ highlightColor:uint,
+ highlightAlpha:Number,
+ shadowColor:uint,
+ shadowAlpha:Number,
+ blurX:Number,
+ blurY:Number,
+ strength:Number,
+ quality:int,
+ type:String,
+ knockout:Boolean):String
+ {
+ var result:String = distance.toString() + ",\n\t\t" + angle.toString() + ",\n\t\t";
+ result += ColorStringFormatter.formatColorHex24(highlightColor) + ",\n\t\t" + highlightAlpha.toString() + ",\n\t\t";
+ result += ColorStringFormatter.formatColorHex24(shadowColor) + ",\n\t\t" + shadowAlpha.toString() + ",\n\t\t";
+ result += blurX.toString() + ",\n\t\t" + blurY.toString() + ",\n\t\t" + strength.toString() + ",\n\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ result += ",\n\t\t";
+
+ switch (type)
+ {
+ case "inner":
+ result += "BitmapFilterType.INNER";
+ break;
+ case "outer":
+ result += "BitmapFilterType.OUTER";
+ break;
+ case "full":
+ result += "BitmapFilterType.FULL";
+ break;
+ }
+
+ result += ",\n\t\t" + knockout.toString();
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/BlurFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/BlurFactory.as
new file mode 100644
index 0000000..e43c0c3
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/BlurFactory.as
@@ -0,0 +1,82 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.BlurFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class BlurFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:BlurFilter;
+ private var _paramString:String;
+
+
+ // ------- Constructor -------
+ public function BlurFactory(blurX:Number = 4, blurY:Number = 4, quality:int = 1)
+ {
+ _filter = new BlurFilter(blurX, blurY, quality);
+ _paramString = buildParamString(blurX, blurY, quality);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.BlurFilter;\n";
+ result += "\n";
+ result += "var blur:BlurFilter;\n";
+ result += "blur = new BlurFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [blur];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(blurX:Number = 4,
+ blurY:Number = 4,
+ quality:int = 1):void
+ {
+ _filter = new BlurFilter(blurX, blurY, quality);
+ _paramString = buildParamString(blurX, blurY, quality);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function buildParamString(blurX:Number, blurY:Number, quality:int):String
+ {
+ var result:String = "\n\t\t\t" + blurX.toString() + ",\n\t\t\t" + blurY.toString() + ",\n\t\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorMatrixFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorMatrixFactory.as
new file mode 100644
index 0000000..80697c9
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorMatrixFactory.as
@@ -0,0 +1,228 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.ColorMatrixFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class ColorMatrixFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:ColorMatrixFilter;
+ private var _paramString:String;
+
+ private var _matrix:Array;
+
+
+ // ------- Constructor -------
+ public function ColorMatrixFactory(matrix:Array = null)
+ {
+ if (matrix == null)
+ {
+ resetMatrix();
+ }
+ else
+ {
+ _matrix = matrix;
+ }
+
+ _filter = new ColorMatrixFilter(_matrix);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.ColorMatrixFilter;\n";
+ result += "\n";
+
+ result += "var matrix:Array = [";
+ for (var i:int = 0; i < 20; i++)
+ {
+ if (i > 0)
+ {
+ result += ", ";
+ }
+ if (i % 5 == 0)
+ {
+ result += "\n\t\t\t";
+ }
+ result += _matrix[i].toString();
+ }
+ result += "\n\t\t\t];\n";
+ result += "\n";
+
+ result += "var colorMatrix:ColorMatrixFilter;\n";
+ result += "colorMatrix = new ColorMatrixFilter(matrix);\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [colorMatrix];";
+
+ return result;
+ }
+
+
+ // ------- Public properties -------
+ public function get matrix():Array
+ {
+ return _matrix;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilterBasic(brightness:Number, contrast:Number, saturation:Number, hue:Number):void
+ {
+ // calculate the combined matrix using the preset values
+ resetMatrix();
+ setHue(hue);
+ setSaturation(saturation);
+ setContrast(contrast);
+ setBrightness(brightness);
+
+ _filter = new ColorMatrixFilter(_matrix);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ public function modifyFilterCustom(matrix:Array = null):void
+ {
+ if (matrix == null)
+ {
+ resetMatrix();
+ }
+ else
+ {
+ _matrix = matrix;
+ }
+
+ _filter = new ColorMatrixFilter(_matrix);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Color adjustments -------
+ private function resetMatrix():void
+ {
+ _matrix = [1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0];
+ }
+
+
+ // Color matrix algorithms derived from:
+ // Haeberli, Paul (1993) "Matrix Operations for Image Processing."
+ // Graphica Obscura: http://www.graficaobscura.com/matrix/index.html
+
+ // takes a brightness value between -100 and 100
+ private function setBrightness(value:Number):void
+ {
+ // convert the value to a percentage of 255
+ var brightness:Number = (value / 100) * 255;
+
+ var matrix:Array = [1, 0, 0, 0, brightness,
+ 0, 1, 0, 0, brightness,
+ 0, 0, 1, 0, brightness,
+ 0, 0, 0, 1, 0];
+
+ _matrix = mMultiply(_matrix, matrix);
+ }
+
+
+ // takes a contrast value between -100 and 100
+ private function setContrast(value:Number):void
+ {
+ var base:Number = value / 100;
+ var multiplier:Number = 1 + ((value > 0) ? 4 * base : base);
+ var offset:Number = (-128 * base) * ((value > 0) ? 5 : 1);
+ var matrix:Array = [multiplier, 0, 0, 0, offset,
+ 0, multiplier, 0, 0, offset,
+ 0, 0, multiplier, 0, offset,
+ 0, 0, 0, 1, 0];
+
+ _matrix = mMultiply(_matrix, matrix);
+ }
+
+
+ // takes a saturation value between -100 and 100
+ private function setSaturation(value:Number):void
+ {
+ var saturation:Number = 1 + ((value > 0) ? 3 * (value / 100) : (value / 100));
+
+ // RGB to Luminance conversion constants by Charles A. Poynton:
+ // http://www.faqs.org/faqs/graphics/colorspace-faq/
+ var rWeight:Number = 0.212671;
+ var gWeight:Number = 0.715160;
+ var bWeight:Number = 0.072169;
+ var baseSat:Number = 1 - saturation;
+ var rSat:Number = (baseSat * rWeight) + saturation;
+ var r:Number = baseSat * rWeight;
+ var gSat:Number = (baseSat * gWeight) + saturation;
+ var g:Number = baseSat * gWeight;
+ var bSat:Number = (baseSat * bWeight) + saturation;
+ var b:Number = baseSat * bWeight;
+
+ var matrix:Array = [rSat, g, b, 0, 0,
+ r, gSat, b, 0, 0,
+ r, g, bSat, 0, 0,
+ 0, 0, 0, 1, 0];
+
+ _matrix = mMultiply(_matrix, matrix);
+ }
+
+
+ // takes a hue value (an angle) between -180 and 180 degrees
+ private function setHue(value:Number):void
+ {
+ var angle:Number = value * Math.PI / 180;
+
+ var c:Number = Math.cos(angle);
+ var s:Number = Math.sin(angle);
+
+ var lumR:Number = 0.213;
+ var lumG:Number = 0.715;
+ var lumB:Number = 0.072;
+
+ var matrix:Array = [lumR + (c * (1 - lumR)) + (s * (-lumR)), lumG + (c * (-lumG)) + (s * (-lumG)), lumB + (c * (-lumB)) + (s * (1 - lumB)), 0, 0,
+ lumR + (c * (-lumR)) + (s * 0.143), lumG + (c * (1 - lumG)) + (s * 0.14), lumB + (c * (-lumB)) + (s * (-0.283)), 0, 0,
+ lumR + (c * (-lumR)) + (s * (-(1 - lumR))), lumG + (c * (-lumG)) + (s * lumG), lumB + (c * (1 - lumB)) + (s * lumB), 0, 0,
+ 0, 0, 0, 1, 0];
+
+ _matrix = mMultiply(_matrix, matrix);
+ }
+
+
+ // Performs matrix multiplication between two 4x5 matrices
+ private static function mMultiply(m1:Array, m2:Array):Array
+ {
+ var result:Array = new Array(20);
+
+ for (var row:int = 0; row < 19; row += 5)
+ {
+ for (var col:int = 0; col < 5; col++)
+ {
+ var cell:Number = (m1[row] * m2[col]) + (m1[row + 1] * m2[col + 5]) + (m1[row + 2] * m2[col + 10]) + (m1[row + 3] * m2[col + 15]);
+ if (col == 4)
+ {
+ cell += m1[row + 4];
+ }
+ result[row + col] = cell;
+ }
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorStringFormatter.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorStringFormatter.as
new file mode 100644
index 0000000..edfe803
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ColorStringFormatter.as
@@ -0,0 +1,16 @@
+package com.example.programmingas3.filterWorkbench
+{
+ public class ColorStringFormatter
+ {
+ public static function formatColorHex24(color:uint):String
+ {
+ var r:String = ((color >> 16) & 0xFF).toString(16);
+ r = (r.length > 1) ? r : "0" + r;
+ var g:String = ((color >> 8) & 0xFF).toString(16);
+ g = (g.length > 1) ? g : "0" + g;
+ var b:String = (color & 0xFF).toString(16);
+ b = (b.length > 1) ? b : "0" + b;
+ return "0x" + r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/ConvolutionFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ConvolutionFactory.as
new file mode 100644
index 0000000..cc862d5
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ConvolutionFactory.as
@@ -0,0 +1,121 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.ConvolutionFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class ConvolutionFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:ConvolutionFilter;
+ private var _paramString:String;
+
+
+ // ------- Constructor -------
+ public function ConvolutionFactory(matrixX:Number = 3,
+ matrixY:Number = 3,
+ matrix:Array = null,
+ divisor:Number = 1.0,
+ bias:Number = 0.0,
+ preserveAlpha:Boolean = true,
+ clamp:Boolean = true,
+ color:uint = 0,
+ alpha:Number = 0.0)
+ {
+ if (matrix == null)
+ {
+ matrix = makeDefaultMatrix(matrixX, matrixY);
+ }
+ _filter = new ConvolutionFilter(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha);
+ _paramString = buildParamString(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.ConvolutionFilter;\n";
+ result += "\n";
+ result += "var convolution:ConvolutionFilter;\n";
+ result += "convolution = new ConvolutionFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [convolution];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(matrixX:Number = 3,
+ matrixY:Number = 3,
+ matrix:Array = null,
+ divisor:Number = 1.0,
+ bias:Number = 0.0,
+ preserveAlpha:Boolean = true,
+ clamp:Boolean = true,
+ color:uint = 0,
+ alpha:Number = 0.0):void
+ {
+ if (matrix == null)
+ {
+ matrix = makeDefaultMatrix(matrixX, matrixY);
+ }
+ _filter = new ConvolutionFilter(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha);
+ _paramString = buildParamString(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function makeDefaultMatrix(matrixX:Number, matrixY:Number):Array
+ {
+ var numFields:Number = matrixX * matrixY;
+ var centerIndex:Number = Math.floor(numFields / 2);
+ var result:Array = new Array(numFields);
+ for (var i:int = 0; i < numFields; i++)
+ {
+ result[i] = (i == centerIndex) ? 1 : 0;
+ }
+
+ return result;
+ }
+
+
+ private function buildParamString(matrixX:Number, matrixY:Number, matrix:Array, divisor:Number, bias:Number, preserveAlpha:Boolean, clamp:Boolean, color:uint, alpha:Number):String
+ {
+ var result:String = "\n\t\t\t" + matrixX.toString() + ",\n\t\t\t" + matrixY.toString() + ",\n\t\t\t";
+
+ result += "[";
+ for (var i:Number = 0, numElements:Number = matrix.length; i < numElements; i++)
+ {
+ if (i > 0)
+ {
+ result += ", ";
+ }
+ if (i != 0 && i % matrixX == 0)
+ {
+ result += "\n\t\t\t ";
+ }
+ result += matrix[i].toString();
+ }
+ result += "],\n\t\t\t";
+
+ result += divisor.toString() + ",\n\t\t\t" + bias.toString() + ",\n\t\t\t";
+ result += preserveAlpha.toString() + ",\n\t\t\t" + clamp.toString() + ",\n\t\t\t";
+ result += ColorStringFormatter.formatColorHex24(color) + ",\n\t\t\t" + alpha.toString();
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/DropShadowFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/DropShadowFactory.as
new file mode 100644
index 0000000..ab24a17
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/DropShadowFactory.as
@@ -0,0 +1,117 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.DropShadowFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class DropShadowFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:DropShadowFilter;
+ private var _paramString:String;
+
+
+ // ------- Constructor -------
+ public function DropShadowFactory(distance:Number = 4,
+ angle:Number = 45,
+ color:uint = 0x000000,
+ alpha:Number = 1,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ inner:Boolean = false,
+ knockout:Boolean = false,
+ hideObject:Boolean = false)
+ {
+ _filter = new DropShadowFilter(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
+ _paramString = buildParamString(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.DropShadowFilter;\n";
+ result += "\n";
+ result += "var dropShadow:DropShadowFilter;\n";
+ result += "dropShadow = new DropShadowFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [dropShadow];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(distance:Number = 4,
+ angle:Number = 45,
+ color:uint = 0x000000,
+ alpha:Number = 1,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ inner:Boolean = false,
+ knockout:Boolean = false,
+ hideObject:Boolean = false):void
+ {
+ _filter = new DropShadowFilter(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
+ _paramString = buildParamString(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function buildParamString(distance:Number,
+ angle:Number,
+ color:uint,
+ alpha:Number,
+ blurX:Number,
+ blurY:Number,
+ strength:Number,
+ quality:int,
+ inner:Boolean,
+ knockout:Boolean,
+ hideObject:Boolean):String
+ {
+ var result:String = "\n\t\t\t" + distance.toString() + ",\n\t\t\t" + angle.toString() + ",\n\t\t\t";
+ result += ColorStringFormatter.formatColorHex24(color) + ",\n\t\t\t" + alpha.toString() + ",\n\t\t\t";
+ result += blurX.toString() + ",\n\t\t\t" + blurY.toString() + ",\n\t\t\t" + strength.toString() + ",\n\t\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ result += ",\n\t\t\t";
+
+ result += inner.toString() + ",\n\t\t\t" + knockout.toString() + ",\n\t\t\t" + hideObject.toString();
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/FilterWorkbenchController.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/FilterWorkbenchController.as
new file mode 100644
index 0000000..49808a4
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/FilterWorkbenchController.as
@@ -0,0 +1,233 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.ImageType;
+
+ import flash.display.DisplayObject;
+ import flash.display.Loader;
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.events.IOErrorEvent;
+ import flash.events.ProgressEvent;
+ import flash.filters.BitmapFilter;
+ import flash.net.URLRequest;
+
+
+ // ------- Events -------
+ /**
+ * Dispatched when progress occurs while the filter target image is loading.
+ */
+ [Event(name="progress", type="flash.events.ProgressEvent")]
+ /**
+ * Dispatched when the filter target image finishes loading.
+ */
+ [Event(name="complete", type="flash.events.Event")]
+ /**
+ * Dispatched when the filter target image changes (its filters change).
+ */
+ [Event(name="change", type="flash.events.Event")]
+
+
+ /**
+ * The main class that provides the functionality of the FilterWorkbench application,
+ * including tracking sets of filters and applying the filters to the currently
+ * selected filter target.
+ */
+ public class FilterWorkbenchController extends EventDispatcher
+ {
+ // ------- Private vars -------
+ private var _filterFactory:IFilterFactory;
+ private var _currentFilters:Array;
+ private var _currentTarget:DisplayObject;
+ private var _loader:Loader;
+
+ private var _loading:Boolean;
+
+
+ // ------- Constructor -------
+ public function FilterWorkbenchController()
+ {
+ super();
+ }
+
+
+ // ------- Public Methods -------
+ /**
+ * Selects a new type of image to be the "filter target" (the image to which
+ * the filters will be applied). Calling this method starts the process of
+ * loading the specified image; the image is available when the Event.COMPLETE
+ * event is dispatched.
+ *
+ * @param targetType The ImageType constant representing the type of image
+ * desired.
+ */
+ public function setFilterTarget(targetType:ImageType):void
+ {
+ if (targetType == null) { return; }
+
+ if (_loading)
+ {
+ _loader.close();
+ _loading = false;
+ }
+
+ if (_currentTarget != null)
+ {
+ _currentTarget = null;
+ }
+
+ _loader = new Loader();
+
+ _loader.load(new URLRequest(targetType.url));
+ _loading = true;
+ _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, targetLoadIOError);
+ _loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, targetLoadProgress);
+ _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, targetLoadComplete);
+
+ setFilter(null);
+
+ // Reset the filters array
+ _currentFilters = new Array();
+ }
+
+ /**
+ * Retrieves the current "filter target" -- the image that was selected to have filters
+ * applied to it
+ * @return The current filter target image
+ */
+ public function getFilterTarget():DisplayObject
+ {
+ return _currentTarget;
+ }
+
+
+ /**
+ * Adds a new filter to the set of filters that are applied to
+ * the current filter target. Calling this method clears away the
+ * current "temporary" filter and replaces it with the one
+ * that's passed in the factory parameter.
+ *
+ * @param factory The IFilterFactory that will build the
+ * current working filter.
+ */
+ public function setFilter(factory:IFilterFactory):void
+ {
+ if (_filterFactory == factory) { return; }
+
+ // Clean up the previous filter factory
+ if (_filterFactory != null)
+ {
+ _filterFactory.removeEventListener(Event.CHANGE, filterChange);
+
+ if (_currentTarget != null)
+ {
+ // refresh the image's filters (removing the last temporary filter)
+ _currentTarget.filters = _currentFilters;
+ }
+ }
+
+ // Store the new one (even if it's null)
+ _filterFactory = factory;
+
+ // Notify listeners that the filter has changed
+ dispatchEvent(new Event(Event.CHANGE));
+
+ if (factory == null || _currentTarget == null)
+ {
+ return;
+ }
+
+ // apply the new filter
+ applyTemporaryFilter();
+
+ _filterFactory.addEventListener(Event.CHANGE, filterChange);
+ }
+
+
+ /**
+ * "Permanently" adds the current filter to the set of filters applied
+ * to the target image. After this method is called, the next time
+ * a filter's values change it is added as an additional filter rather than
+ * replacing the filter that is current when applyFilter() is called.
+ */
+ public function applyFilter():void
+ {
+ if (_filterFactory != null)
+ {
+ _currentFilters.push(_filterFactory.getFilter());
+ _currentTarget.filters = _currentFilters;
+ setFilter(null);
+ }
+ }
+
+
+ /**
+ * Retrieves a String containing the ActionScript code that would be used to create the
+ * currently selected filter.
+ *
+ * @return The ActionScript code to create the filter effect
+ */
+ public function getCode():String
+ {
+ return (_filterFactory != null) ? _filterFactory.getCode() : "";
+ }
+
+
+ // ------- Event Handling -------
+ private function filterChange(event:Event):void
+ {
+ applyTemporaryFilter();
+
+ // dispatch our own change event so the app knows to update the code display
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // Called when an IO Error is reported by a loading filter target image.
+ private function targetLoadIOError(event:IOErrorEvent):void
+ {
+ _loading = false;
+ _loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, targetLoadIOError);
+ _loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, targetLoadProgress);
+ _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, targetLoadComplete);
+ trace("load error");
+ }
+
+
+ // Called when loading progress is reported by a loading filter target image.
+ private function targetLoadProgress(event:ProgressEvent):void
+ {
+ dispatchEvent(event);
+ }
+
+
+ // Called when a filter target image finishes loading.
+ private function targetLoadComplete(event:Event):void
+ {
+ _loading = false;
+ _loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, targetLoadIOError);
+ _loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, targetLoadProgress);
+ _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, targetLoadComplete);
+
+ _currentTarget = _loader.content;
+
+ dispatchEvent(event);
+ }
+
+
+ private function applyTemporaryFilter():void
+ {
+ var currentFilter:BitmapFilter = _filterFactory.getFilter();
+ // Add the current filter to the set temporarily
+ _currentFilters.push(currentFilter);
+
+ // Refresh the filter set of the filter target
+ _currentTarget.filters = _currentFilters;
+
+ // Remove the current filter from the set
+ // (This doesn't remove it from the filter target, since
+ // the target uses a copy of the filters array internally.)
+ _currentFilters.pop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/GlowFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GlowFactory.as
new file mode 100644
index 0000000..304d09a
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GlowFactory.as
@@ -0,0 +1,107 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.GlowFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class GlowFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:GlowFilter;
+ private var _paramString:String;
+
+
+ // ------- Constructor -------
+ public function GlowFactory(color:uint = 0xFF0000,
+ alpha:Number = 1,
+ blurX:Number = 6,
+ blurY:Number = 6,
+ strength:Number = 2,
+ quality:int = 1,
+ inner:Boolean = false,
+ knockout:Boolean = false)
+ {
+ _filter = new GlowFilter(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+ _paramString = buildParamString(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.GlowFilter;\n";
+ result += "\n";
+ result += "var glow:GlowFilter;\n";
+ result += "glow = new GlowFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [glow];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(color:uint = 0xFF0000,
+ alpha:Number = 1,
+ blurX:Number = 6,
+ blurY:Number = 6,
+ strength:Number = 2,
+ quality:int = 1,
+ inner:Boolean = false,
+ knockout:Boolean = false):void
+ {
+ _filter = new GlowFilter(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+ _paramString = buildParamString(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function buildParamString(color:uint,
+ alpha:Number,
+ blurX:Number,
+ blurY:Number,
+ strength:Number,
+ quality:int,
+ inner:Boolean,
+ knockout:Boolean):String
+ {
+ var result:String = "\n\t\t" + ColorStringFormatter.formatColorHex24(color) + ",\n\t\t" + alpha.toString() + ",\n\t\t";
+ result += blurX.toString() + ",\n\t\t" + blurY.toString() + ",\n\t\t" + strength.toString() + ",\n\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ result += ",\n\t\t";
+
+ result += inner.toString() + ",\n\t\t" + knockout.toString();
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientBevelFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientBevelFactory.as
new file mode 100644
index 0000000..1baa57d
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientBevelFactory.as
@@ -0,0 +1,209 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.GradientBevelFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class GradientBevelFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:GradientBevelFilter;
+ private var _paramString:String;
+ private var _colorsString:String;
+ private var _alphasString:String;
+ private var _ratiosString:String;
+
+
+ // ------- Constructor -------
+ public function GradientBevelFactory(distance:Number = 4,
+ angle:Number = 45,
+ combinedColors:Array = null,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false)
+ {
+ if (combinedColors == null)
+ {
+ combinedColors = getDefaultColors();
+ }
+ var colors:Array = new Array(combinedColors.length);
+ var alphas:Array = new Array(combinedColors.length);
+ var ratios:Array = new Array(combinedColors.length);
+
+ separateColorParts(combinedColors, colors, alphas, ratios);
+
+ _filter = new GradientBevelFilter(distance, angle, colors, alphas, ratios, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, blurX, blurY, strength, quality, type, knockout);
+ _colorsString = buildArrayString(colors, true);
+ _alphasString = buildArrayString(alphas);
+ _ratiosString = buildArrayString(ratios);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.BitmapFilterType;\n";
+ result += "import flash.filters.GradientBevelFilter;\n";
+ result += "\n";
+ result += "var colors:Array = [" + _colorsString + "];\n";
+ result += "var alphas:Array = [" + _alphasString + "];\n";
+ result += "var ratios:Array = [" + _ratiosString + "];\n";
+ result += "\n";
+ result += "var gradientBevel:GradientBevelFilter;\n";
+ result += "gradientBevel = new GradientBevelFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [gradientBevel];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(distance:Number = 4,
+ angle:Number = 45,
+ combinedColors:Array = null,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false):void
+ {
+ if (combinedColors == null)
+ {
+ combinedColors = getDefaultColors();
+ }
+ var colors:Array = new Array(combinedColors.length);
+ var alphas:Array = new Array(combinedColors.length);
+ var ratios:Array = new Array(combinedColors.length);
+
+ separateColorParts(combinedColors, colors, alphas, ratios);
+
+ _filter = new GradientBevelFilter(distance, angle, colors, alphas, ratios, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, blurX, blurY, strength, quality, type, knockout);
+ _colorsString = buildArrayString(colors, true);
+ _alphasString = buildArrayString(alphas);
+ _ratiosString = buildArrayString(ratios);
+
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function getDefaultColors():Array
+ {
+ return [new GradientColor(0xFFFFFF, 1, 0),
+ new GradientColor(0xFF0000, .25, 128),
+ new GradientColor(0x000000, 1, 255)];
+ }
+
+
+ // takeas an array of GradientColor objects and splits it into three arrays of colors, alphas, and ratios
+ // the destination arrays must be instantiated and sized before calling this method.
+ private function separateColorParts(source:Array, colorsDest:Array, alphasDest:Array, ratiosDest:Array):void
+ {
+ var numColors:int = source.length;
+
+ for (var i:int = 0; i < numColors; i++)
+ {
+ var gradientColor:GradientColor = source[i];
+
+ colorsDest[i] = gradientColor.color;
+ alphasDest[i] = gradientColor.alpha;
+ ratiosDest[i] = gradientColor.ratio;
+ }
+ }
+
+
+ private function buildParamString(distance:Number,
+ angle:Number,
+ blurX:Number,
+ blurY:Number,
+ strength:Number,
+ quality:int,
+ type:String,
+ knockout:Boolean):String
+ {
+ var result:String = "\n\t\t" + distance.toString() + ",\n\t\t" + angle.toString() + ",\n\t\t";
+ result += "colors, alphas, ratios,\n\t\t";
+ result += blurX.toString() + ",\n\t\t" + blurY.toString() + ",\n\t\t" + strength.toString() + ",\n\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ result += ",\n\t\t";
+
+ switch (type)
+ {
+ case "inner":
+ result += "BitmapFilterType.INNER";
+ break;
+ case "outer":
+ result += "BitmapFilterType.OUTER";
+ break;
+ case "full":
+ result += "BitmapFilterType.FULL";
+ break;
+ }
+
+ result += ",\n\t\t" + knockout.toString();
+
+ return result;
+ }
+
+
+ private function buildArrayString(arr:Array, formatColor:Boolean = false):String
+ {
+ var len:int = arr.length;
+ var result:String = "";
+
+ for (var i:int = 0; i < len; i++)
+ {
+ if (i != 0)
+ {
+ result += ", ";
+ }
+ if (formatColor)
+ {
+ result += ColorStringFormatter.formatColorHex24(arr[i]);
+ }
+ else
+ {
+ result += arr[i].toString();
+ }
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientColor.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientColor.as
new file mode 100644
index 0000000..d912241
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientColor.as
@@ -0,0 +1,49 @@
+package com.example.programmingas3.filterWorkbench
+{
+ public class GradientColor
+ {
+ // ------- Private vars -------
+ private var _color:uint;
+ private var _alpha:Number;
+ private var _ratio:Number;
+
+ // ------- Constructor -------
+ public function GradientColor(color:uint, alpha:Number, ratio:Number)
+ {
+ _color = color;
+ _alpha = alpha;
+ _ratio = ratio;
+ }
+
+
+ // ------- Public properties -------
+ public function get color():uint
+ {
+ return _color;
+ }
+ public function set color(value:uint):void
+ {
+ _color = value;
+ }
+
+
+ public function get alpha():Number
+ {
+ return _alpha;
+ }
+ public function set alpha(value:Number):void
+ {
+ _alpha = value;
+ }
+
+
+ public function get ratio():Number
+ {
+ return _ratio;
+ }
+ public function set ratio(value:Number):void
+ {
+ _ratio = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientGlowFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientGlowFactory.as
new file mode 100644
index 0000000..e459826
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/GradientGlowFactory.as
@@ -0,0 +1,209 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ import flash.events.Event;
+ import flash.events.EventDispatcher;
+ import flash.filters.BitmapFilter;
+ import flash.filters.GradientGlowFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ public class GradientGlowFactory extends EventDispatcher implements IFilterFactory
+ {
+ // ------- Private vars -------
+ private var _filter:GradientGlowFilter;
+ private var _paramString:String;
+ private var _colorsString:String;
+ private var _alphasString:String;
+ private var _ratiosString:String;
+
+
+ // ------- Constructor -------
+ public function GradientGlowFactory(distance:Number = 4,
+ angle:Number = 45,
+ combinedColors:Array = null,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false)
+ {
+ if (combinedColors == null)
+ {
+ combinedColors = getDefaultColors();
+ }
+ var colors:Array = new Array(combinedColors.length);
+ var alphas:Array = new Array(combinedColors.length);
+ var ratios:Array = new Array(combinedColors.length);
+
+ separateColorParts(combinedColors, colors, alphas, ratios);
+
+ _filter = new GradientGlowFilter(distance, angle, colors, alphas, ratios, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, blurX, blurY, strength, quality, type, knockout);
+ _colorsString = buildArrayString(colors, true);
+ _alphasString = buildArrayString(alphas);
+ _ratiosString = buildArrayString(ratios);
+ }
+
+
+ // ------- IFilterFactory implementation -------
+ public function getFilter():BitmapFilter
+ {
+ return _filter;
+ }
+
+
+ public function getCode():String
+ {
+ var result:String = "";
+ result += "import flash.filters.BitmapFilterQuality;\n";
+ result += "import flash.filters.BitmapFilterType;\n";
+ result += "import flash.filters.GradientGlowFilter;\n";
+ result += "\n";
+ result += "var colors:Array = [" + _colorsString + "];\n";
+ result += "var alphas:Array = [" + _alphasString + "];\n";
+ result += "var ratios:Array = [" + _ratiosString + "];\n";
+ result += "\n";
+ result += "var gradientGlow:GradientGlowFilter;\n";
+ result += "gradientGlow = new GradientGlowFilter(" + _paramString + ");\n";
+ result += "\n";
+ result += "myDisplayObject.filters = [gradientGlow];";
+ return result;
+ }
+
+
+ // ------- Public methods -------
+ public function modifyFilter(distance:Number = 4,
+ angle:Number = 45,
+ combinedColors:Array = null,
+ blurX:Number = 4,
+ blurY:Number = 4,
+ strength:Number = 1,
+ quality:int = 1,
+ type:String = "inner",
+ knockout:Boolean = false):void
+ {
+ if (combinedColors == null)
+ {
+ combinedColors = getDefaultColors();
+ }
+ var colors:Array = new Array(combinedColors.length);
+ var alphas:Array = new Array(combinedColors.length);
+ var ratios:Array = new Array(combinedColors.length);
+
+ separateColorParts(combinedColors, colors, alphas, ratios);
+
+ _filter = new GradientGlowFilter(distance, angle, colors, alphas, ratios, blurX, blurY, strength, quality, type, knockout);
+ _paramString = buildParamString(distance, angle, blurX, blurY, strength, quality, type, knockout);
+ _colorsString = buildArrayString(colors, true);
+ _alphasString = buildArrayString(alphas);
+ _ratiosString = buildArrayString(ratios);
+
+ dispatchEvent(new Event(Event.CHANGE));
+ }
+
+
+ // ------- Private methods -------
+ private function getDefaultColors():Array
+ {
+ return [new GradientColor(0xFFFFFF, 1, 0),
+ new GradientColor(0xFF0000, .25, 128),
+ new GradientColor(0x000000, 1, 255)];
+ }
+
+
+ // takeas an array of GradientColor objects and splits it into three arrays of colors, alphas, and ratios
+ // the destination arrays must be instantiated and sized before calling this method.
+ private function separateColorParts(source:Array, colorsDest:Array, alphasDest:Array, ratiosDest:Array):void
+ {
+ var numColors:int = source.length;
+
+ for (var i:int = 0; i < numColors; i++)
+ {
+ var gradientColor:GradientColor = source[i];
+
+ colorsDest[i] = gradientColor.color;
+ alphasDest[i] = gradientColor.alpha;
+ ratiosDest[i] = gradientColor.ratio;
+ }
+ }
+
+
+ private function buildParamString(distance:Number,
+ angle:Number,
+ blurX:Number,
+ blurY:Number,
+ strength:Number,
+ quality:int,
+ type:String,
+ knockout:Boolean):String
+ {
+ var result:String = "\n\t\t" + distance.toString() + ",\n\t\t" + angle.toString() + ",\n\t\t";
+ result += "colors, alphas, ratios,\n\t\t";
+ result += blurX.toString() + ",\n\t\t" + blurY.toString() + ",\n\t\t" + strength.toString() + ",\n\t\t";
+
+ switch (quality)
+ {
+ case 1:
+ result += "BitmapFilterQuality.LOW";
+ break;
+ case 2:
+ result += "BitmapFilterQuality.MEDIUM";
+ break;
+ case 3:
+ result += "BitmapFilterQuality.HIGH";
+ break;
+ }
+
+ result += ",\n\t\t";
+
+ switch (type)
+ {
+ case "inner":
+ result += "BitmapFilterType.INNER";
+ break;
+ case "outer":
+ result += "BitmapFilterType.OUTER";
+ break;
+ case "full":
+ result += "BitmapFilterType.FULL";
+ break;
+ }
+
+ result += ",\n\t\t" + knockout.toString();
+
+ return result;
+ }
+
+
+ private function buildArrayString(arr:Array, formatColor:Boolean = false):String
+ {
+ var len:int = arr.length;
+ var result:String = "";
+
+ for (var i:int = 0; i < len; i++)
+ {
+ if (i != 0)
+ {
+ result += ", ";
+ }
+ if (formatColor)
+ {
+ result += ColorStringFormatter.formatColorHex24(arr[i]);
+ }
+ else
+ {
+ result += arr[i].toString();
+ }
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterFactory.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterFactory.as
new file mode 100644
index 0000000..ee7900c
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterFactory.as
@@ -0,0 +1,26 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import flash.events.Event;
+ import flash.events.IEventDispatcher;
+ import flash.filters.BitmapFilter;
+
+ // ------- Events -------
+ [Event(name="change", type="flash.events.Event")]
+
+
+ /**
+ * Defines the common methods for the filter factory classes used in the FilterWorkbench application.
+ * Each IFilterFactory implementer has specific properties or methods for setting values for its specific
+ * filter. The interface's getFilter() method is used to retreive the filter in its current state.
+ *
+ * Although it can't be explicitly enforced by the compiler, each IFilterFactory implementer should provide
+ * a flash.events.Event.CHANGE event indicating that the filter has changed. For this reason IFilterFactory
+ * implementers must also implement flash.events.IEventDispatcher.
+ */
+ public interface IFilterFactory extends IEventDispatcher
+ {
+ function getFilter():BitmapFilter;
+
+ function getCode():String;
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterPanel.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterPanel.as
new file mode 100644
index 0000000..4ccc064
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/IFilterPanel.as
@@ -0,0 +1,27 @@
+package com.example.programmingas3.filterWorkbench
+{
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+
+ /**
+ * Defines the common methods and properties that the application's
+ * filter panels expose for getting data out of the panels.
+ *
+ * All filter panels should implement this interface and be a display
+ * object.
+ */
+ public interface IFilterPanel
+ {
+ /**
+ * Resets the filter panel to its original settings.
+ */
+ function resetForm():void;
+
+
+ /**
+ * Gets a filter factory that generates the bitmap filter
+ * with the settings and filter type specified by the
+ * filter panel and its current settings.
+ */
+ function get filterFactory():IFilterFactory;
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/com/example/programmingas3/filterWorkbench/ImageType.as b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ImageType.as
new file mode 100644
index 0000000..6353fe2
--- /dev/null
+++ b/FilterWorkbench/com/example/programmingas3/filterWorkbench/ImageType.as
@@ -0,0 +1,45 @@
+package com.example.programmingas3.filterWorkbench
+{
+ /**
+ * Combination value object and enumeration class whose values represent
+ * the types of images that are available to be used as filter targets
+ * in the FilterWorkbench application.
+ */
+ public class ImageType
+ {
+ public static const IMAGE1:ImageType = new ImageType("Bitmap image 1", "images/sampleImage1.jpg");
+ public static const IMAGE2:ImageType = new ImageType("Bitmap image 2", "images/sampleImage2.jpg");
+ public static const SWF:ImageType = new ImageType("SWF animation", "images/sampleAnimation.swf");
+
+
+ public static function getImageTypes():Array
+ {
+ return new Array(IMAGE1, IMAGE2, SWF);
+ }
+
+ // ------- Private vars -------
+ private var _name:String;
+ private var _url:String;
+
+
+ // ------- Constructor -------
+ public function ImageType(name:String, url:String)
+ {
+ _name = name;
+ _url = url;
+ }
+
+
+ // ------- Public properties -------
+ public function get name():String
+ {
+ return _name;
+ }
+
+
+ public function get url():String
+ {
+ return _url;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/BGColorCellRenderer.as b/FilterWorkbench/flashapp/BGColorCellRenderer.as
new file mode 100644
index 0000000..5d7db21
--- /dev/null
+++ b/FilterWorkbench/flashapp/BGColorCellRenderer.as
@@ -0,0 +1,91 @@
+package flashapp
+{
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+
+ import fl.controls.DataGrid;
+ import fl.controls.dataGridClasses.DataGridColumn;
+ import fl.controls.listClasses.CellRenderer;
+ import fl.controls.listClasses.ICellRenderer;
+
+ import flash.display.Shape;
+ import flash.display.DisplayObjectContainer;
+ import flash.text.TextFormat;
+
+ public class BGColorCellRenderer extends CellRenderer implements ICellRenderer
+ {
+ private var defaultTextFormat:TextFormat;
+ private var tf:TextFormat;
+
+ public function BGColorCellRenderer()
+ {
+ super();
+
+ // grab the original font color
+ defaultTextFormat = getStyleValue("textFormat") as TextFormat;
+
+ tf = new TextFormat();
+ tf.align = defaultTextFormat.align;
+ tf.blockIndent = defaultTextFormat.blockIndent;
+ tf.bold = defaultTextFormat.bold;
+ tf.bullet = defaultTextFormat.bullet;
+ tf.color = defaultTextFormat.color;
+ tf.font = defaultTextFormat.font;
+ tf.indent = defaultTextFormat.indent;
+ tf.italic = defaultTextFormat.italic;
+ tf.kerning = defaultTextFormat.kerning;
+ tf.leading = defaultTextFormat.leading;
+ tf.leftMargin = defaultTextFormat.leftMargin;
+ tf.letterSpacing = defaultTextFormat.letterSpacing;
+ tf.rightMargin = defaultTextFormat.rightMargin;
+ tf.size = defaultTextFormat.size;
+ tf.tabStops = defaultTextFormat.tabStops;
+ tf.target = defaultTextFormat.target;
+ tf.underline = defaultTextFormat.underline;
+ tf.url = defaultTextFormat.url;
+ }
+
+ public static function getStyleDefinition():Object
+ {
+ return CellRenderer.getStyleDefinition();
+ }
+
+ protected override function drawBackground():void
+ {
+ super.drawBackground();
+ var gColor:GradientColor = data as GradientColor;
+ var bg:DisplayObjectContainer = background as DisplayObjectContainer;
+ if (mouseState == "up" && gColor != null && bg != null)
+ {
+ // calculate the foreground color
+ var r:Number = (gColor.color >> 16) & 0xFF;
+ var g:Number = (gColor.color >> 8) & 0xFF;
+ var b:Number = gColor.color & 0xFF;
+ var avg:int = Math.round((r + g + b) / 3);
+ var foreground:uint = (avg > 0x99 || gColor.alpha < .6) ? 0x000000 : 0xFFFFFF;
+
+ // create the swatch background
+ var swatch:Shape = new Shape();
+ swatch.graphics.moveTo(0, 0);
+ swatch.graphics.lineStyle(1, foreground, 1);
+ swatch.graphics.beginFill(gColor.color, gColor.alpha);
+ swatch.graphics.lineTo(bg.width, 0);
+ swatch.graphics.lineTo(bg.width, bg.height);
+ swatch.graphics.lineTo(0, bg.height);
+ swatch.graphics.lineTo(0, 0);
+ swatch.graphics.endFill();
+ swatch.x = bg.x;
+ swatch.y = bg.y;
+ bg.addChild(swatch);
+
+ // set the text color
+ tf.color = foreground;
+
+ setStyle("textFormat", tf);
+ }
+ else if (mouseState == "over" || mouseState == "down")
+ {
+ setStyle("textFormat", defaultTextFormat);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/ButtonCellRenderer.as b/FilterWorkbench/flashapp/ButtonCellRenderer.as
new file mode 100644
index 0000000..9b3a6c4
--- /dev/null
+++ b/FilterWorkbench/flashapp/ButtonCellRenderer.as
@@ -0,0 +1,201 @@
+package flashapp
+{
+ import fl.controls.Button;
+ import fl.core.UIComponent;
+ import fl.controls.listClasses.ICellRenderer;
+ import fl.controls.listClasses.ListData;
+ import fl.core.InvalidationType;
+ import fl.managers.IFocusManagerComponent;
+
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.events.MouseEvent;
+
+ // ------- Styles -------
+ [Style(name="background", type="Class")]
+ [Style(name="disabledSkin", type="Class")]
+ [Style(name="downSkin", type="Class")]
+ [Style(name="emphasizedSkin", type="Class")]
+ [Style(name="overSkin", type="Class")]
+ [Style(name="upSkin", type="Class")]
+ [Style(name="selectedDisabledSkin", type="Class")]
+ [Style(name="selectedDownSkin", type="Class")]
+ [Style(name="selectedOverSkin", type="Class")]
+ [Style(name="selectedUpSkin", type="Class")]
+ [Style(name="cellPadding", type="Number", format="Length")]
+
+
+ public class ButtonCellRenderer extends UIComponent implements ICellRenderer
+ {
+ // ------- Private vars -------
+ private var _data:Object;
+ private var _listData:ListData;
+
+
+ // ------- Child controls -------
+ private var _background:DisplayObject;
+ private var _button:Button;
+
+
+ // ------- Constructor -------
+ public function ButtonCellRenderer()
+ {
+ super();
+ }
+
+
+ // ------- Styles -------
+ private static const defaultStyles:Object =
+ {
+ background:"CellRenderer_upSkin",
+ disabledSkin:"Button_disabledSkin",
+ downSkin:"Button_downSkin",
+ emphasizedSkin:"Button_emphasizedSkin",
+ overSkin:"Button_overSkin",
+ upSkin:"Button_upSkin",
+ selectedDisabledSkin:"Button_selectedDisabledSkin",
+ selectedDownSkin:"Button_selectedDownSkin",
+ selectedOverSkin:"Button_selectedOverSkin",
+ selectedUpSkin:"Button_selectedUpSkin",
+ cellPadding:4
+ };
+
+ private static const BUTTON_STYLES:Object =
+ {
+ disabledSkin:"disabledSkin",
+ downSkin:"downSkin",
+ emphasizedSkin:"emphasizedSkin",
+ overSkin:"overSkin",
+ upSkin:"upSkin",
+ selectedDisabledSkin:"selectedDisabledSkin",
+ selectedDownSkin:"selectedDownSkin",
+ selectedOverSkin:"selectedOverSkin",
+ selectedUpSkin:"selectedUpSkin"
+ };
+
+ public static function getStyleDefinition():Object
+ {
+ return defaultStyles;
+ }
+
+
+ // ------- ICellRenderer implementation -------
+ public function get data():Object
+ {
+ return _data;
+ }
+ public function set data(value:Object):void
+ {
+ _data = value;
+ }
+
+
+ public function get listData():ListData
+ {
+ return _listData;
+ }
+ public function set listData(value:ListData):void
+ {
+ _listData = value;
+ _button.label = _listData.label;
+ _button.setStyle("icon", _listData.icon);
+ }
+
+
+ public function get selected():Boolean
+ {
+ return _button.selected;
+ }
+ public function set selected(value:Boolean):void
+ {
+ _button.selected = value;
+ }
+
+
+ public function setMouseState(state:String):void
+ {
+ // do nothing
+ }
+
+
+ public override function setSize(w:Number, h:Number):void
+ {
+ super.setSize(w, h);
+ }
+
+
+ // ------- Undocumented but required by ICellRenderer -------
+ public override function set x(value:Number):void
+ {
+ super.x = value;
+ }
+
+
+ public override function set y(value:Number):void
+ {
+ super.y = value;
+ }
+
+
+ // ------- IFocusManagerComponent implementation -------
+ // all inherited
+
+
+ // ------- Additional UIComponent overrides -------
+ protected override function draw():void
+ {
+ if (isInvalid(InvalidationType.STYLES, InvalidationType.STATE))
+ {
+ copyStylesToChild(_button, BUTTON_STYLES);
+ invalidate(InvalidationType.SIZE, false);
+ }
+
+ if (isInvalid(InvalidationType.SIZE))
+ {
+ drawLayout();
+ }
+
+ validate();
+ }
+
+
+ protected override function configUI():void
+ {
+ super.configUI();
+
+ _background = getDisplayObjectInstance(getStyleValue("background"));
+ addChild(_background);
+
+ _button = new Button();
+ copyStylesToChild(_button, BUTTON_STYLES);
+ _button.autoRepeat = false;
+ addChild(_button);
+
+ _button.addEventListener(MouseEvent.CLICK, buttonClick);
+ }
+
+
+ // ------- Event Handling -------
+ private function buttonClick(event:MouseEvent):void
+ {
+ event.stopImmediatePropagation();
+ dispatchEvent(new MouseEvent(MouseEvent.CLICK));
+ }
+
+
+ // ------- Private methods -------
+ // --- Layout/Styles ---
+ private function drawLayout():void
+ {
+ _background.width = width;
+ _background.height = height;
+
+ var pad:Number = Number(getStyleValue("cellPadding"));
+ _button.setSize(width - pad, Math.min(_button.height, height - pad));
+ var newX:Number = (width / 2) - (_button.width / 2);
+ var newY:Number = (height / 2) - (_button.height / 2);
+ _button.move(newX, newY);
+ _button.drawNow();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/FilterWorkbench.as b/FilterWorkbench/flashapp/FilterWorkbench.as
new file mode 100644
index 0000000..2f9298d
--- /dev/null
+++ b/FilterWorkbench/flashapp/FilterWorkbench.as
@@ -0,0 +1,308 @@
+package flashapp
+{
+ import com.example.programmingas3.filterWorkbench.FilterWorkbenchController;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+ import com.example.programmingas3.filterWorkbench.ImageType;
+
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.events.ProgressEvent;
+ import flash.text.Font;
+ import flash.text.TextFormat;
+
+ import flashapp.filterPanels.BevelPanel;
+ import flashapp.filterPanels.BlurPanel;
+ import flashapp.filterPanels.ColorMatrixPanel;
+ import flashapp.filterPanels.ConvolutionPanel;
+ import flashapp.filterPanels.DropShadowPanel;
+ import flashapp.filterPanels.GlowPanel;
+ import flashapp.filterPanels.GradientBevelPanel;
+ import flashapp.filterPanels.GradientGlowPanel;
+
+ /**
+ * This class is the view (the main form) of the Flash version of the FilterWorkbench application
+ */
+ public class FilterWorkbench extends Sprite
+ {
+ // ------- Constants -------
+ // --- Filter types ---
+ private static const BEVEL:String = "bevel";
+ private static const BLUR:String = "blur";
+ private static const COLOR_MATRIX:String = "colorMatrix";
+ private static const CONVOLUTION:String = "convolution";
+ private static const DROP_SHADOW:String = "dropShadow";
+ private static const GLOW:String = "glow";
+ private static const GRADIENT_BEVEL:String = "gradientBevel";
+ private static const GRADIENT_GLOW:String = "gradientGlow";
+
+ private static const INSTRUCTIONS_TEXT:String = "Select a filter and the corresponding\nActionScript code will appear here.\n\nClick \"Apply filter and add another\"\nto see the effect of multiple filters\napplied in combination.";
+
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var filterPicker:ComboBox;
+ public var filterTargetPicker:ComboBox;
+ public var applyFilterBtn:Button;
+ public var imageContainer:ImageContainer;
+ public var codeDisplay:TextArea;
+
+ // Created in code
+ private var _filterFormContainer:Sprite;
+
+ // Filter Panels
+ private var _bevelPanel:BevelPanel;
+ private var _blurPanel:BlurPanel;
+ private var _colorMatrixPanel:ColorMatrixPanel;
+ private var _convolutionPanel:ConvolutionPanel;
+ private var _dropShadowPanel:DropShadowPanel;
+ private var _glowPanel:GlowPanel;
+ private var _gradientBevelPanel:GradientBevelPanel;
+ private var _gradientGlowPanel:GradientGlowPanel;
+
+ // Model-View-Controller
+ private var _controller:FilterWorkbenchController;
+
+ public function FilterWorkbench()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter panels
+ _bevelPanel = new BevelPanel();
+ _blurPanel = new BlurPanel();
+ _colorMatrixPanel = new ColorMatrixPanel();
+ _convolutionPanel = new ConvolutionPanel();
+ _dropShadowPanel = new DropShadowPanel();
+ _glowPanel = new GlowPanel();
+ _gradientBevelPanel = new GradientBevelPanel();
+ _gradientGlowPanel = new GradientGlowPanel();
+
+ // add the placeholder for the filter properties panels
+ _filterFormContainer = new Sprite();
+ _filterFormContainer.x = 30;
+ _filterFormContainer.y = 360;
+ addChild(_filterFormContainer);
+
+ // set up data and events for the controls
+ var filterList:DataProvider = new DataProvider();
+ filterList.addItem({label:"< None >", data:null});
+ filterList.addItem({label:"Bevel", data:BEVEL});
+ filterList.addItem({label:"Blur", data:BLUR});
+ filterList.addItem({label:"Color matrix", data:COLOR_MATRIX});
+ filterList.addItem({label:"Convolution", data:CONVOLUTION});
+ filterList.addItem({label:"Drop shadow", data:DROP_SHADOW});
+ filterList.addItem({label:"Glow", data:GLOW});
+ filterList.addItem({label:"Gradient bevel", data:GRADIENT_BEVEL});
+ filterList.addItem({label:"Gradient glow", data:GRADIENT_GLOW});
+ filterPicker.dataProvider = filterList;
+
+ filterPicker.addEventListener(Event.CHANGE, setFilter);
+
+ var imageList:DataProvider = new DataProvider(ImageType.getImageTypes());
+ filterTargetPicker.labelField = "name";
+ filterTargetPicker.dropdown.iconField = null;
+ filterTargetPicker.dataProvider = imageList;
+ filterTargetPicker.selectedIndex = 0;
+
+ filterTargetPicker.addEventListener(Event.CHANGE, setImage);
+
+ applyFilterBtn.enabled = false;
+ applyFilterBtn.addEventListener(MouseEvent.CLICK, applyFilter);
+
+ var codeFont:String = getCodeFont();
+
+ var codeFormat:TextFormat = new TextFormat(codeFont);
+ codeDisplay.setStyle("textFormat", codeFormat);
+ codeDisplay.text = INSTRUCTIONS_TEXT;
+
+ // create the controller for the app
+ _controller = new FilterWorkbenchController();
+ _controller.addEventListener(ProgressEvent.PROGRESS, imageLoadProgress);
+ _controller.addEventListener(Event.COMPLETE, imageLoadComplete);
+ _controller.addEventListener(Event.CHANGE, updateCode);
+
+ // load the first image
+ _controller.setFilterTarget(filterTargetPicker.selectedItem as ImageType);
+ }
+
+
+ private function setFilter(event:Event):void
+ {
+ setupSelectedFilter();
+ }
+
+
+ private function setImage(event:Event):void
+ {
+ var imageType:ImageType = filterTargetPicker.selectedItem as ImageType;
+
+ if (imageType == null) { return; }
+
+ // remove the previous image
+ if (imageContainer.hasImage)
+ {
+ imageContainer.removeImage();
+ }
+
+ // Show the progress bar in the mean time
+
+ // Start the image loading
+ _controller.setFilterTarget(imageType);
+
+ resetForm();
+ }
+
+
+ private function applyFilter(event:MouseEvent):void
+ {
+ if (filterPicker.selectedIndex > 0 )
+ {
+ _controller.applyFilter();
+ resetForm();
+ }
+ }
+
+
+ private function updateCode(event:Event):void
+ {
+ codeDisplay.text = _controller.getCode();
+ }
+
+
+ private function imageLoadProgress(event:ProgressEvent):void
+ {
+ // Update the progress bar
+ }
+
+
+ private function imageLoadComplete(event:Event):void
+ {
+ // Clear away the progress bar and display the image
+ var image:DisplayObject = _controller.getFilterTarget();
+ imageContainer.addImage(image);
+ }
+
+
+ // ------- Private methods -------
+ private function resetForm():void
+ {
+ filterPicker.selectedIndex = 0;
+ hideFilterForm();
+ applyFilterBtn.enabled = false;
+ }
+
+
+ private function hideFilterForm():void
+ {
+ if (_filterFormContainer.numChildren > 0)
+ {
+ _filterFormContainer.removeChildAt(0);
+ }
+ }
+
+
+ private function setupSelectedFilter():void
+ {
+ hideFilterForm();
+
+ var filterPanel:IFilterPanel;
+
+ if (filterPicker.selectedIndex <= 0)
+ {
+ applyFilterBtn.enabled = false;
+ _controller.setFilter(null);
+ return;
+ }
+
+ applyFilterBtn.enabled = true;
+
+ switch (filterPicker.selectedItem.data)
+ {
+ case BEVEL:
+ filterPanel = _bevelPanel;
+ break;
+ case BLUR:
+ filterPanel = _blurPanel;
+ break;
+ case COLOR_MATRIX:
+ filterPanel = _colorMatrixPanel;
+ break;
+ case CONVOLUTION:
+ filterPanel = _convolutionPanel;
+ break;
+ case DROP_SHADOW:
+ filterPanel = _dropShadowPanel;
+ break;
+ case GLOW:
+ filterPanel = _glowPanel;
+ break;
+ case GRADIENT_BEVEL:
+ filterPanel = _gradientBevelPanel;
+ break;
+ case GRADIENT_GLOW:
+ filterPanel = _gradientGlowPanel;
+ break;
+ }
+
+ var panelDO:DisplayObject;
+ if ((panelDO = filterPanel as DisplayObject) != null)
+ {
+ filterFormContainer.addChild(panelDO);
+ }
+ filterPanel.resetForm();
+ _controller.setFilter(filterPanel.filterFactory);
+ }
+
+
+ private function getCodeFont():String
+ {
+ // set the code font based on the user's machine.
+ // Priority order:
+ // - Andale Mono
+ // - Monaco
+ // - Courier New
+ // - Courier
+ // - _typewriter (one of Flash's defaults, not a real font)
+ var result:String;
+ var allFonts:Array = Font.enumerateFonts(true);
+ allFonts.sortOn("fontName", Array.CASEINSENSITIVE);
+
+ for each (var font:Font in allFonts)
+ {
+ if (font.fontName == "Andale Mono")
+ {
+ result = "Andale Mono";
+ break;
+ }
+ else if (font.fontName == "Monaco")
+ {
+ result = "Monaco";
+ break;
+ }
+ else if (font.fontName == "Courier New")
+ {
+ result = "Courier New";
+ }
+ else if (result == null && font.fontName == "Courier")
+ {
+ result = "Courier";
+ }
+ else if (font.fontName.substr(0, 1) > "M")
+ {
+ result = "_typewriter";
+ break;
+ }
+ result = "_typewriter";
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/ImageContainer.as b/FilterWorkbench/flashapp/ImageContainer.as
new file mode 100644
index 0000000..6999b67
--- /dev/null
+++ b/FilterWorkbench/flashapp/ImageContainer.as
@@ -0,0 +1,53 @@
+package flashapp
+{
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+
+ public class ImageContainer extends Sprite
+ {
+ private var _image:DisplayObject;
+ private var _container:Sprite;
+
+
+ // ------- Constructor -------
+ public function ImageContainer()
+ {
+ _container = new Sprite();
+ addChild(_container);
+ }
+
+
+ // ------- Public properties -------
+ public function get hasImage():Boolean
+ {
+ return (_image != null);
+ }
+
+
+ // ------- Public methods -------
+ public function addImage(image:DisplayObject):void
+ {
+ if (_image == image) { return; }
+
+ if (_image != null)
+ {
+ _container.removeChild(_image);
+ }
+
+ _container.addChild(image);
+ image.x = (width / 2) - (image.width / 2);
+ image.y = (height / 2) - (image.height / 2);
+
+ _image = image;
+ }
+
+
+ public function removeImage():void
+ {
+ if (_image == null) { return; }
+
+ _container.removeChild(_image);
+ _image = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/BevelPanel.as b/FilterWorkbench/flashapp/filterPanels/BevelPanel.as
new file mode 100644
index 0000000..eef7299
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/BevelPanel.as
@@ -0,0 +1,147 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboDial;
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.BevelFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.ColorPicker;
+ import fl.controls.ComboBox;
+ import fl.controls.TextInput;
+ import fl.controls.CheckBox;
+ import fl.data.DataProvider;
+ import fl.events.ColorPickerEvent;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.filters.BitmapFilterType;
+ import flash.display.Sprite;
+
+ public class BevelPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:BevelFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var strengthValue:ComboSlider;
+ public var qualityValue:ComboBox;
+ public var shadowColorValue:ColorPicker;
+ public var shadowAlphaValue:ComboSlider;
+ public var highlightColorValue:ColorPicker;
+ public var highlightAlphaValue:ComboSlider;
+ public var angleValue:ComboDial;
+ public var distanceValue:TextInput;
+ public var knockoutValue:CheckBox;
+ public var typeValue:ComboBox;
+
+
+ // ------- Constructor -------
+ public function BevelPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 4;
+ blurYValue.value = 4;
+ strengthValue.value = 1;
+ qualityValue.selectedIndex = 0;
+ shadowColorValue.selectedColor = 0x000000;
+ shadowAlphaValue.value = 1;
+ highlightColorValue.selectedColor = 0xFFFFFF;
+ highlightAlphaValue.value = 1;
+ angleValue.value = 45;
+ distanceValue.text = "4";
+ knockoutValue.selected = false;
+ typeValue.selectedIndex = 0;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new BevelFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // populate the type combo box
+ var typeList:DataProvider = new DataProvider();
+ typeList.addItem({label:"Inner", data:BitmapFilterType.INNER});
+ typeList.addItem({label:"Outer", data:BitmapFilterType.OUTER});
+ typeList.addItem({label:"Full", data:BitmapFilterType.FULL});
+ typeValue.dataProvider = typeList;
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ strengthValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ shadowColorValue.addEventListener(ColorPickerEvent.CHANGE, changeFilterValue);
+ shadowAlphaValue.addEventListener(Event.CHANGE, changeFilterValue);
+ highlightColorValue.addEventListener(ColorPickerEvent.CHANGE, changeFilterValue);
+ highlightAlphaValue.addEventListener(Event.CHANGE, changeFilterValue);
+ angleValue.addEventListener(Event.CHANGE, changeFilterValue);
+ distanceValue.addEventListener(Event.CHANGE, changeFilterValue);
+ knockoutValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ typeValue.addEventListener(Event.CHANGE, changeFilterValue);
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // verify that the values are valid
+ if (distanceValue.text.length <= 0) { return; }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var shadowColor:uint = shadowColorValue.selectedColor;
+ var shadowAlpha:Number = shadowAlphaValue.value;
+ var highlightColor:uint = highlightColorValue.selectedColor;
+ var highlightAlpha:Number = highlightAlphaValue.value;
+ var angle:Number = angleValue.value;
+ var distance:Number = Number(distanceValue.text);
+ var knockout:Boolean = knockoutValue.selected;
+ var type:String = typeValue.selectedItem.data;
+
+ _filterFactory.modifyFilter(distance, angle, highlightColor, highlightAlpha, shadowColor, shadowAlpha, blurX, blurY, strength, quality, type, knockout);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/BlurPanel.as b/FilterWorkbench/flashapp/filterPanels/BlurPanel.as
new file mode 100644
index 0000000..888051f
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/BlurPanel.as
@@ -0,0 +1,94 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.BlurFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.ComboBox;
+ import fl.data.DataProvider;
+
+ import flash.events.Event;
+ import flash.filters.BitmapFilterQuality;
+ import flash.display.Sprite;
+
+ public class BlurPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:BlurFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var qualityValue:ComboBox;
+
+
+ // ------- Constructor -------
+ public function BlurPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 4;
+ blurYValue.value = 4;
+ qualityValue.selectedIndex = 0;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new BlurFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+
+ _filterFactory.modifyFilter(blurX, blurY, quality);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/ColorMatrixPanel.as b/FilterWorkbench/flashapp/filterPanels/ColorMatrixPanel.as
new file mode 100644
index 0000000..800b282
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/ColorMatrixPanel.as
@@ -0,0 +1,203 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.ColorMatrixFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.Button;
+ import fl.controls.TextInput;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.display.Sprite;
+
+ public class ColorMatrixPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:ColorMatrixFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var brightnessValue:ComboSlider;
+ public var contrastValue:ComboSlider;
+ public var saturationValue:ComboSlider;
+ public var hueValue:ComboSlider;
+ public var m0:TextInput;
+ public var m1:TextInput;
+ public var m2:TextInput;
+ public var m3:TextInput;
+ public var m4:TextInput;
+ public var m5:TextInput;
+ public var m6:TextInput;
+ public var m7:TextInput;
+ public var m8:TextInput;
+ public var m9:TextInput;
+ public var m10:TextInput;
+ public var m11:TextInput;
+ public var m12:TextInput;
+ public var m13:TextInput;
+ public var m14:TextInput;
+ public var m15:TextInput;
+ public var m16:TextInput;
+ public var m17:TextInput;
+ public var m18:TextInput;
+ public var m19:TextInput;
+ public var resetButton:Button;
+
+
+ // ------- Constructor -------
+ public function ColorMatrixPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ setMatrixForm([1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0]);
+
+ brightnessValue.value = 0;
+ contrastValue.value = 0;
+ saturationValue.value = 0;
+ hueValue.value = 0;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilterCustom();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new ColorMatrixFactory();
+
+ // add event listeners for child controls
+ brightnessValue.addEventListener(Event.CHANGE, changePreset);
+ contrastValue.addEventListener(Event.CHANGE, changePreset);
+ saturationValue.addEventListener(Event.CHANGE, changePreset);
+ hueValue.addEventListener(Event.CHANGE, changePreset);
+
+ m0.addEventListener(Event.CHANGE, changeFilterValue);
+ m1.addEventListener(Event.CHANGE, changeFilterValue);
+ m2.addEventListener(Event.CHANGE, changeFilterValue);
+ m3.addEventListener(Event.CHANGE, changeFilterValue);
+ m4.addEventListener(Event.CHANGE, changeFilterValue);
+ m5.addEventListener(Event.CHANGE, changeFilterValue);
+ m6.addEventListener(Event.CHANGE, changeFilterValue);
+ m7.addEventListener(Event.CHANGE, changeFilterValue);
+ m8.addEventListener(Event.CHANGE, changeFilterValue);
+ m9.addEventListener(Event.CHANGE, changeFilterValue);
+ m10.addEventListener(Event.CHANGE, changeFilterValue);
+ m11.addEventListener(Event.CHANGE, changeFilterValue);
+ m12.addEventListener(Event.CHANGE, changeFilterValue);
+ m13.addEventListener(Event.CHANGE, changeFilterValue);
+ m14.addEventListener(Event.CHANGE, changeFilterValue);
+ m15.addEventListener(Event.CHANGE, changeFilterValue);
+ m16.addEventListener(Event.CHANGE, changeFilterValue);
+ m17.addEventListener(Event.CHANGE, changeFilterValue);
+ m18.addEventListener(Event.CHANGE, changeFilterValue);
+ m19.addEventListener(Event.CHANGE, changeFilterValue);
+
+ resetButton.addEventListener(MouseEvent.CLICK, resetClick);
+ }
+
+
+ private function changePreset(event:Event):void
+ {
+ // update the filter
+ _filterFactory.modifyFilterBasic(brightnessValue.value, contrastValue.value, saturationValue.value, hueValue.value);
+
+ // populate the form values with the new matrix
+ setMatrixForm(_filterFactory.matrix);
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // verify that the values are valid
+ if (m0.text.length <= 0) { return; }
+ if (m1.text.length <= 0) { return; }
+ if (m2.text.length <= 0) { return; }
+ if (m3.text.length <= 0) { return; }
+ if (m4.text.length <= 0) { return; }
+ if (m5.text.length <= 0) { return; }
+ if (m6.text.length <= 0) { return; }
+ if (m7.text.length <= 0) { return; }
+ if (m8.text.length <= 0) { return; }
+ if (m9.text.length <= 0) { return; }
+ if (m10.text.length <= 0) { return; }
+ if (m11.text.length <= 0) { return; }
+ if (m12.text.length <= 0) { return; }
+ if (m13.text.length <= 0) { return; }
+ if (m14.text.length <= 0) { return; }
+ if (m15.text.length <= 0) { return; }
+ if (m16.text.length <= 0) { return; }
+ if (m17.text.length <= 0) { return; }
+ if (m18.text.length <= 0) { return; }
+ if (m19.text.length <= 0) { return; }
+
+ // reset the brightness/contrast/saturation/hue controls
+ brightnessValue.value = 0;
+ contrastValue.value = 0;
+ saturationValue.value = 0;
+ hueValue.value = 0;
+
+ // update the filter
+ var matrix:Array = [Number(m0.text), Number(m1.text), Number(m2.text), Number(m3.text), Number(m4.text),
+ Number(m5.text), Number(m6.text), Number(m7.text), Number(m8.text), Number(m9.text),
+ Number(m10.text), Number(m11.text), Number(m12.text), Number(m13.text), Number(m14.text),
+ Number(m15.text), Number(m16.text), Number(m17.text), Number(m18.text), Number(m19.text)];
+
+ _filterFactory.modifyFilterCustom(matrix);
+ }
+
+
+ private function resetClick(event:MouseEvent):void
+ {
+ resetForm();
+ }
+
+
+ // ------- Utility methods -------
+ private function setMatrixForm(matrix:Array):void
+ {
+ m0.text = matrix[0].toString();
+ m1.text = matrix[1].toString();
+ m2.text = matrix[2].toString();
+ m3.text = matrix[3].toString();
+ m4.text = matrix[4].toString();
+ m5.text = matrix[5].toString();
+ m6.text = matrix[6].toString();
+ m7.text = matrix[7].toString();
+ m8.text = matrix[8].toString();
+ m9.text = matrix[9].toString();
+ m10.text = matrix[10].toString();
+ m11.text = matrix[11].toString();
+ m12.text = matrix[12].toString();
+ m13.text = matrix[13].toString();
+ m14.text = matrix[14].toString();
+ m15.text = matrix[15].toString();
+ m16.text = matrix[16].toString();
+ m17.text = matrix[17].toString();
+ m18.text = matrix[18].toString();
+ m19.text = matrix[19].toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/ConvolutionPanel.as b/FilterWorkbench/flashapp/filterPanels/ConvolutionPanel.as
new file mode 100644
index 0000000..b55e4d9
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/ConvolutionPanel.as
@@ -0,0 +1,251 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.ConvolutionFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.ComboBox;
+ import fl.controls.Label;
+ import fl.controls.TextInput;
+ import fl.controls.ColorPicker;
+ import fl.controls.CheckBox;
+ import fl.data.DataProvider;
+ import fl.events.ColorPickerEvent;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.display.Sprite;
+
+ public class ConvolutionPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Constants -------
+ public const matrixXValue:Number = 3;
+ public const matrixYValue:Number = 3;
+
+ // --- Presets ---
+ private static const NONE:String = "none";
+ private static const SHIFT_LEFT:String = "shiftLeft";
+ private static const SHIFT_UP:String = "shiftUp";
+ private static const BLUR:String = "blur";
+ private static const ENHANCE:String = "enhance";
+ private static const SHARPEN:String = "sharpen";
+ private static const CONTRAST:String = "contrast";
+ private static const EMBOSS:String = "emboss";
+ private static const EDGE_DETECT:String = "edge";
+ private static const HORIZONTAL_EDGE:String = "hEdge";
+ private static const VERTICAL_EDGE:String = "vEdge";
+
+
+ // ------- Private vars -------
+ private var _filterFactory:ConvolutionFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var presetChooser:ComboBox;
+ public var matrixTL:TextInput;
+ public var matrixTC:TextInput;
+ public var matrixTR:TextInput;
+ public var matrixML:TextInput;
+ public var matrixMC:TextInput;
+ public var matrixMR:TextInput;
+ public var matrixBL:TextInput;
+ public var matrixBC:TextInput;
+ public var matrixBR:TextInput;
+ public var divisorValue:TextInput;
+ public var colorValue:ColorPicker;
+ public var alphaValue:ComboSlider;
+ public var biasValue:TextInput;
+ public var clampValue:CheckBox;
+ public var preserveAlphaValue:CheckBox;
+
+
+ // ------- Constructor -------
+ public function ConvolutionPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ setPreset("0", "0", "0", "0", "1", "0", "0", "0", "0", "1", "0", true);
+ alphaValue.value = 0;
+ clampValue.selected = true;
+ colorValue.selectedColor = 0x000000;
+ presetChooser.selectedIndex = -1;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new ConvolutionFactory();
+
+ // populate the preset chooser combo box
+ var presetList:DataProvider = new DataProvider();
+ presetList.addItem({label:"None", data:NONE});
+ presetList.addItem({label:"Shift pixels left", data:SHIFT_LEFT});
+ presetList.addItem({label:"Shift pixels up", data:SHIFT_UP});
+ presetList.addItem({label:"Blur", data:BLUR});
+ presetList.addItem({label:"Enhance", data:ENHANCE});
+ presetList.addItem({label:"Sharpen", data:SHARPEN});
+ presetList.addItem({label:"Add Contrast", data:CONTRAST});
+ presetList.addItem({label:"Emboss", data:EMBOSS});
+ presetList.addItem({label:"Edge detect", data:EDGE_DETECT});
+ presetList.addItem({label:"Horizontal edge detect", data:HORIZONTAL_EDGE});
+ presetList.addItem({label:"Vertical edge detect", data:VERTICAL_EDGE});
+ presetChooser.dataProvider = presetList;
+
+ // add event listeners for child controls
+ presetChooser.addEventListener(Event.CHANGE, choosePreset);
+
+ matrixTL.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixTC.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixTR.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixML.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixMC.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixMR.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixBL.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixBC.addEventListener(Event.CHANGE, changeFilterValue);
+ matrixBR.addEventListener(Event.CHANGE, changeFilterValue);
+ divisorValue.addEventListener(Event.CHANGE, changeFilterValue);
+ biasValue.addEventListener(Event.CHANGE, changeFilterValue);
+ colorValue.addEventListener(ColorPickerEvent.CHANGE, changeNonPresetValue);
+ alphaValue.addEventListener(Event.CHANGE, changeNonPresetValue);
+ clampValue.addEventListener(MouseEvent.CLICK, changeNonPresetValue);
+ preserveAlphaValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ }
+
+
+ private function choosePreset(event:Event):void
+ {
+ // populate the form values according to the selected preset
+ switch (presetChooser.selectedItem.data)
+ {
+ case NONE:
+ setPreset("0", "0", "0", "0", "1", "0", "0", "0", "0", "1", "0", preserveAlphaValue.selected);
+ break;
+ case SHIFT_LEFT:
+ setPreset("0", "0", "0", "0", "0", "1", "0", "0", "0", "1", "0", preserveAlphaValue.selected);
+ break;
+ case SHIFT_UP:
+ setPreset("0", "0", "0", "0", "0", "0", "0", "1", "0", "1", "0", preserveAlphaValue.selected);
+ break;
+ case BLUR:
+ setPreset("0", "1", "0", "1", "1", "1", "0", "1", "0", "5", "0", preserveAlphaValue.selected);
+ break;
+ case ENHANCE:
+ setPreset("0", "-2", "0", "-2", "20", "-2", "0", "-2", "0", "10", "-40", preserveAlphaValue.selected);
+ break;
+ case SHARPEN:
+ setPreset("0", "-1", "0", "-1", "5", "-1", "0", "-1", "0", "1", "0", preserveAlphaValue.selected);
+ break;
+ case CONTRAST:
+ setPreset("0", "0", "0", "0", "2", "0", "0", "0", "0", "1", "-255", preserveAlphaValue.selected);
+ break;
+ case EMBOSS:
+ setPreset("-2", "-1", "0", "-1", "1", "1", "0", "1", "2", "1", "0", preserveAlphaValue.selected);
+ break;
+ case EDGE_DETECT:
+ setPreset("0", "-1", "0", "-1", "4", "-1", "0", "-1", "0", "1", "0", true);
+ break;
+ case HORIZONTAL_EDGE:
+ setPreset("0", "0", "0", "-1", "1", "0", "0", "0", "0", "1", "0", true);
+ break;
+ case VERTICAL_EDGE:
+ setPreset("0", "-1", "0", "0", "1", "0", "0", "0", "0", "1", "0", true);
+ break;
+ }
+
+ updateFilter();
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // verify that the values are valid
+ if (matrixTL.text.length <= 0) { return; }
+ if (matrixTC.text.length <= 0) { return; }
+ if (matrixTR.text.length <= 0) { return; }
+ if (matrixML.text.length <= 0) { return; }
+ if (matrixMC.text.length <= 0) { return; }
+ if (matrixMR.text.length <= 0) { return; }
+ if (matrixBL.text.length <= 0) { return; }
+ if (matrixBC.text.length <= 0) { return; }
+ if (matrixBR.text.length <= 0) { return; }
+ if (divisorValue.text.length <= 0) { return; }
+ if (biasValue.text.length <= 0) { return; }
+
+ // clear the presets drop-down
+ presetChooser.selectedIndex = -1;
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function changeNonPresetValue(event:Event):void
+ {
+ // update the filter, but don't clear the preset
+ updateFilter();
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var alpha:Number = alphaValue.value;
+ var bias:Number = Number(biasValue.text);
+ var clamp:Boolean = clampValue.selected;
+ var color:uint = colorValue.selectedColor;
+ var divisor:Number = Number(divisorValue.text);
+ var matrix:Array = [Number(matrixTL.text), Number(matrixTC.text), Number(matrixTR.text),
+ Number(matrixML.text), Number(matrixMC.text), Number(matrixMR.text),
+ Number(matrixBL.text), Number(matrixBC.text), Number(matrixBR.text)];
+ var matrixX:Number = matrixXValue;
+ var matrixY:Number = matrixYValue;
+ var preserveAlpha:Boolean = preserveAlphaValue.selected;
+
+ _filterFactory.modifyFilter(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha);
+ }
+
+
+ private function setPreset(tl:String, tc:String, tr:String,
+ ml:String, mc:String, mr:String,
+ bl:String, bc:String, br:String,
+ divisor:String,
+ bias:String,
+ preserveAlpha:Boolean):void
+ {
+ matrixTL.text = tl;
+ matrixTC.text = tc;
+ matrixTR.text = tr;
+ matrixML.text = ml;
+ matrixMC.text = mc;
+ matrixMR.text = mr;
+ matrixBL.text = bl;
+ matrixBC.text = bc;
+ matrixBR.text = br;
+ divisorValue.text = divisor;
+ biasValue.text = bias;
+ preserveAlphaValue.selected = preserveAlpha;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/DropShadowPanel.as b/FilterWorkbench/flashapp/filterPanels/DropShadowPanel.as
new file mode 100644
index 0000000..898e8f0
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/DropShadowPanel.as
@@ -0,0 +1,135 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboSlider;
+ import com.adobe.examples.flash.ComboDial;
+ import com.example.programmingas3.filterWorkbench.DropShadowFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.ColorPicker;
+ import fl.controls.ComboBox;
+ import fl.controls.TextInput;
+ import fl.controls.CheckBox;
+ import fl.data.DataProvider;
+ import fl.events.ColorPickerEvent;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.display.Sprite;
+
+ public class DropShadowPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:DropShadowFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var strengthValue:ComboSlider;
+ public var qualityValue:ComboBox;
+ public var colorValue:ColorPicker;
+ public var alphaValue:ComboSlider;
+ public var angleValue:ComboDial;
+ public var distanceValue:TextInput;
+ public var knockoutValue:CheckBox;
+ public var innerValue:CheckBox;
+ public var hideObjectValue:CheckBox
+
+
+ // ------- Constructor -------
+ public function DropShadowPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 4;
+ blurYValue.value = 4;
+ strengthValue.value = 1;
+ qualityValue.selectedIndex = 0;
+ colorValue.selectedColor = 0x000000;
+ alphaValue.value = 1;
+ angleValue.value = 45;
+ distanceValue.text = "4";
+ knockoutValue.selected = false;
+ innerValue.selected = false;
+ hideObjectValue.selected = false;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new DropShadowFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ strengthValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ colorValue.addEventListener(ColorPickerEvent.CHANGE, changeFilterValue);
+ alphaValue.addEventListener(Event.CHANGE, changeFilterValue);
+ angleValue.addEventListener(Event.CHANGE, changeFilterValue);
+ distanceValue.addEventListener(Event.CHANGE, changeFilterValue);
+ knockoutValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ innerValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ hideObjectValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // verify that the values are valid
+ if (distanceValue.text.length <= 0) { return; }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var color:uint = colorValue.selectedColor;
+ var alpha:Number = alphaValue.value;
+ var angle:Number = angleValue.value;
+ var distance:Number = Number(distanceValue.text);
+ var knockout:Boolean = knockoutValue.selected;
+ var inner:Boolean = innerValue.selected;
+ var hideObject:Boolean = hideObjectValue.selected;
+
+ _filterFactory.modifyFilter(distance, angle, color, alpha, blurX, blurY, strength, quality, inner, knockout, hideObject);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/GlowPanel.as b/FilterWorkbench/flashapp/filterPanels/GlowPanel.as
new file mode 100644
index 0000000..0a5edc1
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/GlowPanel.as
@@ -0,0 +1,119 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.GlowFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.ColorPicker;
+ import fl.controls.ComboBox;
+ import fl.controls.NumericStepper;
+ import fl.controls.CheckBox;
+ import fl.data.DataProvider;
+ import fl.events.ColorPickerEvent;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.display.Sprite;
+
+ public class GlowPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:GlowFactory;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var strengthValue:ComboSlider;
+ public var qualityValue:ComboBox;
+ public var colorValue:ColorPicker;
+ public var alphaValue:ComboSlider;
+ public var knockoutValue:CheckBox;
+ public var innerValue:CheckBox;
+
+
+ // ------- Constructor -------
+ public function GlowPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 6;
+ blurYValue.value = 6;
+ strengthValue.value = 2;
+ qualityValue.selectedIndex = 0;
+ colorValue.selectedColor = 0xFF0000;
+ alphaValue.value = 1;
+ knockoutValue.selected = false;
+ innerValue.selected = false;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new GlowFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ strengthValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ colorValue.addEventListener(ColorPickerEvent.CHANGE, changeFilterValue);
+ alphaValue.addEventListener(Event.CHANGE, changeFilterValue);
+ knockoutValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ innerValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var color:uint = colorValue.selectedColor;
+ var alpha:Number = alphaValue.value;
+ var knockout:Boolean = knockoutValue.selected;
+ var inner:Boolean = innerValue.selected;
+
+ _filterFactory.modifyFilter(color, alpha, blurX, blurY, strength, quality, inner, knockout);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/GradientBevelPanel.as b/FilterWorkbench/flashapp/filterPanels/GradientBevelPanel.as
new file mode 100644
index 0000000..cd2d161
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/GradientBevelPanel.as
@@ -0,0 +1,305 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboDial;
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.GradientBevelFactory;
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.Button;
+ import fl.controls.ComboBox;
+ import fl.controls.CheckBox;
+ import fl.controls.ColorPicker;
+ import fl.controls.DataGrid;
+ import fl.controls.dataGridClasses.DataGridColumn;
+ import fl.data.DataProvider;
+ import fl.events.DataGridEvent;
+ import fl.events.DataGridEventReason;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.filters.BitmapFilterType;
+ import flash.display.Sprite;
+
+ import flashapp.BGColorCellRenderer;
+ import flashapp.ButtonCellRenderer;
+
+ public class GradientBevelPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:GradientBevelFactory;
+
+ private var _editValue:Number;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var strengthValue:ComboSlider;
+ public var qualityValue:ComboBox;
+ public var angleValue:ComboDial;
+ public var distanceValue:ComboSlider;
+ public var knockoutValue:CheckBox;
+ public var typeValue:ComboBox;
+ public var gradientValues:DataGrid;
+ public var addColorValue:ColorPicker;
+ public var addAlphaValue:ComboSlider;
+ public var addRatioValue:ComboSlider;
+ public var addGradientBtn:Button;
+
+
+ // ------- Constructor -------
+ public function GradientBevelPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 4;
+ blurYValue.value = 4;
+ strengthValue.value = 1;
+ qualityValue.selectedIndex = 0;
+ angleValue.value = 45;
+ distanceValue.value = 4;
+ knockoutValue.selected = false;
+ typeValue.selectedIndex = 0;
+ gradientValues.dataProvider = getDefaultGradientValues();
+ addColorValue.selectedColor = 0x000000;
+ addAlphaValue.value = 1;
+ addRatioValue.value = 128;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new GradientBevelFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // populate the type combo box
+ var typeList:DataProvider = new DataProvider();
+ typeList.addItem({label:"Inner", data:BitmapFilterType.INNER});
+ typeList.addItem({label:"Outer", data:BitmapFilterType.OUTER});
+ typeList.addItem({label:"Full", data:BitmapFilterType.FULL});
+ typeValue.dataProvider = typeList;
+
+ // populate the gradient colors grid
+ gradientValues.dataProvider = getDefaultGradientValues();
+ var colorColumn:DataGridColumn = new DataGridColumn("Colors");
+ colorColumn.cellRenderer = BGColorCellRenderer;
+ colorColumn.dataField = "color";
+ colorColumn.labelFunction = colorFormatter;
+ colorColumn.resizable = false;
+ colorColumn.width = 70;
+ gradientValues.addColumn(colorColumn);
+ var alphaColumn:DataGridColumn = new DataGridColumn("Alphas");
+ alphaColumn.cellRenderer = BGColorCellRenderer;
+ alphaColumn.dataField = "alpha";
+ alphaColumn.resizable = false;
+ alphaColumn.width = 55;
+ gradientValues.addColumn(alphaColumn);
+ var ratioColumn:DataGridColumn = new DataGridColumn("Ratios");
+ ratioColumn.cellRenderer = BGColorCellRenderer;
+ ratioColumn.dataField = "ratio";
+ ratioColumn.resizable = false;
+ ratioColumn.sortOptions = Array.NUMERIC;
+ ratioColumn.width = 55;
+ gradientValues.addColumn(ratioColumn);
+ var deleteColumn:DataGridColumn = new DataGridColumn(" ");
+ deleteColumn.cellRenderer = ButtonCellRenderer;
+ deleteColumn.labelFunction = deleteButtonLabelFunction;
+ deleteColumn.editable = false;
+ gradientValues.addColumn(deleteColumn);
+
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ strengthValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ angleValue.addEventListener(Event.CHANGE, changeFilterValue);
+ distanceValue.addEventListener(Event.CHANGE, changeFilterValue);
+ knockoutValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ typeValue.addEventListener(Event.CHANGE, changeFilterValue);
+
+ addGradientBtn.addEventListener(MouseEvent.CLICK, addNewGradient);
+
+ // This one gets called before the datagrid updates the dataprovider (which happens in this same
+ // event, with priority -20).
+ gradientValues.addEventListener(DataGridEvent.ITEM_EDIT_END, editPreEnd, false, 100);
+ // This one gets called after the datagrid updates the dataprovider.
+ gradientValues.addEventListener(DataGridEvent.ITEM_EDIT_END, editPostEnd, false, -100);
+
+ // Click handler for datagrid, to detect when the delete button is pressed
+ gradientValues.addEventListener(MouseEvent.CLICK, checkForItemDelete);
+ }
+
+
+ // Called after editing ends in a datagrid cell, but before the dataprovider is updated.
+ private function editPreEnd(event:DataGridEvent):void
+ {
+ if (event.reason == DataGridEventReason.CANCELLED)
+ {
+ return;
+ }
+ // save a snapshot of the pre-change data
+ _editValue = gradientValues.dataProvider.getItemAt(Number(event.rowIndex))[event.dataField];
+ }
+
+
+ // Called after editing ends in a datagrid cell, and after the dataprovider is updated.
+ private function editPostEnd(event:DataGridEvent):void
+ {
+ if (event.reason == DataGridEventReason.CANCELLED)
+ {
+ return;
+ }
+
+ var index:Number = Number(event.rowIndex);
+ var data:GradientColor = gradientValues.dataProvider.getItemAt(index) as GradientColor;
+ var newValue:Number = data[event.dataField];
+ if (newValue == _editValue)
+ {
+ return;
+ }
+ // validate the changed data
+ var needToRevert:Boolean = false;
+
+ if (isNaN(newValue))
+ {
+ needToRevert = true;
+ }
+ else
+ {
+ switch (event.dataField)
+ {
+ case "color":
+ if (newValue < 0) { needToRevert = true; }
+ break;
+ case "alpha":
+ if (newValue > 1 || newValue < 0) { needToRevert = true; }
+ break;
+ case "ratio":
+ if (newValue > 255 || newValue < 0) { needToRevert = true; }
+ break;
+ }
+ }
+
+ if (needToRevert)
+ {
+ data[event.dataField] = _editValue;
+ gradientValues.dataProvider.replaceItemAt(data, index);
+ return;
+ }
+
+ // resort the data if the ratio changed
+ if (event.dataField == "ratio")
+ {
+ gradientValues.dataProvider.sortOn("ratio", Array.NUMERIC);
+ }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function checkForItemDelete(event:MouseEvent):void
+ {
+ var cell:ButtonCellRenderer = event.target as ButtonCellRenderer;
+ if (cell != null)
+ {
+ gradientValues.dataProvider.removeItem(cell.data);
+ updateFilter();
+ }
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function addNewGradient(event:MouseEvent):void
+ {
+ var gradient:GradientColor = new GradientColor(addColorValue.selectedColor, addAlphaValue.value, addRatioValue.value);
+ gradientValues.dataProvider.addItem(gradient);
+ gradientValues.dataProvider.sortOn("ratio", Array.NUMERIC);
+ updateFilter();
+ }
+
+
+ // ------- DataGrid utility methods -------
+ private function colorFormatter(data:Object):String
+ {
+ var c:GradientColor = data as GradientColor;
+ if (c != null)
+ {
+ return ColorStringFormatter.formatColorHex24(c.color);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ private function deleteButtonLabelFunction(data:Object):String
+ {
+ return "Delete";
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var angle:Number = angleValue.value;
+ var distance:Number = distanceValue.value;
+ var knockout:Boolean = knockoutValue.selected;
+ var type:String = typeValue.selectedItem.data;
+ var colors:Array = gradientValues.dataProvider.toArray();
+
+ _filterFactory.modifyFilter(distance, angle, colors, blurX, blurY, strength, quality, type, knockout);
+ }
+
+
+ private static function getDefaultGradientValues():DataProvider
+ {
+ return new DataProvider([new GradientColor(0xFFFFFF, 1, 0),
+ new GradientColor(0xFF0000, .25, 128),
+ new GradientColor(0x000000, 1, 255)]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flashapp/filterPanels/GradientGlowPanel.as b/FilterWorkbench/flashapp/filterPanels/GradientGlowPanel.as
new file mode 100644
index 0000000..c6160f3
--- /dev/null
+++ b/FilterWorkbench/flashapp/filterPanels/GradientGlowPanel.as
@@ -0,0 +1,305 @@
+package flashapp.filterPanels
+{
+ import com.adobe.examples.flash.ComboDial;
+ import com.adobe.examples.flash.ComboSlider;
+ import com.example.programmingas3.filterWorkbench.ColorStringFormatter;
+ import com.example.programmingas3.filterWorkbench.GradientGlowFactory;
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+ import com.example.programmingas3.filterWorkbench.IFilterFactory;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+
+ import fl.controls.Button;
+ import fl.controls.ComboBox;
+ import fl.controls.CheckBox;
+ import fl.controls.ColorPicker;
+ import fl.controls.DataGrid;
+ import fl.controls.dataGridClasses.DataGridColumn;
+ import fl.data.DataProvider;
+ import fl.events.DataGridEvent;
+ import fl.events.DataGridEventReason;
+
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.filters.BitmapFilterType;
+ import flash.display.Sprite;
+
+ import flashapp.BGColorCellRenderer;
+ import flashapp.ButtonCellRenderer;
+
+ public class GradientGlowPanel extends Sprite implements IFilterPanel
+ {
+ // ------- Private vars -------
+ private var _filterFactory:GradientGlowFactory;
+
+ private var _editValue:Number;
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.fla
+ public var blurXValue:ComboSlider;
+ public var blurYValue:ComboSlider;
+ public var strengthValue:ComboSlider;
+ public var qualityValue:ComboBox;
+ public var angleValue:ComboDial;
+ public var distanceValue:ComboSlider;
+ public var knockoutValue:CheckBox;
+ public var typeValue:ComboBox;
+ public var gradientValues:DataGrid;
+ public var addColorValue:ColorPicker;
+ public var addAlphaValue:ComboSlider;
+ public var addRatioValue:ComboSlider;
+ public var addGradientBtn:Button;
+
+
+ // ------- Constructor -------
+ public function GradientGlowPanel()
+ {
+ addEventListener(Event.ADDED, setupChildren);
+ }
+
+
+ // ------- Public Properties -------
+ public function get filterFactory():IFilterFactory
+ {
+ return _filterFactory;
+ }
+
+
+ // ------- Public methods -------
+ public function resetForm():void
+ {
+ blurXValue.value = 4;
+ blurYValue.value = 4;
+ strengthValue.value = 1;
+ qualityValue.selectedIndex = 0;
+ angleValue.value = 45;
+ distanceValue.value = 4;
+ knockoutValue.selected = false;
+ typeValue.selectedIndex = 0;
+ gradientValues.dataProvider = getDefaultGradientValues();
+ addColorValue.selectedColor = 0x000000;
+ addAlphaValue.value = 1;
+ addRatioValue.value = 128;
+
+ if (_filterFactory != null)
+ {
+ _filterFactory.modifyFilter();
+ }
+ }
+
+
+ // ------- Event Handling -------
+ private function setupChildren(event:Event):void
+ {
+ removeEventListener(Event.ADDED, setupChildren);
+
+ // create the filter factory
+ _filterFactory = new GradientGlowFactory();
+
+ // populate the quality combo box
+ var qualityList:DataProvider = new DataProvider();
+ qualityList.addItem({label:"Low", data:BitmapFilterQuality.LOW});
+ qualityList.addItem({label:"Medium", data:BitmapFilterQuality.MEDIUM});
+ qualityList.addItem({label:"High", data:BitmapFilterQuality.HIGH});
+ qualityValue.dataProvider = qualityList;
+
+ // populate the type combo box
+ var typeList:DataProvider = new DataProvider();
+ typeList.addItem({label:"Inner", data:BitmapFilterType.INNER});
+ typeList.addItem({label:"Outer", data:BitmapFilterType.OUTER});
+ typeList.addItem({label:"Full", data:BitmapFilterType.FULL});
+ typeValue.dataProvider = typeList;
+
+ // populate the gradient colors grid
+ gradientValues.dataProvider = getDefaultGradientValues();
+ var colorColumn:DataGridColumn = new DataGridColumn("Colors");
+ colorColumn.cellRenderer = BGColorCellRenderer;
+ colorColumn.dataField = "color";
+ colorColumn.labelFunction = colorFormatter;
+ colorColumn.resizable = false;
+ colorColumn.width = 70;
+ gradientValues.addColumn(colorColumn);
+ var alphaColumn:DataGridColumn = new DataGridColumn("Alphas");
+ alphaColumn.cellRenderer = BGColorCellRenderer;
+ alphaColumn.dataField = "alpha";
+ alphaColumn.resizable = false;
+ alphaColumn.width = 55;
+ gradientValues.addColumn(alphaColumn);
+ var ratioColumn:DataGridColumn = new DataGridColumn("Ratios");
+ ratioColumn.cellRenderer = BGColorCellRenderer;
+ ratioColumn.dataField = "ratio";
+ ratioColumn.resizable = false;
+ ratioColumn.sortOptions = Array.NUMERIC;
+ ratioColumn.width = 55;
+ gradientValues.addColumn(ratioColumn);
+ var deleteColumn:DataGridColumn = new DataGridColumn(" ");
+ deleteColumn.cellRenderer = ButtonCellRenderer;
+ deleteColumn.labelFunction = deleteButtonLabelFunction;
+ deleteColumn.editable = false;
+ gradientValues.addColumn(deleteColumn);
+
+
+ // add event listeners for child controls
+ blurXValue.addEventListener(Event.CHANGE, changeFilterValue);
+ blurYValue.addEventListener(Event.CHANGE, changeFilterValue);
+ strengthValue.addEventListener(Event.CHANGE, changeFilterValue);
+ qualityValue.addEventListener(Event.CHANGE, changeFilterValue);
+ angleValue.addEventListener(Event.CHANGE, changeFilterValue);
+ distanceValue.addEventListener(Event.CHANGE, changeFilterValue);
+ knockoutValue.addEventListener(MouseEvent.CLICK, changeFilterValue);
+ typeValue.addEventListener(Event.CHANGE, changeFilterValue);
+
+ addGradientBtn.addEventListener(MouseEvent.CLICK, addNewGradient);
+
+ // This one gets called before the datagrid updates the dataprovider (which happens in this same
+ // event, with priority -20).
+ gradientValues.addEventListener(DataGridEvent.ITEM_EDIT_END, editPreEnd, false, 100);
+ // This one gets called after the datagrid updates the dataprovider.
+ gradientValues.addEventListener(DataGridEvent.ITEM_EDIT_END, editPostEnd, false, -100);
+
+ // Click handler for datagrid, to detect when the delete button is pressed
+ gradientValues.addEventListener(MouseEvent.CLICK, checkForItemDelete);
+ }
+
+
+ // Called after editing ends in a datagrid cell, but before the dataprovider is updated.
+ private function editPreEnd(event:DataGridEvent):void
+ {
+ if (event.reason == DataGridEventReason.CANCELLED)
+ {
+ return;
+ }
+ // save a snapshot of the pre-change data
+ _editValue = gradientValues.dataProvider.getItemAt(Number(event.rowIndex))[event.dataField];
+ }
+
+
+ // Called after editing ends in a datagrid cell, and after the dataprovider is updated.
+ private function editPostEnd(event:DataGridEvent):void
+ {
+ if (event.reason == DataGridEventReason.CANCELLED)
+ {
+ return;
+ }
+
+ var index:Number = Number(event.rowIndex);
+ var data:GradientColor = gradientValues.dataProvider.getItemAt(index) as GradientColor;
+ var newValue:Number = data[event.dataField];
+ if (newValue == _editValue)
+ {
+ return;
+ }
+ // validate the changed data
+ var needToRevert:Boolean = false;
+
+ if (isNaN(newValue))
+ {
+ needToRevert = true;
+ }
+ else
+ {
+ switch (event.dataField)
+ {
+ case "color":
+ if (newValue < 0) { needToRevert = true; }
+ break;
+ case "alpha":
+ if (newValue > 1 || newValue < 0) { needToRevert = true; }
+ break;
+ case "ratio":
+ if (newValue > 255 || newValue < 0) { needToRevert = true; }
+ break;
+ }
+ }
+
+ if (needToRevert)
+ {
+ data[event.dataField] = _editValue;
+ gradientValues.dataProvider.replaceItemAt(data, index);
+ return;
+ }
+
+ // resort the data if the ratio changed
+ if (event.dataField == "ratio")
+ {
+ gradientValues.dataProvider.sortOn("ratio", Array.NUMERIC);
+ }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function checkForItemDelete(event:MouseEvent):void
+ {
+ var cell:ButtonCellRenderer = event.target as ButtonCellRenderer;
+ if (cell != null)
+ {
+ gradientValues.dataProvider.removeItem(cell.data);
+ updateFilter();
+ }
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function addNewGradient(event:MouseEvent):void
+ {
+ var gradient:GradientColor = new GradientColor(addColorValue.selectedColor, addAlphaValue.value, addRatioValue.value);
+ gradientValues.dataProvider.addItem(gradient);
+ gradientValues.dataProvider.sortOn("ratio", Array.NUMERIC);
+ updateFilter();
+ }
+
+
+ // ------- DataGrid utility methods -------
+ private function colorFormatter(data:Object):String
+ {
+ var c:GradientColor = data as GradientColor;
+ if (c != null)
+ {
+ return ColorStringFormatter.formatColorHex24(c.color);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ private function deleteButtonLabelFunction(data:Object):String
+ {
+ return "Delete";
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var angle:Number = angleValue.value;
+ var distance:Number = distanceValue.value;
+ var knockout:Boolean = knockoutValue.selected;
+ var type:String = typeValue.selectedItem.data;
+ var colors:Array = gradientValues.dataProvider.toArray();
+
+ _filterFactory.modifyFilter(distance, angle, colors, blurX, blurY, strength, quality, type, knockout);
+ }
+
+
+ private static function getDefaultGradientValues():DataProvider
+ {
+ return new DataProvider([new GradientColor(0xFFFFFF, 1, 0),
+ new GradientColor(0xFF0000, .25, 128),
+ new GradientColor(0x000000, 1, 255)]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/FilterWorkbench.as b/FilterWorkbench/flexapp/FilterWorkbench.as
new file mode 100644
index 0000000..bf20c15
--- /dev/null
+++ b/FilterWorkbench/flexapp/FilterWorkbench.as
@@ -0,0 +1,261 @@
+package flexapp
+{
+ import com.example.programmingas3.filterWorkbench.FilterWorkbenchController;
+ import com.example.programmingas3.filterWorkbench.IFilterPanel;
+ import com.example.programmingas3.filterWorkbench.ImageType;
+
+ import flash.display.DisplayObject;
+ import flash.events.Event;
+ import flash.events.ProgressEvent;
+
+ import flexapp.filterPanels.BevelPanel;
+ import flexapp.filterPanels.BlurPanel;
+ import flexapp.filterPanels.ColorMatrixPanel;
+ import flexapp.filterPanels.ConvolutionPanel;
+ import flexapp.filterPanels.DropShadowPanel;
+ import flexapp.filterPanels.GlowPanel;
+ import flexapp.filterPanels.GradientBevelPanel;
+ import flexapp.filterPanels.GradientGlowPanel;
+
+ import mx.collections.ArrayCollection;
+ import mx.containers.Canvas;
+ import mx.controls.Button;
+ import mx.controls.ComboBox;
+ import mx.controls.TextArea;
+ import mx.core.Application;
+ import mx.events.FlexEvent;
+ import mx.events.ListEvent;
+
+ public class FilterWorkbench extends Application
+ {
+ // ------- Constants -------
+ // --- Filter types ---
+ private static const BEVEL:String = "bevel";
+ private static const BLUR:String = "blur";
+ private static const COLOR_MATRIX:String = "colorMatrix";
+ private static const CONVOLUTION:String = "convolution";
+ private static const DROP_SHADOW:String = "dropShadow";
+ private static const GLOW:String = "glow";
+ private static const GRADIENT_BEVEL:String = "gradientBevel";
+ private static const GRADIENT_GLOW:String = "gradientGlow";
+
+ private static const INSTRUCTIONS_TEXT:String = "Select a filter and the corresponding\nActionScript code will appear here.\n\nClick \"Apply filter and add another\"\nto see the effect of multiple filters\napplied in combination.";
+
+
+ // ------- Child Controls -------
+ // Positioned and created within FilterWorkbench.mxml
+ public var filterPicker:ComboBox;
+ public var filterTargetPicker:ComboBox;
+ public var applyFilterBtn:Button;
+ public var imageContainer:ImageContainer;
+ public var codeDisplay:TextArea;
+ public var filterFormContainer:Canvas;
+
+ // Filter panels
+ private var _bevelPanel:BevelPanel;
+ private var _blurPanel:BlurPanel;
+ private var _colorMatrixPanel:ColorMatrixPanel;
+ private var _convolutionPanel:ConvolutionPanel;
+ private var _dropShadowPanel:DropShadowPanel;
+ private var _glowPanel:GlowPanel;
+ private var _gradientBevelPanel:GradientBevelPanel;
+ private var _gradientGlowPanel:GradientGlowPanel;
+
+ // Controller
+ private var _controller:FilterWorkbenchController;
+
+
+ // ------- Constructor -------
+ public function FilterWorkbench()
+ {
+ addEventListener(FlexEvent.CREATION_COMPLETE, setupChildren);
+ }
+
+
+ // ------- Event handling -------
+ private function setupChildren(event:FlexEvent):void
+ {
+ removeEventListener(FlexEvent.CREATION_COMPLETE, setupChildren);
+
+ // instantiate the filter panels
+ _bevelPanel = new BevelPanel();
+ _blurPanel = new BlurPanel();
+ _colorMatrixPanel = new ColorMatrixPanel();
+ _convolutionPanel = new ConvolutionPanel();
+ _dropShadowPanel = new DropShadowPanel();
+ _glowPanel = new GlowPanel();
+ _gradientBevelPanel = new GradientBevelPanel();
+ _gradientGlowPanel = new GradientGlowPanel();
+
+ // set up data and events for the controls
+ var filterList:ArrayCollection = new ArrayCollection();
+ filterList.addItem({label:"< None >", data:null});
+ filterList.addItem({label:"Bevel", data:BEVEL});
+ filterList.addItem({label:"Blur", data:BLUR});
+ filterList.addItem({label:"Color matrix", data:COLOR_MATRIX});
+ filterList.addItem({label:"Convolution", data:CONVOLUTION});
+ filterList.addItem({label:"Drop shadow", data:DROP_SHADOW});
+ filterList.addItem({label:"Glow", data:GLOW});
+ filterList.addItem({label:"Gradient bevel", data:GRADIENT_BEVEL});
+ filterList.addItem({label:"Gradient glow", data:GRADIENT_GLOW});
+ filterPicker.dataProvider = filterList;
+ filterPicker.rowCount = 9;
+
+ filterPicker.addEventListener(ListEvent.CHANGE, setFilter);
+
+ var imageList:ArrayCollection = new ArrayCollection(ImageType.getImageTypes());
+ filterTargetPicker.labelField = "name";
+ filterTargetPicker.dataProvider = imageList;
+ filterTargetPicker.selectedIndex = 0;
+
+ filterTargetPicker.addEventListener(ListEvent.CHANGE, setImage);
+
+ applyFilterBtn.enabled = false;
+ applyFilterBtn.addEventListener(FlexEvent.BUTTON_DOWN, applyFilter);
+
+ codeDisplay.text = INSTRUCTIONS_TEXT;
+
+ // create the controller for the app
+ _controller = new FilterWorkbenchController();
+ _controller.addEventListener(ProgressEvent.PROGRESS, imageLoadProgress);
+ _controller.addEventListener(Event.COMPLETE, imageLoadComplete);
+ _controller.addEventListener(Event.CHANGE, updateCode);
+
+ // load the first image
+ _controller.setFilterTarget(filterTargetPicker.selectedItem as ImageType);
+ }
+
+
+ private function setFilter(event:ListEvent):void
+ {
+ setupSelectedFilter();
+ }
+
+
+ private function setImage(event:ListEvent):void
+ {
+ var imageType:ImageType = filterTargetPicker.selectedItem as ImageType;
+
+ if (imageType == null) { return; }
+
+ // remove the previous image
+ if (imageContainer.numChildren > 0)
+ {
+ while (imageContainer.hasImage)
+ {
+ imageContainer.removeImage();
+ }
+ }
+
+ // Show a progress indicator
+ //CursorManager.setBusyCursor();
+
+ _controller.setFilterTarget(imageType);
+
+ resetForm();
+ }
+
+
+ private function applyFilter(event:FlexEvent):void
+ {
+ if (filterPicker.selectedIndex > 0)
+ {
+ _controller.applyFilter();
+ resetForm();
+ }
+ }
+
+
+ private function updateCode(event:Event):void
+ {
+ codeDisplay.text = _controller.getCode();
+ }
+
+
+ private function imageLoadProgress(event:ProgressEvent):void
+ {
+ // update the progress bar
+ }
+
+
+ private function imageLoadComplete(event:Event):void
+ {
+ // clear away the progress bar and display the image
+ //CursorManager.removeBusyCursor();
+
+ var image:DisplayObject = _controller.getFilterTarget();
+ imageContainer.addImage(image);
+ }
+
+
+ // ------- Private methods -------
+ private function resetForm():void
+ {
+ filterPicker.selectedIndex = 0;
+ hideFilterForm();
+ applyFilterBtn.enabled = false;
+ }
+
+
+ private function hideFilterForm():void
+ {
+ if (filterFormContainer.numChildren > 0)
+ {
+ filterFormContainer.removeChildAt(0);
+ }
+ }
+
+
+ private function setupSelectedFilter():void
+ {
+ hideFilterForm();
+
+ if (filterPicker.selectedIndex <= 0)
+ {
+ applyFilterBtn.enabled = false;
+ _controller.setFilter(null);
+ return;
+ }
+
+ applyFilterBtn.enabled = true;
+
+ var filterPanel:IFilterPanel;
+
+ switch (filterPicker.selectedItem.data)
+ {
+ case BEVEL:
+ filterPanel = _bevelPanel;
+ break;
+ case BLUR:
+ filterPanel = _blurPanel;
+ break;
+ case COLOR_MATRIX:
+ filterPanel = _colorMatrixPanel;
+ break;
+ case CONVOLUTION:
+ filterPanel = _convolutionPanel;
+ break;
+ case DROP_SHADOW:
+ filterPanel = _dropShadowPanel;
+ break;
+ case GLOW:
+ filterPanel = _glowPanel;
+ break;
+ case GRADIENT_BEVEL:
+ filterPanel = _gradientBevelPanel;
+ break;
+ case GRADIENT_GLOW:
+ filterPanel = _gradientGlowPanel;
+ break;
+ }
+
+ var panelDO:DisplayObject;
+ if ((panelDO = filterPanel as DisplayObject) != null)
+ {
+ filterFormContainer.addChild(panelDO);
+ }
+ filterPanel.resetForm();
+ _controller.setFilter(filterPanel.filterFactory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/ImageContainer.as b/FilterWorkbench/flexapp/ImageContainer.as
new file mode 100644
index 0000000..bb1a757
--- /dev/null
+++ b/FilterWorkbench/flexapp/ImageContainer.as
@@ -0,0 +1,56 @@
+package flexapp
+{
+ import mx.containers.Canvas;
+ import flash.display.DisplayObject;
+ import mx.core.UIComponent;
+
+ public class ImageContainer extends Canvas
+ {
+ // ------- Private vars -------
+ private var _image:DisplayObject;
+ private var _container:UIComponent;
+
+
+ // ------- Constructor --------
+ public function ImageContainer()
+ {
+ super();
+ _container = new UIComponent();
+ addChild(_container);
+ }
+
+
+ // ------- Public properties -------
+ public function get hasImage():Boolean
+ {
+ return (_image != null);
+ }
+
+
+ // ------- Public methods -------
+ public function addImage(image:DisplayObject):void
+ {
+ if (_image == image) { return; }
+
+ if (_image != null)
+ {
+ _container.removeChild(_image);
+ }
+
+ _container.addChild(image);
+ image.x = (width / 2) - (image.width / 2);
+ image.y = (height / 2) - (image.height / 2);
+
+ _image = image;
+ }
+
+
+ public function removeImage():void
+ {
+ if (_image == null) { return; }
+
+ _container.removeChild(_image);
+ _image = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/controls/BGColorCellRenderer.as b/FilterWorkbench/flexapp/controls/BGColorCellRenderer.as
new file mode 100644
index 0000000..c08c18b
--- /dev/null
+++ b/FilterWorkbench/flexapp/controls/BGColorCellRenderer.as
@@ -0,0 +1,48 @@
+package flexapp.controls
+{
+ import com.example.programmingas3.filterWorkbench.GradientColor;
+
+ import mx.controls.DataGrid;
+ import mx.controls.Label;
+ import mx.controls.dataGridClasses.DataGridListData;
+
+ public class BGColorCellRenderer extends Label
+ {
+ public function BGColorCellRenderer()
+ {
+ super();
+ }
+
+
+ override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+ {
+ super.updateDisplayList(unscaledWidth, unscaledHeight);
+
+ graphics.clear();
+
+ var grid:DataGrid = DataGrid(DataGridListData(listData).owner);
+
+ if (grid.isItemSelected(data) || grid.isItemHighlighted(data))
+ {
+ return;
+ }
+
+ var gColor:GradientColor = data as GradientColor;
+ if (gColor != null)
+ {
+ // calculate the foreground color
+ var r:Number = (gColor.color >> 16) & 0xFF;
+ var g:Number = (gColor.color >> 8) & 0xFF;
+ var b:Number = gColor.color & 0xFF;
+ var avg:int = Math.round((r + g + b) / 3);
+ var foreground:uint = (avg > 0x99 || gColor.alpha < .6) ? 0x000000 : 0xFFFFFF;
+ setStyle("color", foreground);
+
+ // draw the background
+ graphics.beginFill(data.color, data.alpha);
+ graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
+ graphics.endFill();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/controls/QualityComboBox.as b/FilterWorkbench/flexapp/controls/QualityComboBox.as
new file mode 100644
index 0000000..54835d3
--- /dev/null
+++ b/FilterWorkbench/flexapp/controls/QualityComboBox.as
@@ -0,0 +1,26 @@
+package flexapp.controls
+{
+ import flash.filters.BitmapFilterQuality;
+
+ import mx.controls.ComboBox;
+
+ public class QualityComboBox extends ComboBox
+ {
+ [Bindable]
+ private var _qualityList:Array =
+ [
+ {label:"Low", data:BitmapFilterQuality.LOW},
+ {label:"Medium", data:BitmapFilterQuality.MEDIUM},
+ {label:"High", data:BitmapFilterQuality.HIGH}
+ ];
+
+
+ public function QualityComboBox()
+ {
+ super();
+
+ width = 85;
+ dataProvider = _qualityList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/controls/TypeComboBox.as b/FilterWorkbench/flexapp/controls/TypeComboBox.as
new file mode 100644
index 0000000..3fe9a16
--- /dev/null
+++ b/FilterWorkbench/flexapp/controls/TypeComboBox.as
@@ -0,0 +1,26 @@
+package flexapp.controls
+{
+ import flash.filters.BitmapFilterType;
+
+ import mx.controls.ComboBox;
+
+ public class TypeComboBox extends ComboBox
+ {
+ [Bindable]
+ private var _typeList:Array =
+ [
+ {label:"Inner", data:BitmapFilterType.INNER},
+ {label:"Outer", data:BitmapFilterType.OUTER},
+ {label:"Full", data:BitmapFilterType.FULL}
+ ];
+
+
+ public function TypeComboBox()
+ {
+ super();
+
+ width = 75;
+ dataProvider = _typeList;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/BevelPanel.mxml b/FilterWorkbench/flexapp/filterPanels/BevelPanel.mxml
new file mode 100644
index 0000000..030a8a7
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/BevelPanel.mxml
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/BlurPanel.mxml b/FilterWorkbench/flexapp/filterPanels/BlurPanel.mxml
new file mode 100644
index 0000000..7998600
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/BlurPanel.mxml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/ColorMatrixPanel.mxml b/FilterWorkbench/flexapp/filterPanels/ColorMatrixPanel.mxml
new file mode 100644
index 0000000..ff3e6aa
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/ColorMatrixPanel.mxml
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/ConvolutionPanel.mxml b/FilterWorkbench/flexapp/filterPanels/ConvolutionPanel.mxml
new file mode 100644
index 0000000..35594aa
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/ConvolutionPanel.mxml
@@ -0,0 +1,265 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/DropShadowPanel.mxml b/FilterWorkbench/flexapp/filterPanels/DropShadowPanel.mxml
new file mode 100644
index 0000000..f9b9d7b
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/DropShadowPanel.mxml
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/GlowPanel.mxml b/FilterWorkbench/flexapp/filterPanels/GlowPanel.mxml
new file mode 100644
index 0000000..85d2b1e
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/GlowPanel.mxml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/GradientBevelPanel.mxml b/FilterWorkbench/flexapp/filterPanels/GradientBevelPanel.mxml
new file mode 100644
index 0000000..462bf80
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/GradientBevelPanel.mxml
@@ -0,0 +1,311 @@
+
+
+
+
+ 1 || newValue < 0) { needToRevert = true; }
+ break;
+ case "ratio":
+ if (newValue > 255 || newValue < 0) { needToRevert = true; }
+ break;
+ }
+ }
+
+ if (needToRevert)
+ {
+ data[event.dataField] = _editValue;
+ gradientValues.dataProvider.replaceItemAt(data, index);
+ return;
+ }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function checkForItemDelete(event:ListEvent):void
+ {
+ var cell:Button = event.itemRenderer as Button;
+ if (cell != null)
+ {
+ _gradients.removeItemAt(_gradients.getItemIndex(cell.data));
+ updateFilter();
+ }
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function addNewGradient(event:MouseEvent):void
+ {
+ var gradient:GradientColor = new GradientColor(addColorValue.selectedColor, addAlphaValue.value, addRatioValue.value);
+ _gradients.addItem(gradient);
+
+ updateFilter();
+ }
+
+
+ // ------- DataGrid utility methods -------
+ private function colorFormatter(data:Object, column:DataGridColumn):String
+ {
+ var c:GradientColor = data as GradientColor;
+ if (c != null)
+ {
+ return ColorStringFormatter.formatColorHex24(c.color);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var angle:Number = angleValue.value;
+ var distance:Number = distanceValue.value;
+ var knockout:Boolean = knockoutValue.selected;
+ var type:String = typeValue.selectedItem.data;
+ var colors:Array = _gradients.toArray();
+
+ _filterFactory.modifyFilter(distance, angle, colors, blurX, blurY, strength, quality, type, knockout);
+ }
+
+
+ private function getDefaultGradientValues():void
+ {
+ if (_gradients == null)
+ {
+ _gradients = new ArrayCollection();
+
+ var sort:Sort = new Sort();
+ sort.fields = [new SortField("ratio")];
+ _gradients.sort = sort;
+
+ _gradients.refresh();
+ }
+ else
+ {
+ _gradients.removeAll();
+ }
+
+ _gradients.addItem(new GradientColor(0xFFFFFF, 1, 0));
+ _gradients.addItem(new GradientColor(0xFF0000, .25, 128));
+ _gradients.addItem(new GradientColor(0x000000, 1, 255));
+
+ gradientValues.dataProvider = _gradients;
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/flexapp/filterPanels/GradientGlowPanel.mxml b/FilterWorkbench/flexapp/filterPanels/GradientGlowPanel.mxml
new file mode 100644
index 0000000..e8adf5b
--- /dev/null
+++ b/FilterWorkbench/flexapp/filterPanels/GradientGlowPanel.mxml
@@ -0,0 +1,312 @@
+
+
+
+
+ 1 || newValue < 0) { needToRevert = true; }
+ break;
+ case "ratio":
+ if (newValue > 255 || newValue < 0) { needToRevert = true; }
+ break;
+ }
+ }
+
+ if (needToRevert)
+ {
+ data[event.dataField] = _editValue;
+ gradientValues.dataProvider.replaceItemAt(data, index);
+ return;
+ }
+
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function checkForItemDelete(event:ListEvent):void
+ {
+ var cell:Button = event.itemRenderer as Button;
+ if (cell != null)
+ {
+ _gradients.removeItemAt(_gradients.getItemIndex(cell.data));
+ updateFilter();
+ }
+ }
+
+
+ private function changeFilterValue(event:Event):void
+ {
+ // update the filter
+ updateFilter();
+ }
+
+
+ private function addNewGradient(event:MouseEvent):void
+ {
+ var gradient:GradientColor = new GradientColor(addColorValue.selectedColor, addAlphaValue.value, addRatioValue.value);
+ _gradients.addItem(gradient);
+
+ updateFilter();
+ }
+
+
+ // ------- DataGrid utility methods -------
+ private function colorFormatter(data:Object, column:DataGridColumn):String
+ {
+ var c:GradientColor = data as GradientColor;
+ if (c != null)
+ {
+ return ColorStringFormatter.formatColorHex24(c.color);
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+
+ // ------- Private methods -------
+ private function updateFilter():void
+ {
+ var blurX:Number = blurXValue.value;
+ var blurY:Number = blurYValue.value;
+ var strength:Number = strengthValue.value;
+ var quality:int = qualityValue.selectedItem.data;
+ var angle:Number = angleValue.value;
+ var distance:Number = distanceValue.value;
+ var knockout:Boolean = knockoutValue.selected;
+ var type:String = typeValue.selectedItem.data;
+ var colors:Array = _gradients.toArray();
+
+ _filterFactory.modifyFilter(distance, angle, colors, blurX, blurY, strength, quality, type, knockout);
+ }
+
+
+ private function getDefaultGradientValues():void
+ {
+ if (_gradients == null)
+ {
+ _gradients = new ArrayCollection();
+
+ var sort:Sort = new Sort();
+ sort.fields = [new SortField("ratio")];
+ _gradients.sort = sort;
+
+ _gradients.refresh();
+ }
+ else
+ {
+ _gradients.removeAll();
+ }
+
+ _gradients.addItem(new GradientColor(0xFFFFFF, 1, 0));
+ _gradients.addItem(new GradientColor(0xFF0000, .25, 128));
+ _gradients.addItem(new GradientColor(0x000000, 1, 255));
+
+ gradientValues.dataProvider = _gradients;
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FilterWorkbench/images/sampleAnimation.fla b/FilterWorkbench/images/sampleAnimation.fla
new file mode 100644
index 0000000..16cdcc2
Binary files /dev/null and b/FilterWorkbench/images/sampleAnimation.fla differ
diff --git a/FilterWorkbench/images/sampleAnimation.swf b/FilterWorkbench/images/sampleAnimation.swf
new file mode 100644
index 0000000..d2f4d93
Binary files /dev/null and b/FilterWorkbench/images/sampleAnimation.swf differ
diff --git a/FilterWorkbench/images/sampleImage1.jpg b/FilterWorkbench/images/sampleImage1.jpg
new file mode 100644
index 0000000..824f97a
Binary files /dev/null and b/FilterWorkbench/images/sampleImage1.jpg differ
diff --git a/FilterWorkbench/images/sampleImage2.jpg b/FilterWorkbench/images/sampleImage2.jpg
new file mode 100644
index 0000000..a098625
Binary files /dev/null and b/FilterWorkbench/images/sampleImage2.jpg differ
diff --git a/GeometricShapes/GeometricShapes.fla b/GeometricShapes/GeometricShapes.fla
new file mode 100644
index 0000000..5e4056a
Binary files /dev/null and b/GeometricShapes/GeometricShapes.fla differ
diff --git a/GeometricShapes/GeometricShapes.mxml b/GeometricShapes/GeometricShapes.mxml
new file mode 100644
index 0000000..531674c
--- /dev/null
+++ b/GeometricShapes/GeometricShapes.mxml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Circle
+ Triangle
+ Square
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/Circle.as b/GeometricShapes/com/example/programmingas3/geometricshapes/Circle.as
new file mode 100644
index 0000000..0872f2c
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/Circle.as
@@ -0,0 +1,36 @@
+package com.example.programmingas3.geometricshapes
+{
+
+ public class Circle implements IGeometricShape
+ {
+ public var diameter:Number;
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/EquilateralTriangle.as b/GeometricShapes/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
new file mode 100644
index 0000000..00a6d1f
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
@@ -0,0 +1,33 @@
+package com.example.programmingas3.geometricshapes
+{
+
+ public class EquilateralTriangle extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from 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;
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as b/GeometricShapes/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
new file mode 100644
index 0000000..88af3f7
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
@@ -0,0 +1,21 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class GeometricShapeFactory
+ {
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/IGeometricShape.as b/GeometricShapes/com/example/programmingas3/geometricshapes/IGeometricShape.as
new file mode 100644
index 0000000..56605be
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/IGeometricShape.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IGeometricShape
+ {
+ function getArea():Number;
+ function describe():String;
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/IPolygon.as b/GeometricShapes/com/example/programmingas3/geometricshapes/IPolygon.as
new file mode 100644
index 0000000..2aef5af
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/IPolygon.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IPolygon extends IGeometricShape
+ {
+ function getPerimeter():Number;
+ function getSumOfAngles():Number;
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/RegularPolygon.as b/GeometricShapes/com/example/programmingas3/geometricshapes/RegularPolygon.as
new file mode 100644
index 0000000..944e8fd
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/RegularPolygon.as
@@ -0,0 +1,52 @@
+package com.example.programmingas3.geometricshapes
+{
+
+ /**
+ * A regular polygon is equilateral (all sides are the same length)
+ * and equiangular (all interior angles are the same).
+ */
+ public class RegularPolygon implements IPolygon
+ {
+ public var numSides:Number
+ public var sideLength:Number;
+
+ public function RegularPolygon(len:Number = 100, sides:Number = 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/GeometricShapes/com/example/programmingas3/geometricshapes/Square.as b/GeometricShapes/com/example/programmingas3/geometricshapes/Square.as
new file mode 100644
index 0000000..06d1fb2
--- /dev/null
+++ b/GeometricShapes/com/example/programmingas3/geometricshapes/Square.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class Square extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from RegularPolygon
+
+ public function Square(len:Number = 100):void
+ {
+ super(len, 4);
+ }
+
+ public override function getArea():Number
+ {
+ return (this.sideLength * this.sideLength);
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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 a Square.\n";
+ desc += super.describe();
+
+ return desc;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Geometry/GeometrySample.as b/Geometry/GeometrySample.as
new file mode 100644
index 0000000..a89d7dc
--- /dev/null
+++ b/Geometry/GeometrySample.as
@@ -0,0 +1,203 @@
+package {
+ import flash.display.*;
+ import flash.geom.*;
+ import flash.util.trace;
+ import flash.events.*;
+ import flash.errors.*;
+ import flash.events.*;
+ import com.examples.display.DropDownList;
+ import com.examples.display.StandardTextButton;
+ public class GeometrySample extends Sprite {
+ public var gradientViewBox:GradientViewBox;
+ public var entryPane:Sprite;
+ public var gradientFields:Array;
+ public var gradProps:Object;
+ public function GeometrySample() {
+ gradProps = new GradProps();
+ gradientViewBox = new GradientViewBox(gradProps);
+ addChild(gradientViewBox);
+ gradientViewBox.draw();
+ setUpEntryFields();
+ }
+ public function redrawGradient(e:Event):void {
+ try {
+ gradProps.type = gradientFields["Type"].currentSelection.text;
+ gradProps.colors = arrayToNumbers(gradientFields["Colors"].input.text.split(","));
+ gradProps.alphas = arrayToNumbers(gradientFields["Alphas"].input.text.split(","));
+ gradProps.ratios = arrayToNumbers(gradientFields["Ratios"].input.text.split(","));
+ gradProps.spreadMethod = gradientFields["Spread"].currentSelection.text;
+ gradProps.interp = gradientFields["Interpolation"].currentSelection.text;
+ gradProps.focalPtRatio = Number(gradientFields["Focal Point"].input.text);
+ gradProps.boxWidth = Number(gradientFields["Box Width"].input.text);
+ gradProps.boxHeight = Number(gradientFields["Box Height"].input.text);
+ gradProps.boxRotation = Number(gradientFields["Box Rotation"].input.text);
+ gradProps.tx = Number(gradientFields["tx"].input.text);
+ gradProps.ty = Number(gradientFields["ty"].input.text);
+ var temp:GradientViewBox = gradientViewBox;
+ gradientViewBox = new GradientViewBox(gradProps);
+ removeChild(temp);
+ addChild(gradientViewBox);
+ gradientViewBox.draw();
+ gradientViewBox.addEventListener(MouseEvent.CLICK, redrawGradient);
+ } catch (e:Error) {
+ trace("bad data.")
+ }
+ }
+ private function arrayToNumbers(a:Array):Array {
+ var returnArray:Array = []
+ for(var i:uint = 0; i < a.length; i++) {
+ returnArray = returnArray.concat(Number(a[i]));
+ }
+ return returnArray;
+ }
+ private function setUpEntryFields():void {
+ entryPane = new Sprite();
+ gradientFields = new Array();
+ entryPane.y = stage.stageHeight / 2 ;
+ entryPane.x = 20;
+
+ addDropDownList(["linear", "radial"], "Type");
+ addTextEntryBox("Colors", arrayToHexString(gradProps.colors));
+ addTextEntryBox("Alphas", gradProps.alphas);
+ addTextEntryBox("Ratios", gradProps.ratios.toString());
+ addDropDownList(["pad", "reflect", "repeat"], "Spread");
+ addDropDownList(["linearRGB", "RGB"], "Interpolation");
+ addTextEntryBox("Focal Point", gradProps.focalPtRatio.toString());
+ addTextEntryBox("Box Width", gradProps.boxWidth.toString());
+ addTextEntryBox("Box Height", gradProps.boxHeight.toString());
+ addTextEntryBox("Box Rotation", gradProps.boxRotation.toString());
+ addTextEntryBox("tx", gradProps.tx.toString());
+ addTextEntryBox("ty", gradProps.ty.toString());
+ arrangeFields();
+
+ var drawButton:StandardTextButton = new StandardTextButton("Draw");
+ drawButton.x = stage.stageWidth - drawButton.width - 10;
+ drawButton.y = stage.stageHeight - drawButton.height - 10;
+ drawButton.addEventListener(MouseEvent.CLICK, redrawGradient);
+ addChild(drawButton);
+
+ addChild(entryPane);
+ }
+ private function arrayToHexString(a:Array):String {
+ var str:String = "";
+ for (var i:uint = 0; i < a.length - 1; i++) {
+ var hStr:String = hexStr(a[i]) + ",";
+ str += hStr;
+ }
+ str += hexStr(a[a.length - 1]);
+ str.toUpperCase();
+ return str;
+ }
+ private function hexStr(n:uint):String {
+ var str:String = "";
+ str += n.toString(16);
+ str = str.toUpperCase();
+ var prependZeros:String = "";
+ for (var i:uint = str.length; i < 6; i++) {
+ prependZeros += "0";
+ }
+ str = "0x" + prependZeros + str;
+ return str;
+ }
+ private function addTextEntryBox(label:String, txt:String, inputType:Boolean = true):void {
+ gradientFields[label] = new TextEntryBox(label, txt, inputType);
+ entryPane.addChild (gradientFields[label]);
+ }
+ private function addDropDownList(list:Array, label:String):void {
+ gradientFields[label] = new DropDownList(list, label);
+ entryPane.addChild (gradientFields[label]);
+ }
+ private function arrangeFields():void {
+ var midValue:uint = Math.floor(entryPane.numChildren/2);
+ for (var i:uint = 1; i < midValue; i++) {
+ entryPane.getChildAt(i).y = entryPane.getChildAt(i - 1).y + 22;
+ }
+ entryPane.getChildAt(midValue).y = entryPane.getChildAt(0).y;
+ entryPane.getChildAt(midValue).x = stage.stageWidth / 2;
+ for (var i:uint = midValue + 1; i < entryPane.numChildren; i++) {
+ entryPane.getChildAt(i).y = entryPane.getChildAt(i - 1).y + 22;
+ entryPane.getChildAt(i).x = stage.stageWidth / 2;
+ }
+ for (var i:uint = 0; i < entryPane.numChildren; i++) {
+ if (entryPane.getChildAt(i) is DropDownList) {
+ DropDownList(entryPane.getChildAt(i)).repositionDropDownList(entryPane.getChildAt(i).y)
+ }
+ }
+ }
+
+ }
+}
+
+import flash.display.*;
+import flash.geom.*;
+import flash.text.*;
+import flash.util.trace;
+import flash.ui.Keyboard;
+import flash.events.*;
+
+class TextEntryBox extends Sprite{
+ public var label:TextField;
+ public var input:TextField;
+ public function TextEntryBox(labelText:String = "label", inputText:String="", inputType:Boolean = true) {
+ label = new TextField;
+ label.defaultTextFormat = new TextFormat("Verdana", 10, 0x000000, true);
+ label.text = labelText + ":";
+ addChild(label);
+
+ input = new TextField();
+ input.defaultTextFormat = new TextFormat("Courier");
+ input.backgroundColor = 0xFFFFFF;
+ input.background = true;
+ input.border = true;
+ input.type = TextFieldType.INPUT;
+ input.width = 125;
+ input.height = 20;
+ input.x = 80;
+ input.text = inputText;
+ addChild(input);
+ if (!inputType) {
+ input.type = TextFieldType.DYNAMIC;
+ input.backgroundColor = 0xEEEEEE;
+ }
+ }
+}
+
+class GradientViewBox extends Sprite {
+ public function GradientViewBox(gradProps:GradProps) {
+ var matrix:Matrix = new Matrix();
+ matrix.createGradientBox(gradProps.boxWidth,
+ gradProps.boxHeight,
+ gradProps.boxRotation,
+ gradProps.tx,
+ gradProps.ty);
+
+ graphics.beginGradientFill(gradProps.type,
+ gradProps.colors,
+ gradProps.alphas,
+ gradProps.ratios,
+ matrix,
+ gradProps.spreadMethod,
+ gradProps.interp,
+ gradProps.focalPtRatio);
+ }
+ public function draw():void {
+ graphics.drawRect(20, 20, stage.stageWidth - 40, stage.stageHeight/2 - 30);
+ graphics.lineStyle(1,0x000000);
+ }
+}
+class GradProps {
+ public var type:String = GradientType.LINEAR;
+ public var colors:Array = [0x00FF00, 0x000088];
+ public var alphas:Array = [0.8, 1];
+ public var ratios:Array = [0, 255];
+ public var spreadMethod:String = SpreadMethod.PAD;
+ public var interp:String = InterpolationMethod.LINEAR_RGB;
+ public var focalPtRatio:Number = 0;
+ public var boxWidth:Number = 100;
+ public var boxHeight:Number = 50;
+ public var boxRotation:Number = Math.PI/4;
+ public var tx:Number = 0;
+ public var ty:Number = 0;
+}
+
+
diff --git a/Geometry/com/examples/display/DropDownList.as b/Geometry/com/examples/display/DropDownList.as
new file mode 100644
index 0000000..37083a3
--- /dev/null
+++ b/Geometry/com/examples/display/DropDownList.as
@@ -0,0 +1,199 @@
+package com.examples.display {
+ import flash.display.*;
+ import flash.geom.*;
+ import flash.text.*;
+ import flash.util.*;
+ import flash.ui.Keyboard;
+ import flash.events.*;
+
+ public class DropDownList extends Sprite{
+ public var selectionList:Sprite;
+ public var currentLine:uint = 0;
+ public var currentSelection:ListEntry;
+ public var label:TextField;
+ public var listExpanded:Boolean = false;
+ private var dropDownButton:DropDownButton;
+ public function DropDownList(entryList:Array, labelText:String = "label") {
+ label = new TextField;
+ label.defaultTextFormat = new TextFormat("Verdana", 10, 0x000000, true);
+ label.text = labelText + ":";
+ addChild(label);
+
+ currentSelection = new ListEntry();
+ currentSelection.border = true;
+ currentSelection.selectable = true;
+ currentSelection.text = entryList[0].toString();
+ currentSelection.height = currentSelection.textHeight + 6;
+ currentSelection.width = 125;
+ currentSelection.addEventListener(MouseEvent.CLICK, textSelect);
+ addChild(currentSelection);
+
+ selectionList = new SelectionList(entryList);
+ for (var i:uint = 0; i < entryList.length; i++) {
+ SelectionList(selectionList).entries[i].addEventListener(MouseEvent.MOUSE_DOWN, listEntryClicked);
+ }
+ selectionList.x = currentSelection.x;
+ selectionList.y = currentSelection.height + 2;
+
+ dropDownButton = new DropDownButton();
+ repositionDropDownList(0);
+ dropDownButton.addEventListener(MouseEvent.CLICK, expandList)
+ addChild(dropDownButton);
+ }
+ public function listEntryClicked(e:Event):void {
+ var index:uint = e.target.parent.getChildIndex(e.target);
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ TextField(selectionList.getChildAt(index)).backgroundColor = 0xFFFF00;
+ currentLine = index;
+ selectionList.addEventListener(MouseEvent.MOUSE_UP, collapseList);
+ selectionList.addEventListener(MouseEvent.MOUSE_MOVE, checkMousePosition);
+ }
+ public function checkMousePosition(e:Event):void {
+ var mousePt:Point = new Point(stage.mouseX, stage.mouseY);
+ var selectedLine:uint;
+ for (var i:uint = 0; i < selectionList.numChildren - 1; i++) {
+ if (selectionList.getChildAt(i).hitTestPoint(mousePt.x, mousePt.y)) {
+ highlightLine(i);
+ trace(i);
+ }
+ }
+ }
+ public function repositionDropDownList(yOffset:Number):void {
+ dropDownButton.x = width - 20;
+ selectionList.y = yOffset + currentSelection.height;
+ }
+ public function textSelect(e:Event):void {
+ highlightCurrentSelection();
+ currentSelection.addEventListener(KeyboardEvent.KEY_DOWN, arrowTextSelect);
+ }
+ public function arrowTextSelect(e:KeyboardEvent):void {
+ var key:uint = e.keyCode;
+ if (key == Keyboard.DOWN) {
+ currentLine = Math.min(selectionList.numChildren - 2, currentLine + 1);
+ } else if (key == Keyboard.UP) {
+ currentLine = Math.max(0, currentLine - 1);
+ }
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ setInterval(highlightCurrentSelection, 50);
+ if (key == Keyboard.ENTER) {
+ setInterval(unhighlightCurrentSelection, 50);
+ currentSelection.removeEventListener(KeyboardEvent.KEY_DOWN, arrowTextSelect);
+ }
+ }
+ public function highlightCurrentSelection():void {
+ currentSelection.setSelection(0, currentSelection.length);
+ }
+ public function unhighlightCurrentSelection():void {
+ currentSelection.setSelection(0, 0);
+ }
+ public function expandList(e:Event) {
+ listExpanded = true;
+ stage.addEventListener(MouseEvent.MOUSE_DOWN, clearDropDownLists)
+ parent.addChildAt(selectionList, parent.numChildren - 1);
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ stage.focus = currentSelection;
+ currentSelection.addEventListener(KeyboardEvent.KEY_DOWN, arrowListSelect);
+ var clickPt:Point = new Point(stage.mouseX, stage.mouseY);
+ }
+ public function clearDropDownLists(e:Event):void {
+ if (!hitTestPoint(stage.mouseX, stage.mouseY)) {
+ collapseList();
+ }
+ }
+ public function arrowListSelect(e:KeyboardEvent):void {
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ var key:uint = e.keyCode;
+ if (key == Keyboard.DOWN) {
+ currentLine = Math.min(selectionList.numChildren - 2, currentLine + 1);
+ } else if (key == Keyboard.UP) {
+ currentLine = Math.max(0, currentLine - 1);
+ }
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ setInterval(highlightCurrentSelection, 50);
+ if (key == Keyboard.ENTER) {
+ setInterval(unhighlightCurrentSelection, 50);
+ currentSelection.removeEventListener(KeyboardEvent.KEY_DOWN, arrowListSelect);
+ collapseList();
+ }
+ }
+ public function collapseList(...args) {
+ if (listExpanded) parent.removeChild(selectionList);
+ listExpanded = false;
+ selectionList.removeEventListener(MouseEvent.MOUSE_MOVE, checkMousePosition);
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ }
+ public function highlightLine(n:uint):void {
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ currentLine = n;
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ }
+ }
+}
+
+import flash.display.*;
+import flash.geom.*;
+import flash.util.trace;
+import flash.text.TextFormat;
+
+class ListEntry extends TextField {
+ public function ListEntry() {
+ defaultTextFormat = new TextFormat("Courier", 12);
+ background = true;
+ backgroundColor = 0xFFFFFF;
+ width = 125;
+ type = TextFieldType.DYNAMIC;
+ wordWrap = true;
+ height = 20;
+ x = 80;
+ }
+}
+
+class SelectionList extends Sprite {
+ public var entries:Array;
+ public function SelectionList(entryList:Array):void {
+ entries = new Array();
+ for (var i:uint=0; i < entryList.length ; i++) {
+ entries[i] = new ListEntry();
+ entries[i].x = 0;
+ entries[i].text += entryList[i].toString();
+ if (i > 0) {
+ entries[i].y = entries[i - 1].y + entries[i - 1].height;
+ }
+ addChild(entries[i]);
+ }
+ var outline:Shape = new Shape();
+ outline.graphics.lineStyle(1, 0x000000);
+ outline.graphics.drawRect(0, 0, width, height);
+ addChild(outline);
+ }
+}
+class DropDownButton extends SimpleButton {
+ public function DropDownButton() {
+ upState = new DropDownButtonState();
+ hitTestState = upState;
+ overState = upState;
+ downState = new DropDownButtonState();
+ var downStateTransform:Transform = downState.transform;
+ const downShadeMultiplier:Number = 0.75;
+ downStateTransform.colorTransform = new ColorTransform(downShadeMultiplier, downShadeMultiplier, downShadeMultiplier);
+ downState.transform = downStateTransform;
+
+ }
+}
+
+class DropDownButtonState extends Sprite {
+ public function DropDownButtonState() {
+ const size:Number = 19;
+ const sideMargin:Number = 7;
+ const topMargin:Number = 8;
+ graphics.lineStyle(1, 0x000000);
+ graphics.beginFill(0xDDDDDD);
+ graphics.drawRect(1, 1, size, size);
+ graphics.moveTo(sideMargin, topMargin);
+ graphics.beginFill(0x000000);
+ graphics.lineTo(size - sideMargin, topMargin);
+ graphics.lineTo(size / 2, size - topMargin);
+ graphics.lineTo(sideMargin, topMargin);
+ }
+}
\ No newline at end of file
diff --git a/Geometry/com/examples/display/StandardTextButton.as b/Geometry/com/examples/display/StandardTextButton.as
new file mode 100644
index 0000000..bf68d89
--- /dev/null
+++ b/Geometry/com/examples/display/StandardTextButton.as
@@ -0,0 +1,44 @@
+package com.examples.display {
+ import flash.display.*;
+ import flash.geom.*;
+
+ public class StandardTextButton extends SimpleButton {
+ public var label:String;
+ public function StandardTextButton(txtString:String="label") {
+ label = txtString;
+ upState = new StandardTextButtonState(label);
+ hitTestState = upState;
+ overState = upState;
+ downState = new StandardTextButtonState(label);
+ var downStateTransform:Transform = downState.transform;
+ const downShadeMultiplier:Number = 0.75;
+ downStateTransform.colorTransform = new ColorTransform(downShadeMultiplier, downShadeMultiplier, downShadeMultiplier);
+ downState.transform = downStateTransform;
+
+ }
+
+ }
+}
+
+import flash.display.*;
+import flash.text.*;
+import flash.util.trace;
+
+class StandardTextButtonState extends Sprite {
+ public var txt:TextField;
+ public function StandardTextButtonState(txtString:String) {
+ txt = new TextField();
+ txt.defaultTextFormat = new TextFormat("Verdana");
+ txt.defaultTextFormat.size = 12;
+ txt.defaultTextFormat.align = TextFormatAlign.CENTER
+ txt.text = txtString;
+ addChild(txt);
+ txt.x += 4;
+ txt.width = txt.textWidth + 12;
+ txt.height = txt.textHeight + 6;
+
+ graphics.beginFill(0xEEEEEE);
+ graphics.lineStyle(1, 0x000000);
+ graphics.drawRoundRect(0, 0, width, height, 8, 8);
+ }
+}
diff --git a/GlobalStockTicker/GlobalStockTicker.mxml b/GlobalStockTicker/GlobalStockTicker.mxml
new file mode 100644
index 0000000..3a9943e
--- /dev/null
+++ b/GlobalStockTicker/GlobalStockTicker.mxml
@@ -0,0 +1,169 @@
+
+
+
+ = 0 && rowIndex < stockData.stocks.length)
+ {
+ marketIndex = rowIndex;
+ // reload company data
+ companyGrid.dataProvider = stockData.stocks[marketIndex];
+ }
+ }
+
+ private function onCompanySelected(event:ListEvent):void
+ {
+ graph.redraw();
+ }
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GlobalStockTicker/GlobalStockTicker.swf b/GlobalStockTicker/GlobalStockTicker.swf
new file mode 100644
index 0000000..21828c0
Binary files /dev/null and b/GlobalStockTicker/GlobalStockTicker.swf differ
diff --git a/GlobalStockTicker/GlobalStockTicker_CS5.fla b/GlobalStockTicker/GlobalStockTicker_CS5.fla
new file mode 100644
index 0000000..333c363
Binary files /dev/null and b/GlobalStockTicker/GlobalStockTicker_CS5.fla differ
diff --git a/GlobalStockTicker/Requires Flash Professional CS5 or Flex SDK 4.txt b/GlobalStockTicker/Requires Flash Professional CS5 or Flex SDK 4.txt
new file mode 100644
index 0000000..e69de29
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/FinancialGraph.as b/GlobalStockTicker/com/example/programmingas3/stockticker/FinancialGraph.as
new file mode 100644
index 0000000..3f9bab2
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/FinancialGraph.as
@@ -0,0 +1,122 @@
+package com.example.programmingas3.stockticker
+{
+ import flash.display.GradientType;
+ import flash.display.Sprite;
+ import flash.geom.Matrix;
+
+ public class FinancialGraph extends Sprite
+ {
+ public var granularity : int = 10;
+ public var variation : Number = 0.5;
+
+ public var gridColor : uint = 0x8080c0;
+ public var gridThickness : uint = 1;
+
+ public var borderColor : uint = 0xc0c0c0;
+ public var borderThickness : uint = 2;
+
+ public var bgColor : uint = 0x666666;
+ public var graphColor : uint = 0x8080ff;
+ public var graphThickness : uint = 2;
+
+ public var graphGradientColors:Array = [0x000000, 0x000000];
+ public var graphGradientAlphas:Array = [1, 0];
+
+ public var graphHeight:Number;
+ public var graphWidth:Number;
+
+ /**
+ * Creates random line graphs that simulate stock price charts.
+ */
+ public function FinancialGraph(w:Number = 740, h:Number = 310, graphColor:uint = 0x8080ff)
+ {
+ super();
+ this.graphWidth = w;
+ this.graphHeight = h;
+ this.graphColor = graphColor;
+ }
+ public function init():void
+ {
+ redraw();
+ }
+
+ private function fractalDraw( x1 : int, y1 : int, x2 : int, y2 : int ):void
+ {
+ graphics.moveTo(x1,y1);
+ fractalDrawRecurse( x1, y1, x2, y2 );
+ }
+
+ private function fractalDrawRecurse( x1 : int, y1 : int, x2 : int, y2 : int ):void
+ {
+ var delta:int = x2 - x1;
+ if( delta < 0 )
+ {
+ delta = -delta;
+ }
+
+ if( delta <= granularity )
+ {
+ graphics.lineTo(x2,y2);
+ return;
+ }
+
+ var midx : int = (x1 + x2)/2;
+ var midy : int = (y1 + y2)/2;
+
+ midy += (Math.random() - 0.5) * variation * (x2 - x1);
+
+ if( midy > height )
+ {
+ midy = height;
+ }
+ if( midy < 0 )
+ {
+ midy = 0;
+ }
+
+ fractalDrawRecurse( x1, y1, midx, midy );
+ fractalDrawRecurse( midx, midy, x2, y2 );
+ }
+
+ public function redraw() : void
+ {
+ var ybegin: int = graphHeight * Math.random();
+ var yend: int = graphHeight * Math.random();
+ var ymin: int = Math.min(ybegin, yend);
+
+ var ratios:Array = [0, 255];
+ var verticalGradient:Matrix = new Matrix();
+ verticalGradient.createGradientBox(graphWidth, graphHeight-ymin, Math.PI/2, 0, ymin);
+
+ graphics.clear();
+
+ graphics.beginFill(bgColor);
+ graphics.drawRect(0, 0, graphWidth, graphHeight);
+ graphics.endFill();
+
+ graphics.beginGradientFill( GradientType.LINEAR, graphGradientColors,
+ graphGradientAlphas, ratios, verticalGradient );
+ graphics.lineStyle( graphThickness, graphColor );
+ graphics.moveTo(0, graphHeight);
+ graphics.lineTo(0, ybegin);
+ fractalDraw( 0, ybegin, graphWidth, yend );
+ graphics.lineTo(graphWidth, graphHeight);
+ graphics.lineTo(0, graphHeight);
+ graphics.endFill();
+
+ graphics.lineStyle( gridThickness, gridColor );
+
+ for( var i:int = 1; i < 3; ++i )
+ {
+ graphics.moveTo( 0, i * graphHeight / 3 );
+ graphics.lineTo( graphWidth, i * graphHeight / 3 );
+ }
+
+ for( i = 1; i < 6; ++i )
+ {
+ graphics.moveTo( i * graphWidth / 6, 0 );
+ graphics.lineTo( i * graphWidth / 6, graphHeight );
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/Localizer.as b/GlobalStockTicker/com/example/programmingas3/stockticker/Localizer.as
new file mode 100644
index 0000000..6d87dda
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/Localizer.as
@@ -0,0 +1,118 @@
+package com.example.programmingas3.stockticker
+{
+ import flash.globalization.CurrencyFormatter;
+ import flash.globalization.DateTimeFormatter;
+ import flash.globalization.DateTimeNameStyle;
+ import flash.globalization.DateTimeStyle;
+ import flash.globalization.LastOperationStatus;
+ import flash.globalization.LocaleID;
+ import flash.globalization.NumberFormatter;
+
+ /**
+ * Manages the localization of numbers, currency amounts, and dates by using the
+ * features of the flash.globalization library.
+ */
+ public class Localizer
+ {
+ private var locale:LocaleID;
+ private var currentCurrency:String = "USD";
+ private var currentFraction:Number = 2;
+ private var currentSymbol:String = "$";
+ private var symbolIsSafe:Boolean;
+
+ public var df:DateTimeFormatter;
+ public var nf:NumberFormatter;
+ public var cf:CurrencyFormatter;
+
+ public var monthNames:Vector.;
+
+ public function Localizer():void
+ {
+ //
+ }
+
+ /**
+ * Creates new formatters when the locale ID changes.
+ */
+ public function setLocale(newLocale:String):void
+ {
+ locale = new LocaleID(newLocale);
+
+ nf = new NumberFormatter(locale.name);
+ traceError(nf.lastOperationStatus, "NumberFormatter", nf.actualLocaleIDName);
+
+ cf = new CurrencyFormatter(locale.name);
+ traceError(cf.lastOperationStatus, "CurrencyFormatter", cf.actualLocaleIDName);
+ symbolIsSafe = cf.formattingWithCurrencySymbolIsSafe(currentCurrency);
+ cf.setCurrency(currentCurrency, currentSymbol);
+ cf.fractionalDigits = currentFraction;
+
+ df = new DateTimeFormatter(locale.name, DateTimeStyle.LONG, DateTimeStyle.SHORT);
+ traceError(df.lastOperationStatus, "DateTimeFormatter", df.actualLocaleIDName);
+
+ monthNames = df.getMonthNames(DateTimeNameStyle.LONG_ABBREVIATION);
+ }
+
+ /**
+ * Sends information about errors and fallbacks to the console.
+ * In a real application this should be replaced by error handling logic.
+ */
+ public function traceError(status:String, serviceName:String, localeID:String):void
+ {
+ if(status != LastOperationStatus.NO_ERROR)
+ {
+ if(status == LastOperationStatus.USING_FALLBACK_WARNING)
+ {
+ trace("Warning - Fallback locale ID used by " + serviceName + ": " + localeID);
+ }
+ else
+ {
+ trace("Error in " + serviceName + ": " + status);
+ }
+ }
+ else
+ {
+ trace(serviceName + " created for locale ID: " + localeID);
+ }
+ }
+
+ /**
+ * Saves the currency string, currency symbol, and number of franctional digits for the
+ * currently selected market.
+ */
+ public function setFormatProperties(fractionalDigits:int, currency:String, symbol:String):void
+ {
+ currentFraction = fractionalDigits;
+ currentCurrency = currency;
+ currentSymbol = symbol;
+ }
+
+ public function formatNumber(value:Number, fractionalDigits:int = 2):String
+ {
+ nf.fractionalDigits = fractionalDigits;
+ return nf.formatNumber(value);
+ }
+
+ public function formatPercent(value:Number, fractionalDigits:int = 2):String
+ {
+ nf.fractionalDigits = fractionalDigits;
+
+ // Most operating systems don't support formatting of percentages,
+ // so it's not implemented in the first version of the
+ // flash.globalization package. There are locales where the percent sign
+ // is not at the end, such as 12,34 %" (Danish, German, French, others),
+ // "-%�12,34" (Turkish), and "-%12,34" (Farsi).
+ return nf.formatNumber(value) + "%";
+ }
+
+ public function formatCurrency(value:Number):String
+ {
+ return cf.format(value, symbolIsSafe)
+ }
+
+ public function formatDate(dateValue:Date):String
+ {
+ return df.format(dateValue);
+ }
+ }
+}
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/StockDataModel.as b/GlobalStockTicker/com/example/programmingas3/stockticker/StockDataModel.as
new file mode 100644
index 0000000..5e4996c
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/StockDataModel.as
@@ -0,0 +1,68 @@
+package com.example.programmingas3.stockticker
+{
+ /**
+ * Stores all of the data for the GlobalStockTicker example.
+ */
+ public class StockDataModel
+ {
+
+ private var usStocks : Array = [
+ { ticker:"EQXE", volume: 1232131, price:31.62, change:23.5 },
+ { ticker:"FRRQ", volume: 4435921, price:26.21, change:-1.2 },
+ { ticker:"GXQQ", volume: 915832, price:16.24, change: 4.6 },
+ { ticker:"OQKE", volume: 7712977, price:1251.18, change:14.8 },
+ { ticker:"QJEL", volume:13523881, price:17.95, change: 7.3 },
+ { ticker:"ZQRW", volume: 5834773, price:32.83, change:-2.6 }
+ ];
+ private var euStocks : Array = [
+ { ticker:"AVGOX", volume: 6905431, price:26.31, change:-4.5 },
+ { ticker:"BRAGK", volume: 84959872, price:62.26, change: 3.9 },
+ { ticker:"KXAQA", volume: 40596529, price:42.16, change: 6.4 },
+ { ticker:"MZAOS", volume:154959562, price:81.51, change:-5.6 },
+ { ticker:"RXAQR", volume: 84171312, price:59.17, change: 7.7 },
+ { ticker:"TOSQK", volume: 4841984, price:38.32, change: 5.1 }
+ ];
+ private var jaStocks : Array = [
+ { ticker:"後芝電器産業(株)", volume: 3716026, price: 1233, change: 5.62 },
+ { ticker:"トヤタ自動車(株)", volume: 925569, price:31234, change: 24.81 },
+ { ticker:"キョノン(株)", volume: 2971599, price:51231, change: -9.51 },
+ { ticker:"任端堂(株)", volume: 9849265, price: 4324, change: 20.34 },
+ { ticker:"パニソナック(株)", volume:15195171, price:12234, change: -4.08 },
+ { ticker:"武畑薬品工業(株)", volume: 5423988, price: 5523, change: 13.74 }
+ ];
+
+ /**
+ * Depending on your system you might have problems rendering some of the text,
+ * or might have locales that are not supported.
+ */
+ public const locales:Array = [
+ { data:"de-de", label: "de-DE (German, Germany)" },
+ { data:"el", label: "el (Greek)" },
+ { data:"en-gb", label: "en-GB (English, Great Britain)" },
+ { data:"en-us", label: "en-US (English, U.S.)" },
+ { data:"fr-ca", label: "fr-CA (French, Canada)" },
+ { data:"fr-ch", label: "fr-CH (French, Swiss)" },
+ { data:"fr-fr", label: "fr-FR (French, France)" },
+ { data:"hi", label: "hi (Hindi)" },
+ { data:"ja", label: "ja (Japanese)" },
+ { data:"ru", label: "ru (Russian)" },
+ { data:"zh-cn", label: "zh-CN (Chinese, China)" }
+ ];
+
+ public const markets:Array = [
+ {label:"US", data:1, currency:"USD", symbol:"$", fraction:2},
+ {label:"Japanese", data:2, currency:"JPY", symbol:"¥", fraction:0},
+ {label:"European", data:3, currency:"EUR", symbol:"€", fraction:2}
+ ];
+
+ public var stocks:Array;
+
+ public function StockDataModel()
+ {
+ stocks = new Array(3);
+ stocks[0] = usStocks;
+ stocks[1] = jaStocks;
+ stocks[2] = euStocks;
+ }
+ }
+}
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/flash/GlobalStockTicker.as b/GlobalStockTicker/com/example/programmingas3/stockticker/flash/GlobalStockTicker.as
new file mode 100644
index 0000000..b212d51
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/flash/GlobalStockTicker.as
@@ -0,0 +1,159 @@
+package com.example.programmingas3.stockticker.flash
+{
+ import com.example.programmingas3.stockticker.FinancialGraph;
+ import com.example.programmingas3.stockticker.Localizer;
+ import com.example.programmingas3.stockticker.StockDataModel;
+ import com.example.programmingas3.stockticker.flash.RightAlignedCell;
+
+ import fl.controls.dataGridClasses.DataGridColumn;
+ import fl.data.DataProvider;
+ import fl.events.ListEvent;
+ import flash.display.MovieClip;
+
+ /**
+ * This is the Flash Professional version of the UI and controller for the
+ * Global Stock Ticker example in the ActionScript 3.0 Developer's Guide.
+ *
+ * If you are using Flash Builder or the Flex SDK you do not need this file.
+ * The UI and control will be handled in the file GlobalStockTicker.mxml.
+ */
+ public class GlobalStockTicker extends MovieClip
+ {
+ public var graph:FinancialGraph;
+ public var localizer:Localizer;
+ public var stockData:StockDataModel;
+
+ public var currentLocale:String;
+ public var marketIndex:int = 0;
+
+ public function GlobalStockTicker()
+ {
+ init();
+ }
+
+ public function init():void
+ {
+ stockData = new StockDataModel();
+
+ // select US market by default
+ currentLocale = "en-US";
+ localizer = new Localizer();
+ loadCurrentLocale();
+ // specify USD for the initial currency
+ localizer.setFormatProperties(2, "USD", "$");
+
+ setUpCombos();
+ setUpGrid();
+
+// set the company data provider to the list of US companies
+ companyGrid.dataProvider = new DataProvider(stockData.stocks[marketIndex]);
+
+ setUpGraph();
+ }
+ public function loadCurrentLocale():void
+ {
+ localizer.setLocale(currentLocale);
+ currentTimeTxt.text = localizer.formatDate(new Date());
+ month1Txt.text = localizer.monthNames[3];
+ month2Txt.text = localizer.monthNames[4];
+ month3Txt.text = localizer.monthNames[5];
+ month4Txt.text = localizer.monthNames[6];
+ month5Txt.text = localizer.monthNames[7];
+ month6Txt.text = localizer.monthNames[8];
+ }
+
+ private function setUpCombos():void
+ {
+ this.localeCbo.dataProvider = new DataProvider(stockData.locales);
+ this.localeCbo.addEventListener(ListEvent.ITEM_CLICK, onLocaleSelected);
+
+ this.marketCbo.dataProvider = new DataProvider(stockData.markets);
+ this.marketCbo.addEventListener(ListEvent.ITEM_CLICK, onMarketSelected);
+ }
+
+ private function setUpGrid():void
+ {
+ var col1:DataGridColumn = new DataGridColumn("ticker");
+ col1.headerText = "Company";
+ col1.sortOptions = Array.NUMERIC;
+ col1.width = 200;
+
+ var col2:DataGridColumn = new DataGridColumn("volume");
+ col2.headerText = "Volume";
+ col2.width = 120;
+ col2.cellRenderer = RightAlignedCell;
+ col2.labelFunction = displayVolume;
+
+ var col3:DataGridColumn = new DataGridColumn("price");
+ col3.headerText = "Price";
+ col3.width = 70;
+ col3.cellRenderer = RightAlignedCell;
+ col3.labelFunction = displayPrice;
+
+ var col4:DataGridColumn = new DataGridColumn("change");
+ col4.headerText = "Change";
+ col4.width = 120;
+ col4.cellRenderer = RightAlignedCell;
+ col4.labelFunction = displayPercent;
+
+ this.companyGrid.addColumn(col1);
+ this.companyGrid.addColumn(col2);
+ this.companyGrid.addColumn(col3);
+ this.companyGrid.addColumn(col4);
+
+ this.companyGrid.addEventListener(ListEvent.ITEM_CLICK, onCompanySelected);
+ }
+
+ private function setUpGraph(w:int = 740, h:int = 285):void
+ {
+ graph = new FinancialGraph(w, h, 0x80ffff);
+ graph.x = 20;
+ graph.y = 280;
+ graph.init();
+
+ this.addChild(graph);
+ }
+ private function onLocaleSelected(event:ListEvent):void
+ {
+ var rowIndex:int = event.rowIndex as int;
+ currentLocale = event.target.dataProvider.getItemAt(rowIndex).data;
+ loadCurrentLocale();
+
+ // refresh the datagrid
+ companyGrid.dataProvider = new DataProvider(stockData.stocks[marketIndex]);
+ }
+
+ private function onMarketSelected(event:ListEvent):void
+ {
+ var rowIndex:int = event.rowIndex as int;
+ var dataItem:Object = event.target.dataProvider.getItemAt(rowIndex);
+ localizer.setFormatProperties(dataItem.fraction, dataItem.currency, dataItem.symbol);
+ loadCurrentLocale();
+ if (rowIndex >= 0 && rowIndex < stockData.stocks.length)
+ {
+ marketIndex = rowIndex;
+ companyGrid.dataProvider = new DataProvider(stockData.stocks[marketIndex]);
+ }
+ }
+
+ private function onCompanySelected(event:ListEvent):void
+ {
+ graph.redraw();
+ }
+
+ private function displayVolume(item:Object):String
+ {
+ return localizer.formatNumber(item.volume, 0);
+ }
+
+ private function displayPercent(item:Object):String
+ {
+ return localizer.formatPercent(item.change);
+ }
+
+ private function displayPrice(item:Object):String
+ {
+ return localizer.formatCurrency(item.price);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/flash/RightAlignedCell.as b/GlobalStockTicker/com/example/programmingas3/stockticker/flash/RightAlignedCell.as
new file mode 100644
index 0000000..3d15727
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/flash/RightAlignedCell.as
@@ -0,0 +1,37 @@
+package com.example.programmingas3.stockticker.flash
+{
+ import fl.controls.listClasses.CellRenderer;
+ import fl.controls.listClasses.ICellRenderer;
+ import flash.text.TextFormat;
+ import flash.text.TextFormatAlign;
+ /**
+ * The main class definition. Make sure the class is marked "public" and in the case
+ * of our custom cell renderer, extends the CellRenderer class and implements the
+ * ICellRenderer interface.
+ */
+ public class RightAlignedCell extends CellRenderer implements ICellRenderer
+ {
+ // Create a new private variable to hold the custom text format.
+ private var tf:TextFormat;
+ /**
+ * This method defines the TextFormat object and sets the align
+ * property to "right".
+ */
+ public function RightAlignedCell()
+ {
+ tf = new TextFormat();
+ tf.align = TextFormatAlign.RIGHT;
+ }
+ /**
+ * Override the inherited drawLayout() method from the CellRenderer class.
+ * This method sets the text field's width to the width of the data grid column,
+ * applies the new text format using the setTextFormat() method, and calls the
+ * parent class's drawLayout() method.
+ */
+ override protected function drawLayout():void {
+ textField.width = this.width;
+ textField.setTextFormat(tf);
+ super.drawLayout();
+ }
+ }
+}
diff --git a/GlobalStockTicker/com/example/programmingas3/stockticker/flex/FinGraph.mxml b/GlobalStockTicker/com/example/programmingas3/stockticker/flex/FinGraph.mxml
new file mode 100644
index 0000000..6a21328
--- /dev/null
+++ b/GlobalStockTicker/com/example/programmingas3/stockticker/flex/FinGraph.mxml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/HelloWorld/Greeter.as b/HelloWorld/Greeter.as
new file mode 100644
index 0000000..9479e9b
--- /dev/null
+++ b/HelloWorld/Greeter.as
@@ -0,0 +1,12 @@
+package
+{
+ public class Greeter
+ {
+ public function sayHello():String
+ {
+ var greeting:String;
+ greeting = "Hello World!";
+ return greeting;
+ }
+ }
+}
diff --git a/HelloWorld/HelloWorld.fla b/HelloWorld/HelloWorld.fla
new file mode 100644
index 0000000..1876e91
Binary files /dev/null and b/HelloWorld/HelloWorld.fla differ
diff --git a/HelloWorld/HelloWorld.mxml b/HelloWorld/HelloWorld.mxml
new file mode 100644
index 0000000..bc80006
--- /dev/null
+++ b/HelloWorld/HelloWorld.mxml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HelloWorldEnhanced/Greeter.as b/HelloWorldEnhanced/Greeter.as
new file mode 100644
index 0000000..5e74498
--- /dev/null
+++ b/HelloWorldEnhanced/Greeter.as
@@ -0,0 +1,46 @@
+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
+ {
+ if (validNames.indexOf(inputName) > -1)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/HelloWorldEnhanced/HelloWorld.fla b/HelloWorldEnhanced/HelloWorld.fla
new file mode 100644
index 0000000..933f343
Binary files /dev/null and b/HelloWorldEnhanced/HelloWorld.fla differ
diff --git a/HelloWorldEnhanced/HelloWorld.mxml b/HelloWorldEnhanced/HelloWorld.mxml
new file mode 100644
index 0000000..4290d7b
--- /dev/null
+++ b/HelloWorldEnhanced/HelloWorld.mxml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IntrovertIM_CSharp/App.ico b/IntrovertIM_CSharp/App.ico
new file mode 100644
index 0000000..3a5525f
Binary files /dev/null and b/IntrovertIM_CSharp/App.ico differ
diff --git a/IntrovertIM_CSharp/AppForm.cs b/IntrovertIM_CSharp/AppForm.cs
new file mode 100644
index 0000000..dc71f06
--- /dev/null
+++ b/IntrovertIM_CSharp/AppForm.cs
@@ -0,0 +1,302 @@
+using System;
+using System.IO;
+//using System.Collections;
+//using System.ComponentModel;
+//using System.Drawing;
+using System.Windows.Forms;
+
+using Flash.External;
+
+namespace IntrovertIM_CSharp
+{
+ ///
+ /// Summary description for Form1.
+ ///
+ public class AppForm : System.Windows.Forms.Form
+ {
+ #region Private Fields
+
+ private bool appReady = false;
+ private bool swfReady = false;
+ private ExternalInterfaceProxy proxy;
+
+ #endregion
+
+ #region Constructor
+
+ public AppForm()
+ {
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ String swfPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "IntrovertIMApp.swf";
+ this.IntrovertIMApp.LoadMovie(0, swfPath);
+
+ // Create the proxy and register this app to receive notification when the proxy receives
+ // a call from ActionScript
+ proxy = new ExternalInterfaceProxy(IntrovertIMApp);
+ proxy.ExternalInterfaceCall += new ExternalInterfaceCallEventHandler(proxy_ExternalInterfaceCall);
+
+ appReady = true;
+ }
+
+ #endregion
+
+ #region Event Handling
+
+ ///
+ /// Called when the "Send" button is clicked by the user.
+ ///
+ ///
+ ///
+ private void SendButton_Click(object sender, System.EventArgs e)
+ {
+ sendMessage(MessageText.Text);
+ }
+
+
+ ///
+ /// Called by the proxy when an ActionScript ExternalInterface call
+ /// is made by the SWF
+ ///
+ /// The object raising the event
+ /// The event arguments associated with the event
+ /// The response to the function call.
+ private object proxy_ExternalInterfaceCall(object sender, ExternalInterfaceCallEventArgs e)
+ {
+ switch (e.FunctionCall.FunctionName)
+ {
+ case "isReady":
+ return isReady();
+ case "setSWFIsReady":
+ setSWFIsReady();
+ return null;
+ case "newMessage":
+ newMessage((string)e.FunctionCall.Arguments[0]);
+ return null;
+ case "statusChange":
+ statusChange();
+ return null;
+ default:
+ return null;
+ }
+ }
+
+
+ #endregion
+
+ #region Methods called by Flash Player
+
+ ///
+ /// Called to check if the page has initialized and JavaScript is available
+ ///
+ ///
+ private bool isReady()
+ {
+ return appReady;
+ }
+
+ ///
+ /// Called to notify the page that the SWF has set it's callbacks
+ ///
+ private void setSWFIsReady()
+ {
+ // record that the SWF has registered it's functions (i.e. that C#
+ // can safely call the ActionScript functions)
+ swfReady = true;
+
+ updateStatus();
+ }
+
+ ///
+ /// Called to notify C# of a new message from the SWF IM client
+ ///
+ ///
+ private void newMessage(string message)
+ {
+ // append the message text to the end of the transcript
+ string textToAppend = String.Format("The Other Person: {0}{1}", message, Environment.NewLine);
+ Transcript.AppendText(textToAppend);
+ }
+
+ ///
+ /// Called to notify the page that the SWF user's availability (status) has changed
+ ///
+ private void statusChange()
+ {
+ updateStatus();
+ }
+
+ #endregion
+
+ #region Other Private Methods
+
+ ///
+ /// Called when the "Send" button is pressed; the value in the
+ /// MessageText text field is passed in as a parameter.
+ ///
+ /// The message to send.
+ private void sendMessage(string message)
+ {
+ if (swfReady)
+ {
+ // if the SWF has registered it's functions, add the message text to
+ // the local transcript, send it to the SWF file to be processed
+ // there, and clear the message text field.
+ string localText = String.Format("You: {0}{1}", message, Environment.NewLine);
+ Transcript.AppendText(localText);
+ MessageText.Clear();
+ // call the newMessage function in ActionScript
+ proxy.Call("newMessage", message);
+ }
+ }
+
+ ///
+ /// Calls the ActionScript function to get the current "availability"
+ /// status and writes it into the text field.
+ ///
+ private void updateStatus()
+ {
+ Status.Text = (string)proxy.Call("getStatus");
+ }
+
+ #endregion
+
+ #region Controls
+
+ private AxShockwaveFlashObjects.AxShockwaveFlash IntrovertIMApp;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox Status;
+ private System.Windows.Forms.TextBox Transcript;
+ private System.Windows.Forms.TextBox MessageText;
+ private System.Windows.Forms.Button SendButton;
+
+ #endregion
+
+ #region Miscellaneous
+
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.Run(new AppForm());
+ }
+
+
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if (components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #endregion
+
+ #region Windows Form Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(AppForm));
+ this.IntrovertIMApp = new AxShockwaveFlashObjects.AxShockwaveFlash();
+ this.label1 = new System.Windows.Forms.Label();
+ this.Status = new System.Windows.Forms.TextBox();
+ this.Transcript = new System.Windows.Forms.TextBox();
+ this.MessageText = new System.Windows.Forms.TextBox();
+ this.SendButton = new System.Windows.Forms.Button();
+ ((System.ComponentModel.ISupportInitialize)(this.IntrovertIMApp)).BeginInit();
+ this.SuspendLayout();
+ //
+ // IntrovertIMApp
+ //
+ this.IntrovertIMApp.Enabled = true;
+ this.IntrovertIMApp.Location = new System.Drawing.Point(16, 16);
+ this.IntrovertIMApp.Name = "IntrovertIMApp";
+ this.IntrovertIMApp.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("IntrovertIMApp.OcxState")));
+ this.IntrovertIMApp.Size = new System.Drawing.Size(600, 300);
+ this.IntrovertIMApp.TabIndex = 0;
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(0, 352);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(144, 22);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "The Other Person\'s Status";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // Status
+ //
+ this.Status.Location = new System.Drawing.Point(152, 352);
+ this.Status.Name = "Status";
+ this.Status.ReadOnly = true;
+ this.Status.Size = new System.Drawing.Size(224, 20);
+ this.Status.TabIndex = 2;
+ this.Status.Text = "";
+ //
+ // Transcript
+ //
+ this.Transcript.Location = new System.Drawing.Point(16, 384);
+ this.Transcript.Multiline = true;
+ this.Transcript.Name = "Transcript";
+ this.Transcript.ReadOnly = true;
+ this.Transcript.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.Transcript.Size = new System.Drawing.Size(600, 200);
+ this.Transcript.TabIndex = 3;
+ this.Transcript.Text = "";
+ //
+ // MessageText
+ //
+ this.MessageText.Location = new System.Drawing.Point(16, 592);
+ this.MessageText.Name = "MessageText";
+ this.MessageText.Size = new System.Drawing.Size(512, 20);
+ this.MessageText.TabIndex = 4;
+ this.MessageText.Text = "";
+ //
+ // SendButton
+ //
+ this.SendButton.Location = new System.Drawing.Point(544, 592);
+ this.SendButton.Name = "SendButton";
+ this.SendButton.Size = new System.Drawing.Size(72, 22);
+ this.SendButton.TabIndex = 5;
+ this.SendButton.Text = "Send";
+ this.SendButton.Click += new System.EventHandler(this.SendButton_Click);
+ //
+ // AppForm
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(632, 630);
+ this.Controls.Add(this.SendButton);
+ this.Controls.Add(this.MessageText);
+ this.Controls.Add(this.Transcript);
+ this.Controls.Add(this.Status);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.IntrovertIMApp);
+ this.Name = "AppForm";
+ this.Text = "Introvert IM (C# Edition)";
+ ((System.ComponentModel.ISupportInitialize)(this.IntrovertIMApp)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+ }
+}
diff --git a/IntrovertIM_CSharp/AppForm.resx b/IntrovertIM_CSharp/AppForm.resx
new file mode 100644
index 0000000..4f86338
--- /dev/null
+++ b/IntrovertIM_CSharp/AppForm.resx
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ Private
+
+
+ Private
+
+
+
+ AAEAAAD/////AQAAAAAAAAAMAgAAAFpTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0xLjAuNTAw
+ MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACFT
+ eXN0ZW0uV2luZG93cy5Gb3Jtcy5BeEhvc3QrU3RhdGUBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA5wAA
+ AAIBAAAAAQAAAAAAAAAAAAAAANIAAABnVWZVEAcAAAM+AAACHwAACAACAAAAAAAIAAAAAAAIAAAAAAAI
+ AA4AAABXAGkAbgBkAG8AdwAAAAsA//8LAP//CAAKAAAASABpAGcAaAAAAAgAAgAAAAAACwD//wgAAAAA
+ AAgAAgAAAAAACAAQAAAAUwBoAG8AdwBBAGwAbAAAAAsAAAALAAAACAACAAAAAAAIAAAAAAAIAAIAAAAA
+ AA0AAAAAAAAAAAAAAAAAAAAAAAsAAQALAP//CAAAAAAAAwAAAAAACAAIAAAAYQBsAGwAAAAL
+
+
+
+ False
+
+
+ Private
+
+
+ Private
+
+
+ Private
+
+
+ False
+
+
+ Private
+
+
+ Private
+
+
+ False
+
+
+ Private
+
+
+ Private
+
+
+ False
+
+
+ Private
+
+
+ False
+
+
+ Private
+
+
+ Private
+
+
+ False
+
+
+ (Default)
+
+
+ False
+
+
+ False
+
+
+ 8, 8
+
+
+ True
+
+
+ 80
+
+
+ True
+
+
+ AppForm
+
+
+ Private
+
+
\ No newline at end of file
diff --git a/IntrovertIM_CSharp/AssemblyInfo.cs b/IntrovertIM_CSharp/AssemblyInfo.cs
new file mode 100644
index 0000000..9f89a32
--- /dev/null
+++ b/IntrovertIM_CSharp/AssemblyInfo.cs
@@ -0,0 +1,58 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/AssemblyInfo.cs b/IntrovertIM_CSharp/ExternalInterfaceProxy/AssemblyInfo.cs
new file mode 100644
index 0000000..9f89a32
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/AssemblyInfo.cs
@@ -0,0 +1,58 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCall.cs b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCall.cs
new file mode 100644
index 0000000..0851421
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCall.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Flash.External
+{
+ ///
+ /// Value object containing information about an ExternalInterface call
+ /// sent between a .NET application and a Shockwave Flash object.
+ ///
+ public class ExternalInterfaceCall
+ {
+ #region Private Fields
+
+ private string _functionName;
+ private ArrayList _arguments;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Creates a new ExternalInterfaceCall instance with the specified
+ /// function name.
+ ///
+ /// The name of the function as provided
+ /// by Flash Player
+ public ExternalInterfaceCall(string functionName)
+ {
+ _functionName = functionName;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ ///
+ /// The name of the function call provided by Flash Player
+ ///
+ public string FunctionName
+ {
+ get { return _functionName; }
+ }
+
+ ///
+ /// The function parameters associated with this function call.
+ ///
+ public object[] Arguments
+ {
+ get { return (object[])_arguments.ToArray(typeof(object)); }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public override string ToString()
+ {
+ StringBuilder result = new StringBuilder();
+ result.AppendFormat("Function Name: {0}{1}", _functionName, Environment.NewLine);
+ if (_arguments != null && _arguments.Count > 0)
+ {
+ result.AppendFormat("Arguments:{0}", Environment.NewLine);
+ foreach (object arg in _arguments)
+ {
+ result.AppendFormat("\t{0}{1}", arg, Environment.NewLine);
+ }
+ }
+ return result.ToString();
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal void AddArgument(object argument)
+ {
+ if (_arguments == null)
+ {
+ _arguments = new ArrayList();
+ }
+ _arguments.Add(argument);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCallEventArgs.cs b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCallEventArgs.cs
new file mode 100644
index 0000000..2427534
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceCallEventArgs.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Flash.External
+{
+ public delegate object ExternalInterfaceCallEventHandler(object sender, ExternalInterfaceCallEventArgs e);
+
+ ///
+ /// Event arguments for the ExternalInterfaceCallEventHandler.
+ ///
+ public class ExternalInterfaceCallEventArgs : System.EventArgs
+ {
+ private ExternalInterfaceCall _functionCall;
+
+ public ExternalInterfaceCallEventArgs(ExternalInterfaceCall functionCall)
+ : base()
+ {
+ _functionCall = functionCall;
+ }
+
+ public ExternalInterfaceCall FunctionCall
+ {
+ get { return _functionCall; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.cs b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.cs
new file mode 100644
index 0000000..cdbb90b
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Runtime.InteropServices;
+using AxShockwaveFlashObjects;
+
+namespace Flash.External
+{
+ ///
+ /// Facilitates External Interface communication between a .NET application and a Shockwave
+ /// Flash ActiveX control by providing an abstraction layer over the XML-serialized data format
+ /// used by Flash Player for ExternalInterface communication.
+ /// This class provides the Call method for calling ActionScript functions and raises
+ /// the ExternalInterfaceCall event when calls come from ActionScript.
+ ///
+ public class ExternalInterfaceProxy
+ {
+ #region Private Fields
+
+ private AxShockwaveFlash _flashControl;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Creates a new ExternalInterfaceProxy for the specified Shockwave Flash ActiveX control.
+ ///
+ /// The Shockwave Flash ActiveX control with whom communication
+ /// is managed by this proxy.
+ public ExternalInterfaceProxy(AxShockwaveFlash flashControl)
+ {
+ _flashControl = flashControl;
+ _flashControl.FlashCall += new _IShockwaveFlashEvents_FlashCallEventHandler(_flashControl_FlashCall);
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Calls the ActionScript function which is registered as a callback method with the
+ /// ActionScript ExternalInterface class.
+ ///
+ /// The function name registered with the ExternalInterface class
+ /// corresponding to the ActionScript function that is to be called
+ /// Additional arguments, if any, to pass to the ActionScript function.
+ /// The result returned by the ActionScript function, or null if no result is returned.
+ /// Thrown when there is an error
+ /// calling the method on Flash Player. For instance, this exception is raised if the
+ /// specified function name is not registered as a callable function with the ExternalInterface
+ /// class; it is also raised if the ActionScript method throws an Error.
+ public object Call(string functionName, params object[] arguments)
+ {
+ try
+ {
+ string request = ExternalInterfaceSerializer.EncodeInvoke(functionName, arguments);
+ string response = _flashControl.CallFunction(request);
+ object result = ExternalInterfaceSerializer.DecodeResult(response);
+ return result;
+ }
+ catch (COMException)
+ {
+ throw;
+ }
+ }
+
+ #endregion
+
+ #region Events
+
+ ///
+ /// Raised when an External Interface call is made from Flash Player.
+ ///
+ public event ExternalInterfaceCallEventHandler ExternalInterfaceCall;
+
+ ///
+ /// Raises the ExternalInterfaceCall event, indicating that a call has come from Flash Player.
+ ///
+ /// The event arguments related to the event being raised.
+ protected virtual object OnExternalInterfaceCall(ExternalInterfaceCallEventArgs e)
+ {
+ if (ExternalInterfaceCall != null)
+ {
+ return ExternalInterfaceCall(this, e);
+ }
+ return null;
+ }
+
+ #endregion
+
+ #region Event Handling
+
+ ///
+ /// Called when Flash Player raises the FlashCallEvent (when an External Interface call
+ /// is made by ActionScript)
+ ///
+ /// The object raising the event
+ /// The arguments for the event
+ private void _flashControl_FlashCall(object sender, _IShockwaveFlashEvents_FlashCallEvent e)
+ {
+ ExternalInterfaceCall functionCall = ExternalInterfaceSerializer.DecodeInvoke(e.request);
+ ExternalInterfaceCallEventArgs eventArgs = new ExternalInterfaceCallEventArgs(functionCall);
+ object response = OnExternalInterfaceCall(eventArgs);
+ _flashControl.SetReturnValue(ExternalInterfaceSerializer.EncodeResult(response));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.csproj b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.csproj
new file mode 100644
index 0000000..b8e9acd
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceProxy.csproj
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceSerializer.cs b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceSerializer.cs
new file mode 100644
index 0000000..7f5246b
--- /dev/null
+++ b/IntrovertIM_CSharp/ExternalInterfaceProxy/ExternalInterfaceSerializer.cs
@@ -0,0 +1,305 @@
+using System;
+using System.Collections;
+using System.Xml;
+using System.IO;
+using System.Text;
+
+namespace Flash.External
+{
+ ///
+ /// Provides methods to convert the XML communication format to .NET classes. Typically
+ /// this class will not be used directly; it supports the operations of the
+ /// ExternalInterfaceProxy class.
+ ///
+ public class ExternalInterfaceSerializer
+ {
+ #region Constructor
+
+ private ExternalInterfaceSerializer()
+ {
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Encodes a function call to be sent to Flash.
+ ///
+ /// The name of the function to call.
+ /// Zero or more parameters to pass in to the ActonScript function.
+ /// The XML string representation of the function call to pass to Flash
+ public static string EncodeInvoke(string functionName, object[] arguments)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb));
+
+ //
+ writer.WriteStartElement("invoke");
+ writer.WriteAttributeString("name", functionName);
+ writer.WriteAttributeString("returntype", "xml");
+
+ if (arguments != null && arguments.Length > 0)
+ {
+ //
+ writer.WriteStartElement("arguments");
+
+ // individual arguments
+ foreach (object value in arguments)
+ {
+ WriteElement(writer, value);
+ }
+
+ //
+ writer.WriteEndElement();
+ }
+
+ //
+ writer.WriteEndElement();
+
+ writer.Flush();
+ writer.Close();
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Encodes a value to send to Flash as the result of a function call from Flash.
+ ///
+ /// The value to encode.
+ /// The XML string representation of the value.
+ public static string EncodeResult(object value)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb));
+
+ WriteElement(writer, value);
+
+ writer.Flush();
+ writer.Close();
+
+ return sb.ToString();
+ }
+
+
+ ///
+ /// Decodes a function call from Flash.
+ ///
+ /// The XML string representing the function call.
+ /// An ExternalInterfaceCall object representing the function call.
+ public static ExternalInterfaceCall DecodeInvoke(string xml)
+ {
+ XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, null);
+
+ reader.Read();
+
+ string functionName = reader.GetAttribute("name");
+ ExternalInterfaceCall result = new ExternalInterfaceCall(functionName);
+
+ reader.ReadStartElement("invoke");
+ reader.ReadStartElement("arguments");
+
+ while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "arguments")
+ {
+ result.AddArgument(ReadElement(reader));
+ }
+
+ reader.ReadEndElement();
+ reader.ReadEndElement();
+
+ return result;
+ }
+
+
+ ///
+ /// Decodes the result of a function call to Flash
+ ///
+ /// The XML string representing the result.
+ /// A containing the result
+ public static object DecodeResult(string xml)
+ {
+ XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Document, null);
+ reader.Read();
+ return ReadElement(reader);
+ }
+
+ #endregion
+
+ #region Writers
+
+ private static void WriteElement(XmlTextWriter writer, object value)
+ {
+ if (value == null)
+ {
+ writer.WriteStartElement("null");
+ writer.WriteEndElement();
+ }
+ else if (value is string)
+ {
+ writer.WriteStartElement("string");
+ writer.WriteString(value.ToString());
+ writer.WriteEndElement();
+ }
+ else if (value is bool)
+ {
+ writer.WriteStartElement((bool)value ? "true" : "false");
+ writer.WriteEndElement();
+ }
+ else if (value is Single || value is Double || value is int || value is uint)
+ {
+ writer.WriteStartElement("number");
+ writer.WriteString(value.ToString());
+ writer.WriteEndElement();
+ }
+ else if (value is ArrayList)
+ {
+ WriteArray(writer, (ArrayList)value);
+ }
+ else if (value is Hashtable)
+ {
+ WriteObject(writer, (Hashtable)value);
+ }
+ else
+ {
+ // null is the default when ActionScript can't serialize an object
+ writer.WriteStartElement("null");
+ writer.WriteEndElement();
+ }
+ }
+
+
+ private static void WriteArray(XmlTextWriter writer, ArrayList array)
+ {
+ writer.WriteStartElement("array");
+
+ int len = array.Count;
+
+ for (int i = 0; i < len; i++)
+ {
+ writer.WriteStartElement("property");
+ writer.WriteAttributeString("id", i.ToString());
+ WriteElement(writer, array[i]);
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+
+
+ private static void WriteObject(XmlTextWriter writer, Hashtable table)
+ {
+ writer.WriteStartElement("object");
+
+ foreach (DictionaryEntry entry in table)
+ {
+ writer.WriteStartElement("property");
+ writer.WriteAttributeString("id", entry.Key.ToString());
+ WriteElement(writer, entry.Value);
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+
+ #endregion
+
+ #region Readers
+
+ private static object ReadElement(XmlTextReader reader)
+ {
+ if (reader.NodeType != XmlNodeType.Element)
+ {
+ throw new XmlException();
+ }
+
+ if (reader.Name == "true")
+ {
+ reader.Read();
+ return true;
+ }
+
+ if (reader.Name == "false")
+ {
+ reader.Read();
+ return false;
+ }
+
+ if (reader.Name == "null" || reader.Name == "undefined")
+ {
+ reader.Read();
+ return null;
+ }
+
+ if (reader.IsStartElement("number"))
+ {
+ reader.ReadStartElement("number");
+ double value = Double.Parse(reader.Value);
+ reader.Read();
+ reader.ReadEndElement();
+ return value;
+ }
+
+ if (reader.IsStartElement("string"))
+ {
+ reader.ReadStartElement("string");
+ string value = reader.Value;
+ reader.Read();
+ reader.ReadEndElement();
+ return value;
+ }
+
+ if (reader.IsStartElement("array"))
+ {
+ reader.ReadStartElement("array");
+ ArrayList value = ReadArray(reader);
+ reader.ReadEndElement();
+ return value;
+ }
+
+ if (reader.IsStartElement("object"))
+ {
+ reader.ReadStartElement("object");
+ Hashtable value = ReadObject(reader);
+ reader.ReadEndElement();
+ return value;
+ }
+ throw new XmlException();
+ }
+
+
+ private static ArrayList ReadArray(XmlTextReader reader)
+ {
+ ArrayList result = new ArrayList();
+
+ while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "array")
+ {
+ int id = int.Parse(reader.GetAttribute("id"));
+ reader.ReadStartElement("property");
+ result.Add(ReadElement(reader));
+ reader.ReadEndElement();
+ }
+
+ return result;
+ }
+
+
+ private static Hashtable ReadObject(XmlTextReader reader)
+ {
+ Hashtable result = new Hashtable();
+
+ while (reader.NodeType != XmlNodeType.EndElement && reader.Name != "object")
+ {
+ string id = reader.GetAttribute("id");
+ reader.ReadStartElement("property");
+ result.Add(id, ReadElement(reader));
+ reader.ReadEndElement();
+ }
+
+ return result;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/AxInterop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/AxInterop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..df49b50
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/AxInterop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/ExternalInterfaceProxy.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/ExternalInterfaceProxy.dll
new file mode 100644
index 0000000..a559d92
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/ExternalInterfaceProxy.dll differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/Interop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/Interop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..834b406
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/bin/Debug/Interop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/AxInterop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/AxInterop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..df49b50
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/AxInterop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.dll
new file mode 100644
index 0000000..a559d92
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.dll differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.projdata b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.projdata
new file mode 100644
index 0000000..febaa42
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Debug/ExternalInterfaceProxy.projdata differ
diff --git a/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Interop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Interop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..834b406
Binary files /dev/null and b/IntrovertIM_CSharp/ExternalInterfaceProxy/obj/Interop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/IntrovertIM_CSharp.csproj b/IntrovertIM_CSharp/IntrovertIM_CSharp.csproj
new file mode 100644
index 0000000..271e678
--- /dev/null
+++ b/IntrovertIM_CSharp/IntrovertIM_CSharp.csproj
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IntrovertIM_CSharp/IntrovertIM_CSharp.sln b/IntrovertIM_CSharp/IntrovertIM_CSharp.sln
new file mode 100644
index 0000000..5d81286
--- /dev/null
+++ b/IntrovertIM_CSharp/IntrovertIM_CSharp.sln
@@ -0,0 +1,29 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntrovertIM_CSharp", "IntrovertIM_CSharp.csproj", "{27805798-5EDF-44AE-8274-C3238C6448EC}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalInterfaceProxy", "ExternalInterfaceProxy\ExternalInterfaceProxy.csproj", "{0138EE1D-8B87-4E9E-BD98-05100C3E32C7}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {27805798-5EDF-44AE-8274-C3238C6448EC}.Debug.ActiveCfg = Debug|.NET
+ {27805798-5EDF-44AE-8274-C3238C6448EC}.Debug.Build.0 = Debug|.NET
+ {27805798-5EDF-44AE-8274-C3238C6448EC}.Release.ActiveCfg = Release|.NET
+ {27805798-5EDF-44AE-8274-C3238C6448EC}.Release.Build.0 = Release|.NET
+ {0138EE1D-8B87-4E9E-BD98-05100C3E32C7}.Debug.ActiveCfg = Debug|.NET
+ {0138EE1D-8B87-4E9E-BD98-05100C3E32C7}.Debug.Build.0 = Debug|.NET
+ {0138EE1D-8B87-4E9E-BD98-05100C3E32C7}.Release.ActiveCfg = Release|.NET
+ {0138EE1D-8B87-4E9E-BD98-05100C3E32C7}.Release.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/IntrovertIM_CSharp/bin/Debug/AxInterop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/bin/Debug/AxInterop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..df49b50
Binary files /dev/null and b/IntrovertIM_CSharp/bin/Debug/AxInterop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/bin/Debug/ExternalInterfaceProxy.dll b/IntrovertIM_CSharp/bin/Debug/ExternalInterfaceProxy.dll
new file mode 100644
index 0000000..a559d92
Binary files /dev/null and b/IntrovertIM_CSharp/bin/Debug/ExternalInterfaceProxy.dll differ
diff --git a/IntrovertIM_CSharp/bin/Debug/Interop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/bin/Debug/Interop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..834b406
Binary files /dev/null and b/IntrovertIM_CSharp/bin/Debug/Interop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/bin/Debug/IntrovertIMApp.swf b/IntrovertIM_CSharp/bin/Debug/IntrovertIMApp.swf
new file mode 100644
index 0000000..e8a45ed
Binary files /dev/null and b/IntrovertIM_CSharp/bin/Debug/IntrovertIMApp.swf differ
diff --git a/IntrovertIM_CSharp/bin/Debug/IntrovertIM_CSharp.exe b/IntrovertIM_CSharp/bin/Debug/IntrovertIM_CSharp.exe
new file mode 100644
index 0000000..ca6040f
Binary files /dev/null and b/IntrovertIM_CSharp/bin/Debug/IntrovertIM_CSharp.exe differ
diff --git a/IntrovertIM_CSharp/obj/AxInterop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/obj/AxInterop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..df49b50
Binary files /dev/null and b/IntrovertIM_CSharp/obj/AxInterop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.AppForm.resources b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.AppForm.resources
new file mode 100644
index 0000000..6694d6f
Binary files /dev/null and b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.AppForm.resources differ
diff --git a/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.exe b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.exe
new file mode 100644
index 0000000..ca6040f
Binary files /dev/null and b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.exe differ
diff --git a/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.projdata b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.projdata
new file mode 100644
index 0000000..68c8379
Binary files /dev/null and b/IntrovertIM_CSharp/obj/Debug/IntrovertIM_CSharp.projdata differ
diff --git a/IntrovertIM_CSharp/obj/Interop.ShockwaveFlashObjects.dll b/IntrovertIM_CSharp/obj/Interop.ShockwaveFlashObjects.dll
new file mode 100644
index 0000000..834b406
Binary files /dev/null and b/IntrovertIM_CSharp/obj/Interop.ShockwaveFlashObjects.dll differ
diff --git a/IntrovertIM_HTML/IntrovertIMApp.fla b/IntrovertIM_HTML/IntrovertIMApp.fla
new file mode 100644
index 0000000..001f449
Binary files /dev/null and b/IntrovertIM_HTML/IntrovertIMApp.fla differ
diff --git a/IntrovertIM_HTML/IntrovertIMApp.mxml b/IntrovertIM_HTML/IntrovertIMApp.mxml
new file mode 100644
index 0000000..029430b
--- /dev/null
+++ b/IntrovertIM_HTML/IntrovertIMApp.mxml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IntrovertIM_HTML/ReadMe.txt b/IntrovertIM_HTML/ReadMe.txt
new file mode 100644
index 0000000..a86982d
--- /dev/null
+++ b/IntrovertIM_HTML/ReadMe.txt
@@ -0,0 +1,9 @@
+Notes for the IntrovertIM_HTML example:
+
+Flex version:
+The source code for this example includes the HTML document with JavaScript code that is used to complete the application, in the "html-template" folder. That is the folder where Flex Builder puts its own HTML template. By default Flex Builder overwrites the html-template folder's contents, so to run this example you will want to keep a separate copy of the html-template folder and paste it in once Flex Builder has created the files in the "html-template" folder.
+
+Flash version:
+The HTML page containing the JavaScript for this example is in the "html-flash" folder. This is the folder where the Flash is set to publish the SWF. The FLA file's publish settings are set to not publish an HTML page by default, because the new page would override the predefined HTML page.
+To test the application, publish the Flash movie, then open the IntrovertIMApp.html page from the "html-flash" folder in your web browser.
+In order to test the IntrovertIM HTML application on your hard drive, you will need to designate the "html-flash" folder as a trusted location; otherwise you will see a security error when you view the page in a browser.
\ No newline at end of file
diff --git a/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMManager.as b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMManager.as
new file mode 100644
index 0000000..0c1d153
--- /dev/null
+++ b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMManager.as
@@ -0,0 +1,167 @@
+package com.example.programmingas3.introvertIM
+{
+ import flash.events.EventDispatcher;
+ import flash.events.TimerEvent;
+ import flash.external.ExternalInterface;
+ import flash.utils.Timer;
+ import com.example.programmingas3.introvertIM.IMMessageEvent;
+ import com.example.programmingas3.introvertIM.IMStatus;
+
+ public class IMManager extends EventDispatcher
+ {
+ // ------- Private vars -------
+ /**
+ * Keeps track of the current "availability" status of the user.
+ */
+ private var _status:IMStatus;
+
+
+ // ------- Constructor -------
+ public function IMManager(initialStatus:IMStatus)
+ {
+ _status = initialStatus;
+
+ // check if the container is able to use the External API
+ if (ExternalInterface.available)
+ {
+ try
+ {
+ // This calls the isContainerReady() method, which in turn calls
+ // the container to see if Flash Player has loaded and the container
+ // is ready to receive calls from the SWF.
+ var containerReady:Boolean = isContainerReady();
+ if (containerReady)
+ {
+ // if the container is ready, register the SWF's functions
+ setupCallbacks();
+ }
+ else
+ {
+ // If the container is not ready, set up a Timer to call the
+ // container at 100ms intervals. Once the container responds that
+ // it's ready, the timer will be stopped.
+ var readyTimer:Timer = new Timer(100);
+ readyTimer.addEventListener(TimerEvent.TIMER, timerHandler);
+ readyTimer.start();
+ }
+ }
+ catch (error:SecurityError)
+ {
+ trace("A SecurityError occurred: " + error.message + "\n");
+ throw error;
+ }
+ catch (error:Error)
+ {
+ trace("An Error occurred: " + error.message + "\n");
+ throw error;
+ }
+ }
+ else
+ {
+ trace("External interface is not available for this container.");
+ }
+ }
+
+
+ // ------- Public Properties -------
+ /**
+ * Gets or sets the user's current availability.
+ */
+ public function get status():IMStatus
+ {
+ return _status;
+ }
+ public function set status(newStatus:IMStatus):void
+ {
+ _status = newStatus;
+ // notify the container that the SWF user's status has changed
+ ExternalInterface.call("statusChange");
+ }
+
+
+ // -------- Public Methods -------
+ /**
+ * Sends a new message to the container IM client
+ * @param message The message to send
+ */
+ public function sendMessage(message:String):void
+ {
+ ExternalInterface.call("newMessage", message);
+ }
+
+
+ // ------- Externally-callable Methods -------
+ /**
+ * Called by the container to send a new message to the SWF client.
+ * @param message The message that was sent
+ */
+ private function newMessage(message:String):void
+ {
+ // notify listeners (i.e. the UI) that there's a new message;
+ // the UI will then update itself accordingly
+ var ev:IMMessageEvent = new IMMessageEvent(message);
+ dispatchEvent(ev);
+ }
+
+
+ /**
+ * Called by the container to retrieve the current status
+ * @return The current status of the SWF IM client
+ */
+ private function getStatus():String
+ {
+ return status.toString();
+ }
+
+
+ // ------- Private Methods -------
+ /**
+ * Calls the container's isReady() function, to check if the container is loaded
+ * and ready to communicate with the SWF file.
+ * @return Whether the container is ready to communicate with ActionScript.
+ */
+ private function isContainerReady():Boolean
+ {
+ var result:Boolean = ExternalInterface.call("isReady");
+ return result;
+ }
+
+
+ /**
+ * Registers the appropriate ActionScript functions with the container, so that
+ * they can be called, and calls the "setSWFIsReady()" function in the container
+ * which tells the container that the SWF file is ready to receive function calls.
+ */
+ private function setupCallbacks():void
+ {
+ // register the SWF client functions with the container
+ ExternalInterface.addCallback("newMessage", newMessage);
+ ExternalInterface.addCallback("getStatus", getStatus);
+ // notify the container that the SWF is ready to be called.
+ ExternalInterface.call("setSWFIsReady");
+ }
+
+
+ /**
+ * Handles the timer event; this function is called by the timer each
+ * time the elapsed time has been reached.
+ * The net effect is that on regular intervals this function checks
+ * to see if the container is ready to receive communication.
+ * @param event The event object for the Timer event.
+ */
+ private function timerHandler(event:TimerEvent):void
+ {
+ // check if the container is now ready
+ var isReady:Boolean = isContainerReady();
+ if (isReady)
+ {
+ // If the container has become ready, we don't need to check anymore,
+ // so stop the timer.
+ Timer(event.target).stop();
+ // Set up the ActionScript methods that will be available to be
+ // called by the container.
+ setupCallbacks();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMMessageEvent.as b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMMessageEvent.as
new file mode 100644
index 0000000..c296fc3
--- /dev/null
+++ b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMMessageEvent.as
@@ -0,0 +1,41 @@
+package com.example.programmingas3.introvertIM
+{
+ import flash.events.Event;
+
+ public class IMMessageEvent extends Event
+ {
+ // ------- Public Constants -------
+ public static const NEW_MESSAGE:String = "newmessage";
+
+
+ // ------- Private vars -------
+ private var _message:String;
+
+
+ // ------- Constructor -------
+ public function IMMessageEvent(message:String)
+ {
+ super(NEW_MESSAGE);
+ _message = message;
+ }
+
+
+ // ------- Public Properties -------
+ public function get message():String
+ {
+ return _message;
+ }
+
+
+ // ------- Event Method Overrides -------
+ public override function clone():Event
+ {
+ return new IMMessageEvent(_message);
+ }
+
+ public override function toString():String
+ {
+ return formatToString("IMMessageEvent", "type", "bubbles", "cancelable", "eventPhase", "message");
+ }
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMStatus.as b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMStatus.as
new file mode 100644
index 0000000..a4ab9dc
--- /dev/null
+++ b/IntrovertIM_HTML/com/example/programmingas3/introvertIM/IMStatus.as
@@ -0,0 +1,65 @@
+package com.example.programmingas3.introvertIM
+{
+ public final class IMStatus
+ {
+ // ------- "Enum" members -------
+ public static var NeedAFriend:IMStatus = new IMStatus(1, "Please talk to me!");
+ public static var ImHere:IMStatus = new IMStatus(2, "I'm here");
+ public static var NotTooBusy:IMStatus = new IMStatus(3, "Not too busy for you");
+ public static var EmergenciesOnly:IMStatus = new IMStatus(4, "Emergencies Only");
+ public static var GoAway:IMStatus = new IMStatus(5, "Go Away!");
+
+
+ /**
+ * An array containing all the available statuses, for populating the list
+ * of statuses.
+ */
+ public static const allStatuses:Array = [
+ NeedAFriend,
+ ImHere,
+ NotTooBusy,
+ EmergenciesOnly,
+ GoAway
+ ];
+
+ // ------- Private vars -------
+ private var _uid:uint;
+ private var _displayText:String;
+
+
+ // ------- Constructor -------
+ public function IMStatus(uid:uint, displayText:String)
+ {
+ _uid = uid;
+ _displayText = displayText;
+ }
+
+
+ // ------- Public Accessors -------
+
+ /**
+ * Provides a String representation of the current IMStatus object.
+ * This property is provided to satisfy the Flash components, which require a "label" field on data items.
+ */
+ public function get label():String
+ {
+ return this.toString();
+ }
+
+ public function get icon():String
+ {
+ return null;
+ }
+
+ // ------- Public Methods -------
+
+ /**
+ * Provides a String representation of the current IMStatus object.
+ * @return The appropriate String
+ */
+ public function toString():String
+ {
+ return (_displayText != null && _displayText.length > 0) ? _displayText : "[Object IMStatus uid:" + _uid.toString() + "]";
+ }
+ }
+}
\ No newline at end of file
diff --git a/IntrovertIM_HTML/html-flash/AC_OETags.js b/IntrovertIM_HTML/html-flash/AC_OETags.js
new file mode 100644
index 0000000..2c70d28
--- /dev/null
+++ b/IntrovertIM_HTML/html-flash/AC_OETags.js
@@ -0,0 +1,270 @@
+// Flash Player Version Detection - Rev 1.5
+// Detect Client Browser type
+// Copyright 2006 Adobe Systems, Inc. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+function ControlVersion()
+{
+ var version;
+ var axo;
+ var e;
+
+ // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry
+
+ try {
+ // version will be set for 7.X or greater players
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 6.X players only
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+
+ // installed player is some revision of 6.0
+ // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
+ // so we have to be careful.
+
+ // default to the first public version
+ version = "WIN 6,0,21,0";
+
+ // throws if AllowScripAccess does not exist (introduced in 6.0r47)
+ axo.AllowScriptAccess = "always";
+
+ // safe to call for 6.0r47 or greater
+ version = axo.GetVariable("$version");
+
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 4.X or 5.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 3.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = "WIN 3,0,18,0";
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 2.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ version = "WIN 2,0,0,11";
+ } catch (e) {
+ version = -1;
+ }
+ }
+
+ return version;
+}
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ if ( descArray[3] != "" ) {
+ tempArrayMinor = descArray[3].split("r");
+ } else {
+ tempArrayMinor = descArray[4].split("r");
+ }
+ var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+//v1.0
+//Copyright 2006 Adobe Systems, Inc. All rights reserved.
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '';
+ } else {
+ str += '';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ case "id":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
diff --git a/IntrovertIM_HTML/html-flash/IntrovertIMApp.html b/IntrovertIM_HTML/html-flash/IntrovertIMApp.html
new file mode 100644
index 0000000..513da22
--- /dev/null
+++ b/IntrovertIM_HTML/html-flash/IntrovertIMApp.html
@@ -0,0 +1,136 @@
+
+
+
+IntrovertIMApp
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/IntrovertIM_HTML/html-template/AC_OETags.js b/IntrovertIM_HTML/html-template/AC_OETags.js
new file mode 100644
index 0000000..2c64e6d
--- /dev/null
+++ b/IntrovertIM_HTML/html-template/AC_OETags.js
@@ -0,0 +1,203 @@
+// Flash Player Version Detection - Rev 1.5
+// Detect Client Browser type
+// Copyright 2006 Adobe Systems, Inc. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(i){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ if ( descArray[3] != "" ) {
+ tempArrayMinor = descArray[3].split("r");
+ } else {
+ tempArrayMinor = descArray[4].split("r");
+ }
+ var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ try{
+ var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ for (var i=3; axo!=null; i++) {
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+i);
+ flashVer = axo.GetVariable("$version");
+ }
+ }catch(e){}
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ reqVer = parseFloat(reqMajorVer + "." + reqRevision);
+ // loop backwards through the versions until we find the newest version
+ for (i=25;i>0;i--) {
+ versionStr = GetSwfVer(i);
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ tempArray = versionStr.split(" ");
+ tempString = tempArray[1];
+ versionArray = tempString .split(",");
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ var versionString = versionMajor + "." + versionRevision; // 7.0r24 == 7.24
+ var versionNum = parseFloat(versionString);
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > reqMajorVer) {
+ return true;
+ } else if (versionMajor == reqMajorVer) {
+ if (versionMinor > reqMinorVer)
+ return true;
+ else if (versionMinor == reqMinorVer) {
+ if (versionRevision >= reqRevision)
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
+
+//v1.0
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "id":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
diff --git a/IntrovertIM_HTML/html-template/index.template.html b/IntrovertIM_HTML/html-template/index.template.html
new file mode 100644
index 0000000..b466634
--- /dev/null
+++ b/IntrovertIM_HTML/html-template/index.template.html
@@ -0,0 +1,137 @@
+
+
+
+
+${application}
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a50eacf
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,360 @@
+Creative Commons Legal Code
+
+Attribution-NonCommercial-ShareAlike 3.0 Unported
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+ DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
+
+1. Definitions
+
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+ other pre-existing works, such as a translation, adaptation,
+ derivative work, arrangement of music or other alterations of a
+ literary or artistic work, or phonogram or performance and includes
+ cinematographic adaptations or any other form in which the Work may be
+ recast, transformed, or adapted including in any form recognizably
+ derived from the original, except that a work that constitutes a
+ Collection will not be considered an Adaptation for the purpose of
+ this License. For the avoidance of doubt, where the Work is a musical
+ work, performance or phonogram, the synchronization of the Work in
+ timed-relation with a moving image ("synching") will be considered an
+ Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+ encyclopedias and anthologies, or performances, phonograms or
+ broadcasts, or other works or subject matter other than works listed
+ in Section 1(g) below, which, by reason of the selection and
+ arrangement of their contents, constitute intellectual creations, in
+ which the Work is included in its entirety in unmodified form along
+ with one or more other contributions, each constituting separate and
+ independent works in themselves, which together are assembled into a
+ collective whole. A work that constitutes a Collection will not be
+ considered an Adaptation (as defined above) for the purposes of this
+ License.
+ c. "Distribute" means to make available to the public the original and
+ copies of the Work or Adaptation, as appropriate, through sale or
+ other transfer of ownership.
+ d. "License Elements" means the following high-level license attributes
+ as selected by Licensor and indicated in the title of this License:
+ Attribution, Noncommercial, ShareAlike.
+ e. "Licensor" means the individual, individuals, entity or entities that
+ offer(s) the Work under the terms of this License.
+ f. "Original Author" means, in the case of a literary or artistic work,
+ the individual, individuals, entity or entities who created the Work
+ or if no individual or entity can be identified, the publisher; and in
+ addition (i) in the case of a performance the actors, singers,
+ musicians, dancers, and other persons who act, sing, deliver, declaim,
+ play in, interpret or otherwise perform literary or artistic works or
+ expressions of folklore; (ii) in the case of a phonogram the producer
+ being the person or legal entity who first fixes the sounds of a
+ performance or other sounds; and, (iii) in the case of broadcasts, the
+ organization that transmits the broadcast.
+ g. "Work" means the literary and/or artistic work offered under the terms
+ of this License including without limitation any production in the
+ literary, scientific and artistic domain, whatever may be the mode or
+ form of its expression including digital form, such as a book,
+ pamphlet and other writing; a lecture, address, sermon or other work
+ of the same nature; a dramatic or dramatico-musical work; a
+ choreographic work or entertainment in dumb show; a musical
+ composition with or without words; a cinematographic work to which are
+ assimilated works expressed by a process analogous to cinematography;
+ a work of drawing, painting, architecture, sculpture, engraving or
+ lithography; a photographic work to which are assimilated works
+ expressed by a process analogous to photography; a work of applied
+ art; an illustration, map, plan, sketch or three-dimensional work
+ relative to geography, topography, architecture or science; a
+ performance; a broadcast; a phonogram; a compilation of data to the
+ extent it is protected as a copyrightable work; or a work performed by
+ a variety or circus performer to the extent it is not otherwise
+ considered a literary or artistic work.
+ h. "You" means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License with
+ respect to the Work, or who has received express permission from the
+ Licensor to exercise rights under this License despite a previous
+ violation.
+ i. "Publicly Perform" means to perform public recitations of the Work and
+ to communicate to the public those public recitations, by any means or
+ process, including by wire or wireless means or public digital
+ performances; to make available to the public Works in such a way that
+ members of the public may access these Works from a place and at a
+ place individually chosen by them; to perform the Work to the public
+ by any means or process and the communication to the public of the
+ performances of the Work, including by public digital performance; to
+ broadcast and rebroadcast the Work by any means including signs,
+ sounds or images.
+ j. "Reproduce" means to make copies of the Work by any means including
+ without limitation by sound or visual recordings and the right of
+ fixation and reproducing fixations of the Work, including storage of a
+ protected performance or phonogram in digital form or other electronic
+ medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ a. to Reproduce the Work, to incorporate the Work into one or more
+ Collections, and to Reproduce the Work as incorporated in the
+ Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+ including any translation in any medium, takes reasonable steps to
+ clearly label, demarcate or otherwise identify that changes were made
+ to the original Work. For example, a translation could be marked "The
+ original work was translated from English to Spanish," or a
+ modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+ in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved, including but not limited to the
+rights described in Section 4(e).
+
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
+
+ a. You may Distribute or Publicly Perform the Work only under the terms
+ of this License. You must include a copy of, or the Uniform Resource
+ Identifier (URI) for, this License with every copy of the Work You
+ Distribute or Publicly Perform. You may not offer or impose any terms
+ on the Work that restrict the terms of this License or the ability of
+ the recipient of the Work to exercise the rights granted to that
+ recipient under the terms of the License. You may not sublicense the
+ Work. You must keep intact all notices that refer to this License and
+ to the disclaimer of warranties with every copy of the Work You
+ Distribute or Publicly Perform. When You Distribute or Publicly
+ Perform the Work, You may not impose any effective technological
+ measures on the Work that restrict the ability of a recipient of the
+ Work from You to exercise the rights granted to that recipient under
+ the terms of the License. This Section 4(a) applies to the Work as
+ incorporated in a Collection, but this does not require the Collection
+ apart from the Work itself to be made subject to the terms of this
+ License. If You create a Collection, upon notice from any Licensor You
+ must, to the extent practicable, remove from the Collection any credit
+ as required by Section 4(d), as requested. If You create an
+ Adaptation, upon notice from any Licensor You must, to the extent
+ practicable, remove from the Adaptation any credit as required by
+ Section 4(d), as requested.
+ b. You may Distribute or Publicly Perform an Adaptation only under: (i)
+ the terms of this License; (ii) a later version of this License with
+ the same License Elements as this License; (iii) a Creative Commons
+ jurisdiction license (either this or a later license version) that
+ contains the same License Elements as this License (e.g.,
+ Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License").
+ You must include a copy of, or the URI, for Applicable License with
+ every copy of each Adaptation You Distribute or Publicly Perform. You
+ may not offer or impose any terms on the Adaptation that restrict the
+ terms of the Applicable License or the ability of the recipient of the
+ Adaptation to exercise the rights granted to that recipient under the
+ terms of the Applicable License. You must keep intact all notices that
+ refer to the Applicable License and to the disclaimer of warranties
+ with every copy of the Work as included in the Adaptation You
+ Distribute or Publicly Perform. When You Distribute or Publicly
+ Perform the Adaptation, You may not impose any effective technological
+ measures on the Adaptation that restrict the ability of a recipient of
+ the Adaptation from You to exercise the rights granted to that
+ recipient under the terms of the Applicable License. This Section 4(b)
+ applies to the Adaptation as incorporated in a Collection, but this
+ does not require the Collection apart from the Adaptation itself to be
+ made subject to the terms of the Applicable License.
+ c. You may not exercise any of the rights granted to You in Section 3
+ above in any manner that is primarily intended for or directed toward
+ commercial advantage or private monetary compensation. The exchange of
+ the Work for other copyrighted works by means of digital file-sharing
+ or otherwise shall not be considered to be intended for or directed
+ toward commercial advantage or private monetary compensation, provided
+ there is no payment of any monetary compensation in con-nection with
+ the exchange of copyrighted works.
+ d. If You Distribute, or Publicly Perform the Work or any Adaptations or
+ Collections, You must, unless a request has been made pursuant to
+ Section 4(a), keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i) the
+ name of the Original Author (or pseudonym, if applicable) if supplied,
+ and/or if the Original Author and/or Licensor designate another party
+ or parties (e.g., a sponsor institute, publishing entity, journal) for
+ attribution ("Attribution Parties") in Licensor's copyright notice,
+ terms of service or by other reasonable means, the name of such party
+ or parties; (ii) the title of the Work if supplied; (iii) to the
+ extent reasonably practicable, the URI, if any, that Licensor
+ specifies to be associated with the Work, unless such URI does not
+ refer to the copyright notice or licensing information for the Work;
+ and, (iv) consistent with Section 3(b), in the case of an Adaptation,
+ a credit identifying the use of the Work in the Adaptation (e.g.,
+ "French translation of the Work by Original Author," or "Screenplay
+ based on original Work by Original Author"). The credit required by
+ this Section 4(d) may be implemented in any reasonable manner;
+ provided, however, that in the case of a Adaptation or Collection, at
+ a minimum such credit will appear, if a credit for all contributing
+ authors of the Adaptation or Collection appears, then as part of these
+ credits and in a manner at least as prominent as the credits for the
+ other contributing authors. For the avoidance of doubt, You may only
+ use the credit required by this Section for the purpose of attribution
+ in the manner set out above and, by exercising Your rights under this
+ License, You may not implicitly or explicitly assert or imply any
+ connection with, sponsorship or endorsement by the Original Author,
+ Licensor and/or Attribution Parties, as appropriate, of You or Your
+ use of the Work, without the separate, express prior written
+ permission of the Original Author, Licensor and/or Attribution
+ Parties.
+ e. For the avoidance of doubt:
+
+ i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme cannot be waived, the Licensor
+ reserves the exclusive right to collect such royalties for any
+ exercise by You of the rights granted under this License;
+ ii. Waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme can be waived, the Licensor reserves
+ the exclusive right to collect such royalties for any exercise by
+ You of the rights granted under this License if Your exercise of
+ such rights is for a purpose or use which is otherwise than
+ noncommercial as permitted under Section 4(c) and otherwise waives
+ the right to collect royalties through any statutory or compulsory
+ licensing scheme; and,
+ iii. Voluntary License Schemes. The Licensor reserves the right to
+ collect royalties, whether individually or, in the event that the
+ Licensor is a member of a collecting society that administers
+ voluntary licensing schemes, via that society, from any exercise
+ by You of the rights granted under this License that is for a
+ purpose or use which is otherwise than noncommercial as permitted
+ under Section 4(c).
+ f. Except as otherwise agreed in writing by the Licensor or as may be
+ otherwise permitted by applicable law, if You Reproduce, Distribute or
+ Publicly Perform the Work either by itself or as part of any
+ Adaptations or Collections, You must not distort, mutilate, modify or
+ take other derogatory action in relation to the Work which would be
+ prejudicial to the Original Author's honor or reputation. Licensor
+ agrees that in those jurisdictions (e.g. Japan), in which any exercise
+ of the right granted in Section 3(b) of this License (the right to
+ make Adaptations) would be deemed to be a distortion, mutilation,
+ modification or other derogatory action prejudicial to the Original
+ Author's honor and reputation, the Licensor will waive or not assert,
+ as appropriate, this Section, to the fullest extent permitted by the
+ applicable national law, to enable You to reasonably exercise Your
+ right under Section 3(b) of this License (right to make Adaptations)
+ but not otherwise.
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE
+FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS
+AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
+WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
+LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
+ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
+DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
+WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ a. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Adaptations or Collections
+ from You under this License, however, will not have their licenses
+ terminated provided such individuals or entities remain in full
+ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+ survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+ perpetual (for the duration of the applicable copyright in the Work).
+ Notwithstanding the above, Licensor reserves the right to release the
+ Work under different license terms or to stop distributing the Work at
+ any time; provided, however that any such election will not serve to
+ withdraw this License (or any other license that has been, or is
+ required to be, granted under the terms of this License), and this
+ License will continue in full force and effect unless terminated as
+ stated above.
+
+8. Miscellaneous
+
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+ the Licensor offers to the recipient a license to the Work on the same
+ terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+ offers to the recipient a license to the original Work on the same
+ terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability of
+ the remainder of the terms of this License, and without further action
+ by the parties to this agreement, such provision shall be reformed to
+ the minimum extent necessary to make such provision valid and
+ enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in writing
+ and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+ respect to the Work licensed here. There are no understandings,
+ agreements or representations with respect to the Work not specified
+ here. Licensor shall not be bound by any additional provisions that
+ may appear in any communication from You. This License may not be
+ modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+ License were drafted utilizing the terminology of the Berne Convention
+ for the Protection of Literary and Artistic Works (as amended on
+ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+ and the Universal Copyright Convention (as revised on July 24, 1971).
+ These rights and subject matter take effect in the relevant
+ jurisdiction in which the License terms are sought to be enforced
+ according to the corresponding provisions of the implementation of
+ those treaty provisions in the applicable national law. If the
+ standard suite of rights granted under applicable copyright law
+ includes additional rights not granted under this License, such
+ additional rights are deemed to be included in the License; this
+ License is not intended to restrict the license of any rights under
+ applicable law.
+
+
+Creative Commons Notice
+
+ Creative Commons is not a party to this License, and makes no warranty
+ whatsoever in connection with the Work. Creative Commons will not be
+ liable to You or any party on any legal theory for any damages
+ whatsoever, including without limitation any general, special,
+ incidental or consequential damages arising in connection to this
+ license. Notwithstanding the foregoing two (2) sentences, if Creative
+ Commons has expressly identified itself as the Licensor hereunder, it
+ shall have all rights and obligations of Licensor.
+
+ Except for the limited purpose of indicating to the public that the
+ Work is licensed under the CCPL, Creative Commons does not authorize
+ the use by either party of the trademark "Creative Commons" or any
+ related trademark or logo of Creative Commons without the prior
+ written consent of Creative Commons. Any permitted use will be in
+ compliance with Creative Commons' then-current trademark usage
+ guidelines, as may be published on its website or otherwise made
+ available upon request from time to time. For the avoidance of doubt,
+ this trademark restriction does not form part of this License.
+
+ Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/NewsLayout/NewsLayout.fla b/NewsLayout/NewsLayout.fla
new file mode 100644
index 0000000..0c82a49
Binary files /dev/null and b/NewsLayout/NewsLayout.fla differ
diff --git a/NewsLayout/NewsLayout.mxml b/NewsLayout/NewsLayout.mxml
new file mode 100644
index 0000000..47cc345
--- /dev/null
+++ b/NewsLayout/NewsLayout.mxml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/NewsLayout/com/example/programmingas3/newslayout/FormattedTextField.as b/NewsLayout/com/example/programmingas3/newslayout/FormattedTextField.as
new file mode 100644
index 0000000..84816a0
--- /dev/null
+++ b/NewsLayout/com/example/programmingas3/newslayout/FormattedTextField.as
@@ -0,0 +1,116 @@
+package com.example.programmingas3.newslayout
+{
+ import flash.text.TextFieldAutoSize;
+ import flash.text.TextFormat;
+ import flash.text.TextFormatAlign;
+
+ import flash.text.TextLineMetrics;
+ import flash.text.TextField;
+
+ /**
+ *
+ */
+ public class FormattedTextField extends flash.text.TextField
+ {
+ private var _format:TextFormat;
+
+ public var preferredWidth:Number = 300;
+ public var preferredHeight:Number = 100;
+
+ public function FormattedTextField(tf:TextFormat = null)
+ {
+ super();
+
+ this.autoSize = TextFieldAutoSize.NONE;
+ this.wordWrap = true;
+
+ if (tf != null)
+ {
+ _format = tf;
+ }
+ else
+ {
+ _format = getDefaultTextFormat();
+ }
+
+ //this.setStyle(_format);
+ }
+
+ private function getDefaultTextFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = false;
+ format.leading = 0;
+ return format;
+ }
+
+ public function changeFace(faceName:String="Verdana"):void
+ {
+ if (faceName != null)
+ {
+ this._format.font = faceName;
+ this.setTextFormat(this._format);
+ }
+ }
+
+ public function changeSize(size:uint=12):void
+ {
+ if (size > 5)
+ {
+ this._format.size = size;
+ this.setTextFormat(this._format);
+ }
+ }
+
+ public function changeBold(isBold:Boolean = false):void
+ {
+ //this.setStyle("fontWeight", isBold ? "bold" : "normal");
+ this._format.bold = isBold;
+ this.setTextFormat(this._format);
+ }
+
+ public function changeItalic(isItalic:Boolean = false):void
+ {
+ //this.setStyle("fontStyle", isItalic ? flash.text.FontStyle.ITALIC : flash.text.FontStyle.REGULAR );
+ this._format.italic = isItalic;
+ this.setTextFormat(this._format);
+ }
+
+ public function changeNormal(isNormal:Boolean = false):void
+ {
+ this._format.italic = false;
+ this._format.bold = false;
+ this.setTextFormat(this._format);
+ }
+
+ public function changeSpacing(spacing:int=1):void
+ {
+ if (spacing > -10 && spacing < 100)
+ {
+ this._format.letterSpacing = spacing;
+ this.setTextFormat(this._format);
+ }
+ }
+
+ public function changeLeading(leading:int=0):void
+ {
+ if (leading > -100 && leading < 100)
+ {
+ this._format.leading = leading;
+ this.setTextFormat(this._format);
+ }
+ }
+
+ public function changeAlign(align:String = "left"):void
+ {
+ if (align == TextFormatAlign.LEFT || align == TextFormatAlign.RIGHT ||
+ align == TextFormatAlign.JUSTIFY || align == TextFormatAlign.CENTER)
+ {
+ this._format.align = align;
+ this.setTextFormat(this._format);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NewsLayout/com/example/programmingas3/newslayout/HeadlineTextField.as b/NewsLayout/com/example/programmingas3/newslayout/HeadlineTextField.as
new file mode 100644
index 0000000..414e9de
--- /dev/null
+++ b/NewsLayout/com/example/programmingas3/newslayout/HeadlineTextField.as
@@ -0,0 +1,115 @@
+package com.example.programmingas3.newslayout
+{
+ import flash.text.TextFieldAutoSize;
+ import flash.text.TextFormat;
+ import flash.text.TextFormatAlign;
+
+ import flash.text.TextLineMetrics;
+ import flash.text.TextField;
+
+ /**
+ * Changes the size of the text to fit a given width and number of lines.
+ * Useful for news headlines that should extend across a full column.
+ *
+ * We need to know:
+ * - which font family, weight, and style to use
+ * - the max width of the headline
+ * - the max height in pixels of the headline
+ * - the max number of lines
+ *
+ * Algorithm 1:
+ * - figure out the N-width of a character that should work based on pixelWidth / numChars
+ * - translate that N-width into a point size
+ * - try the point size, if outside of tolerance,;
+ * - if too wide, adjust down a point, try again
+ * - if too small, adjust up a point, try again
+ * - if too wide last time, too small this time or vice versa, stick with the too small size
+ *
+ * Pixels per character (width-wise) is roughly 1/2 the point size, so that's a good starting
+ * point. So to get a starting point size, divide the overall width by the number of characters
+ * to get pixels-per-character, then double to get the point size.
+ */
+ public class HeadlineTextField extends FormattedTextField
+ {
+
+ public static var MIN_POINT_SIZE:uint = 6;
+ public static var MAX_POINT_SIZE:uint = 128;
+
+ public function HeadlineTextField(tf:TextFormat = null)
+ {
+ super(tf);
+ this.autoSize = TextFieldAutoSize.LEFT;
+ }
+
+ 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)
+ {
+ // 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;
+ }
+
+ this.changeSize(pointSize);
+
+ if (this.numLines > maxLines)
+ {
+ return shrinkText(pointSize - 1, maxLines);
+ }
+ else
+ {
+ return pointSize;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NewsLayout/com/example/programmingas3/newslayout/MultiColumnText.as b/NewsLayout/com/example/programmingas3/newslayout/MultiColumnText.as
new file mode 100644
index 0000000..1d0b12e
--- /dev/null
+++ b/NewsLayout/com/example/programmingas3/newslayout/MultiColumnText.as
@@ -0,0 +1,332 @@
+package com.example.programmingas3.newslayout
+{
+ import flash.display.Sprite;
+ import flash.text.TextField;
+ import flash.text.TextFieldAutoSize;
+ import flash.text.TextFormat;
+ import flash.text.TextFormatAlign;
+ import flash.text.TextLineMetrics;
+ import flash.events.TextEvent;
+ import flash.events.Event;
+
+ public class MultiColumnText extends Sprite
+ {
+ public var fieldArray:Array;
+
+ public var numColumns:uint = 2;
+
+ public var lineHeight:uint = 16;
+ public var linesPerCol:uint = 15;
+
+ /**
+ * The space between columns.
+ */
+ public var gutter:uint = 10;
+
+ public var format:TextFormat;
+ public var lastLineFormat:TextFormat;
+ public var firstLineFormat:TextFormat;
+
+ private var _text:String = "";
+
+ public var preferredWidth:Number = 400;
+ public var preferredHeight:Number = 100;
+
+ /**
+ * The width of each column.
+ */
+ public var colWidth:int = 200;
+
+
+ public function MultiColumnText(cols:uint = 2,
+ gutter:uint = 10, w:Number = 400, h:Number = 100,
+ tf:TextFormat = null):void
+ {
+ this.numColumns = Math.max(1, cols);
+ this.gutter = Math.max(1, gutter);
+
+ this.preferredWidth = w;
+ this.preferredHeight = h;
+
+ if (tf != null)
+ {
+ applyTextFormat(tf);
+ }
+
+ this.setColumnWidth();
+
+ this.fieldArray = new Array();
+
+ // Create a text field for each column
+ for (var i:int = 0; i < cols; i++)
+ {
+ var field:TextField = new TextField();
+ field.multiline = true;
+ field.autoSize = TextFieldAutoSize.NONE;
+ field.wordWrap = true;
+ field.width = this.colWidth;
+
+ if (tf != null)
+ {
+ field.setTextFormat(tf);
+ }
+
+ this.fieldArray.push(field);
+ this.addChild(field);
+ }
+ }
+
+ public function applyTextFormat(tf:TextFormat):void
+ {
+ this.format = tf;
+
+ // The last line format starts as a direct copy
+ this.lastLineFormat = new TextFormat(tf.font, tf.size, tf.color, tf.bold,
+ tf.italic, tf.underline, tf.url,
+ tf.target, tf.align, tf.leftMargin,
+ tf.rightMargin, tf.indent, tf.leading);
+ this.lastLineFormat["letterSpacing"] = this.format["letterSpacing"];
+
+ // The first line format removes the indent value
+ this.firstLineFormat = new TextFormat(tf.font, tf.size, tf.color, tf.bold,
+ tf.italic, tf.underline, tf.url,
+ tf.target, tf.align, tf.leftMargin,
+ tf.rightMargin, 0, tf.leading);
+ this.firstLineFormat["letterSpacing"] = this.format["letterSpacing"];
+ }
+
+ /**
+ * Spread the text across multiple text fields.
+ */
+ public function layoutColumns():void
+ {
+ if (this._text == "" || this._text == null)
+ {
+ return;
+ }
+
+ var field:TextField = fieldArray[0] as TextField;
+ field.text = this._text;
+ field.setTextFormat(this.format);
+
+ this.preferredHeight = this.getOptimalHeight(field);
+
+ var remainder:String = this._text;
+ var fieldText:String = "";
+ var lastLineEndedPara:Boolean = true;
+
+ var indent:Number = this.format.indent as Number;
+
+ for (var i:int = 0; i < fieldArray.length; i++)
+ {
+ field = this.fieldArray[i] as TextField;
+
+ // First set the text for the field so we can find out where the
+ // line break will be in the last line of the field
+ //field.width = colWidth;
+ field.height = this.preferredHeight;
+ field.text = remainder;
+
+ // Apply the text format for to get the lines to all wrap as intended
+ field.setTextFormat(this.format);
+
+ // Remove indents from the start of each new field, unless the new field
+ // marks the start of a new paragraph
+ var lineLen:int;
+ if (indent > 0 && !lastLineEndedPara && field.numLines > 0)
+ {
+ lineLen = field.getLineLength(0);
+ if (lineLen > 0)
+ {
+ field.setTextFormat(this.firstLineFormat, 0, lineLen);
+ }
+ }
+
+ field.x = i * (colWidth + gutter);
+ field.y = 0;
+
+ remainder = "";
+ fieldText = "";
+
+ var linesRemaining:int = field.numLines;
+ var linesVisible:int = Math.min(this.linesPerCol, linesRemaining);
+
+ // Now copy lines from the text into the field for display
+ // purposes. Remaining lines will be saved for the next field.
+ for (var j:int = 0; j < linesRemaining; j++)
+ {
+ if (j < linesVisible)
+ {
+ fieldText += field.getLineText(j);
+ }
+ else
+ {
+ remainder += field.getLineText(j);
+ }
+ }
+
+ field.text = fieldText;
+
+ // Apply the format again, this time to the exactly-fitting text
+ field.setTextFormat(this.format);
+
+ // Remove the indent again if needed
+ if (indent > 0 && !lastLineEndedPara)
+ {
+ lineLen = field.getLineLength(0);
+ if (lineLen > 0)
+ {
+ field.setTextFormat(this.firstLineFormat, 0, lineLen);
+ }
+ }
+
+ // Check if a paragraph ends on the last line
+ var lastLine:String = field.getLineText(field.numLines - 1);
+ var lastCharCode:Number = lastLine.charCodeAt(lastLine.length - 1);
+
+ if (lastCharCode == 10 || lastCharCode == 13)
+ {
+ lastLineEndedPara = true;
+ }
+ else
+ {
+ lastLineEndedPara = false;
+ }
+
+ // If the last line doesn't end a paragraph, manually justify itif needed
+ if ((this.format.align == TextFormatAlign.JUSTIFY) && (i < fieldArray.length - 1))
+ {
+ if (!lastLineEndedPara)
+ {
+ justifyLastLine(field, lastLine);
+ }
+ }
+ }
+ }
+
+ /**
+ * If the text alignment style is set to "justify" it will not justify the final
+ * line in a paragraph. It also assumes that the last line in a field is the
+ * final line in a paragraph. Of course when we split text across fields, the
+ * last line in a field might be in the middle of a paragraph.
+ *
+ * This method "manually" justfies the last line in a text field by changing the
+ * letter spacing style and applying it to space characters in the line. The effect
+ * is to add additional pixels to each space until the line width matches the
+ * maximum text width for the field.
+ */
+ public function justifyLastLine(field:TextField, lastLine:String):void
+ {
+ // boost letter spacing to widen the last line
+ var metrics:TextLineMetrics = field.getLineMetrics(field.numLines - 1);
+
+ // get the original letter spacing
+ var spacing:Number = this.format.letterSpacing as Number;
+
+ var maxTextWidth:Number = field.width - 4 - (this.lastLineFormat.leftMargin as Number) - (this.lastLineFormat.rightMargin as Number);
+
+ // adjust for paragraph indent value
+ var indent:Number = (this.lastLineFormat.indent as Number);
+ if (indent > 0)
+ {
+ var secondToLastLine:String = field.getLineText(field.numLines - 2);
+ var lastCharCode:int = secondToLastLine.charCodeAt(secondToLastLine.length - 1);
+ if (lastCharCode == 10 || lastCharCode == 13)
+ {
+ // previous line was the end of a paragraph,
+ // so subtract the indent value from this line's max width
+ maxTextWidth -= indent;
+ }
+ }
+ var extraWidth:Number = maxTextWidth - metrics.width;
+
+ // remove trailing spaces
+ while (lastLine.charAt(lastLine.length - 1) == " ")
+ {
+ lastLine = lastLine.substr(0, lastLine.length - 1);
+ }
+
+ var wordArray:Array = lastLine.split(" ");
+ var numSpaces:int = wordArray.length - 1;
+ // if there aren't any spaces, give up
+ if (numSpaces < 1)
+ {
+ return;
+ }
+ var spaceSize:int = Math.floor(extraWidth / numSpaces) + spacing;
+ this.lastLineFormat.letterSpacing = spaceSize;
+
+ var remainingPixels:int = extraWidth % spaceSize;
+
+ var lastChars:String = lastLine;
+ var lastlineOffset:Number = field.getLineOffset(field.numLines - 1);
+
+ var sPos:int;
+ var counter:int = -1;
+
+ for (var i:int = 0; i < numSpaces; i++)
+ {
+ // later in the loop add an extra pixel to the spacing
+ // to account for the remaining pixels
+ if ((numSpaces - i) == remainingPixels)
+ {
+ this.lastLineFormat.letterSpacing = spaceSize + 1;
+ }
+
+ sPos = lastChars.indexOf(" ");
+ counter += sPos + 1;
+ field.setTextFormat(this.lastLineFormat, lastlineOffset + counter, lastlineOffset + counter + 1);
+ lastChars = lastChars.substring(sPos + 1);
+ }
+ }
+
+ public function set text(str:String):void
+ {
+ this._text = str;
+ layoutColumns();
+ }
+
+ public function get text():String
+ {
+ return this._text;
+ }
+
+ public function set textFormat(tf:TextFormat):void
+ {
+ applyTextFormat(tf);
+ layoutColumns();
+ }
+
+ public function get textFormat():TextFormat
+ {
+ return this.format;
+ }
+
+ public function setColumnWidth():void
+ {
+ this.colWidth = Math.floor( (this.preferredWidth -
+ ((this.numColumns - 1) * this.gutter)) / this.numColumns);
+ }
+
+ public function getOptimalHeight(field:TextField):int
+ {
+ if (field.text == "" || field.text == null)
+ {
+ return this.preferredHeight;
+ }
+ else
+ {
+ this.linesPerCol = Math.ceil(field.numLines / this.numColumns);
+
+ // Get the line height based on the font and size being used
+ var metrics:TextLineMetrics = field.getLineMetrics(0);
+ this.lineHeight = metrics.height;
+ var prefHeight:int = linesPerCol * this.lineHeight;
+
+ // Add 4 pixels as a buffer to account for the standard
+ // 2 pixel TextField border
+ return prefHeight + 4;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NewsLayout/com/example/programmingas3/newslayout/StoryLayout.as b/NewsLayout/com/example/programmingas3/newslayout/StoryLayout.as
new file mode 100644
index 0000000..a5140cf
--- /dev/null
+++ b/NewsLayout/com/example/programmingas3/newslayout/StoryLayout.as
@@ -0,0 +1,203 @@
+package com.example.programmingas3.newslayout
+{
+ import flash.display.Sprite;
+ import flash.text.TextFormatAlign;
+ import flash.text.TextFormat;
+ import flash.text.StyleSheet;
+
+ import flash.events.Event;
+ import flash.net.URLRequest;
+ import flash.net.URLLoader;
+ import flash.display.Sprite;
+ import flash.display.Graphics;
+
+ public class StoryLayout extends Sprite
+ {
+ public var headlineTxt:HeadlineTextField;
+ public var subtitleTxt:HeadlineTextField;
+ public var storyTxt:MultiColumnText;
+ public var sheet:StyleSheet;
+ public var h1Format:TextFormat;
+ public var h2Format:TextFormat;
+ public var pFormat:TextFormat;
+
+ private var loader:URLLoader;
+
+ public var paddingLeft:Number;
+ public var paddingRight:Number;
+ public var paddingTop:Number;
+ public var paddingBottom:Number;
+
+ public var preferredWidth:Number;
+ public var preferredHeight:Number;
+
+ public var numColumns:int;
+
+ public var bgColor:Number = 0xFFFFFF;
+
+ public var headline:String = "News Layout Example";
+ public var subtitle:String = "This example formats text like a newspaper page with a headline, a subtitle, and multiple columns. ";
+
+ public var loremIpsum:String = "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. \r\
+As an ActionScript 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.\r\
+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 component or the \
+TextInput component, that are provided in the Adobe Flex framework and in the Flash authoring environment.\r\
+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.\r\
+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.";
+
+ public function StoryLayout(w:int = 400, h:int = 200, cols:int = 3, padding:int = 10):void
+ {
+ this.preferredWidth = w;
+ this.preferredHeight = h;
+
+ this.numColumns = cols;
+
+ this.paddingLeft = padding;
+ this.paddingRight = padding;
+ this.paddingTop = padding;
+ this.paddingBottom = padding;
+
+ var req:URLRequest = new URLRequest("story.css");
+ loader = new URLLoader();
+ loader.addEventListener(Event.COMPLETE, onCSSFileLoaded);
+ loader.load(req);
+ }
+
+ public function onCSSFileLoaded(event:Event):void
+ {
+ this.sheet = new StyleSheet();
+ this.sheet.parseCSS(loader.data);
+
+ // convert headline styles to TextFormat objects
+ h1Format = getTextStyle("h1", this.sheet);
+ if (h1Format == null)
+ {
+ h1Format = getDefaultHeadFormat();
+ }
+
+ h2Format = getTextStyle("h2", this.sheet);
+ if (h2Format == null)
+ {
+ h2Format = getDefaultHeadFormat();
+ h2Format.size = 16;
+ }
+
+ pFormat = getTextStyle("p", this.sheet);
+ if (pFormat == null)
+ {
+ pFormat = getDefaultTextFormat();
+ pFormat.size = 12;
+ }
+
+ displayText();
+ }
+
+ public function drawBackground():void
+ {
+ var h:Number = this.storyTxt.y + this.storyTxt.height + this.paddingTop + this.paddingBottom;
+ var g:Graphics = this.graphics;
+ g.beginFill(this.bgColor);
+ g.drawRect(0, 0, this.width + this.paddingRight + this.paddingLeft, h);
+ g.endFill();
+ }
+
+ /**
+ * Reads a set of style properties for a named style and then creates
+ * a TextFormat object that uses the same properties.
+ */
+ public function getTextStyle(styleName:String, ss:StyleSheet):TextFormat
+ {
+ var format:TextFormat = null;
+
+ var style:Object = ss.getStyle(styleName);
+ if (style != null)
+ {
+ 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.indent,
+ style.leading);
+
+ if (style.hasOwnProperty("letterSpacing"))
+ {
+ format.letterSpacing = style.letterSpacing;
+ }
+ }
+ return format;
+ }
+
+ public function getDefaultHeadFormat():TextFormat
+ {
+ var tf:TextFormat = new TextFormat("Arial", 20, 0x000000, true);
+ return tf;
+ }
+
+ public function getDefaultTextFormat():TextFormat
+ {
+ var tf:TextFormat = new TextFormat("Georgia", 12, 0x000000, true);
+ return tf;
+ }
+
+ public function displayText():void
+ {
+ headlineTxt = new HeadlineTextField(h1Format);
+ headlineTxt.wordWrap = true;
+ headlineTxt.x = this.paddingLeft;
+ headlineTxt.y = this.paddingTop;
+ headlineTxt.width = this.preferredWidth;
+ this.addChild(headlineTxt);
+
+ headlineTxt.fitText(this.headline, 1, true);
+
+ subtitleTxt = new HeadlineTextField(h2Format);
+ subtitleTxt.wordWrap = true;
+ subtitleTxt.x = this.paddingLeft;
+ subtitleTxt.y = headlineTxt.y + headlineTxt.height;
+ subtitleTxt.width = this.preferredWidth;
+ this.addChild(subtitleTxt);
+
+ subtitleTxt.fitText(this.subtitle, 2, false);
+
+ storyTxt = new MultiColumnText(this.numColumns,
+ 20,
+ this.preferredWidth,
+ this.preferredHeight,
+ this.pFormat);
+ storyTxt.x = this.paddingLeft;
+ storyTxt.y = subtitleTxt.y + subtitleTxt.height + 10;
+ this.addChild(storyTxt);
+
+ storyTxt.text = loremIpsum;
+
+ drawBackground();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NewsLayout/com/example/programmingas3/newslayout/StoryLayoutComponent.as b/NewsLayout/com/example/programmingas3/newslayout/StoryLayoutComponent.as
new file mode 100644
index 0000000..b376643
--- /dev/null
+++ b/NewsLayout/com/example/programmingas3/newslayout/StoryLayoutComponent.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.newslayout
+{
+ import mx.core.UIComponent;
+ import com.example.programmingas3.newslayout.StoryLayout;
+
+ public class StoryLayoutComponent extends UIComponent
+ {
+ public var story:StoryLayout;
+
+ public var preferredWidth:Number = 400;
+ public var preferredHeight:Number = 300;
+ public var numColumns:int = 3;
+ public var padding:int = 10;
+
+ public function StoryLayoutComponent():void
+ {
+ super();
+ }
+
+ public function initStory():void
+ {
+ this.story = new StoryLayout(this.preferredWidth,
+ this.preferredHeight,
+ this.numColumns,
+ this.padding);
+
+ this.addChild(story);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NewsLayout/story.css b/NewsLayout/story.css
new file mode 100644
index 0000000..7661914
--- /dev/null
+++ b/NewsLayout/story.css
@@ -0,0 +1,23 @@
+/* CSS file */
+p {
+ font-family: Georgia, "Times New Roman", Times, _serif;
+ font-size: 12;
+ leading: 2;
+ text-align: justify;
+ indent: 24;
+}
+
+h1 {
+ font-family: Verdana, Arial, Helvetica, _sans;
+ font-size: 20;
+ font-weight: bold;
+ color: #000099;
+ text-align: left;
+}
+
+h2 {
+ font-family: Verdana, Arial, Helvetica, _sans;
+ font-size: 16;
+ font-weight: normal;
+ text-align: left;
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/AllTests.as b/ObjectOrientedProgramming/AllTests.as
new file mode 100644
index 0000000..747e132
--- /dev/null
+++ b/ObjectOrientedProgramming/AllTests.as
@@ -0,0 +1,11 @@
+package {
+ import asunit.framework.TestSuite;
+ import com.AllTests;
+
+ public class AllTests extends TestSuite {
+
+ public function AllTests() {
+ addTest(new com.AllTests());
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/ChapterFour.as b/ObjectOrientedProgramming/ChapterFour.as
new file mode 100644
index 0000000..631827b
--- /dev/null
+++ b/ObjectOrientedProgramming/ChapterFour.as
@@ -0,0 +1,14 @@
+package {
+ import com.example.display.SampleFoundation;
+ import com.example.display.ChapterFourContent;
+
+ public class ChapterFour extends SampleFoundation {
+
+ protected override function init():Void {
+ title = "Chapter Four: Object-Oriented Programming";
+ description = "This is a description about OOP.";
+ content = new ChapterFourContent();
+ super.init();
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/SampleFoundationRunner.as b/ObjectOrientedProgramming/SampleFoundationRunner.as
new file mode 100644
index 0000000..5b27c76
--- /dev/null
+++ b/ObjectOrientedProgramming/SampleFoundationRunner.as
@@ -0,0 +1,22 @@
+package {
+ import asunit.textui.TestRunner;
+ import flash.system.fscommand;
+ import flash.display.Stage;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.util.trace;
+ import com.example.display.SampleComponentTest;
+ import com.example.display.SampleHeaderTest;
+
+ public class SampleFoundationRunner extends TestRunner {
+
+ public function SampleFoundationRunner() {
+ fscommand("fullscreen", "true");
+ stage.align = StageAlign.TOP_LEFT;
+ stage.scaleMode = StageScaleMode.NO_SCALE;
+ start(AllTests);
+// start(SampleHeaderTest, "testTitleAndDescription");
+// start(SampleComponentTest, "testDisplayMock");
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/AllTests.as b/ObjectOrientedProgramming/com/AllTests.as
new file mode 100644
index 0000000..40d4b66
--- /dev/null
+++ b/ObjectOrientedProgramming/com/AllTests.as
@@ -0,0 +1,11 @@
+package com {
+ import asunit.framework.TestSuite;
+ import com.example.AllTests;
+
+ public class AllTests extends TestSuite {
+
+ public function AllTests() {
+ addTest(new com.example.AllTests());
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/AllTests.as b/ObjectOrientedProgramming/com/example/AllTests.as
new file mode 100644
index 0000000..1ddc6b3
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/AllTests.as
@@ -0,0 +1,11 @@
+package com.example {
+ import asunit.framework.TestSuite;
+ import com.example.display.AllTests;
+
+ public class AllTests extends TestSuite {
+
+ public function AllTests() {
+ addTest(new com.example.display.AllTests());
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/AllTests.as b/ObjectOrientedProgramming/com/example/display/AllTests.as
new file mode 100644
index 0000000..29a548e
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/AllTests.as
@@ -0,0 +1,15 @@
+package com.example.display {
+ import asunit.framework.TestSuite;
+ import com.example.display.SampleComponentTest;
+ import com.example.display.SampleFoundationTest;
+ import com.example.display.SampleHeaderTest;
+
+ public class AllTests extends TestSuite {
+
+ public function AllTests() {
+ addTest(new com.example.display.SampleComponentTest());
+ addTest(new com.example.display.SampleFoundationTest());
+ addTest(new com.example.display.SampleHeaderTest());
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/ChapterFourContent.as b/ObjectOrientedProgramming/com/example/display/ChapterFourContent.as
new file mode 100644
index 0000000..0cf009b
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/ChapterFourContent.as
@@ -0,0 +1,34 @@
+package com.example.display {
+ import flash.display.TextField;
+ import flash.text.TextFormat;
+ import flash.display.TextFieldAutoSize;
+
+ public class ChapterFourContent extends SampleComponent {
+ private var txt:TextField;
+
+ public function ChapterFourContent() {
+ }
+
+ protected override function init():Void {
+ txt = new TextField();
+ txt.defaultTextFormat = getFormat();
+ txt.text = "Chapter Four Content";
+ txt.x = gutter;
+ txt.y = gutter;
+ addChild(txt);
+ }
+
+ public override function draw():Void {
+ super.draw();
+ txt.width = getWidth() - (2 * gutter);
+ }
+
+ private function getFormat():TextFormat {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 18;
+ format.bold = true;
+ return format;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/com/example/display/ISampleComponent.as b/ObjectOrientedProgramming/com/example/display/ISampleComponent.as
new file mode 100644
index 0000000..1b62984
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/ISampleComponent.as
@@ -0,0 +1,14 @@
+package com.example.display {
+
+ public interface ISampleComponent {
+ function setWidth(width:Number):void;
+ function getWidth():Number;
+ function setHeight(height:Number):void;
+ function getHeight():Number;
+ function getX():Number;
+ function setX(x:Number):void;
+ function getY():Number;
+ function setY(y:Number):void;
+ function draw():void;
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleComponent.as b/ObjectOrientedProgramming/com/example/display/SampleComponent.as
new file mode 100644
index 0000000..ae8f698
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleComponent.as
@@ -0,0 +1,61 @@
+package com.example.display {
+ import flash.display.Sprite;
+ import flash.text.TextFormat;
+
+ public class SampleComponent extends Sprite implements ISampleComponent {
+ protected var _width:Number = 0;
+ protected var _height:Number = 0;
+ protected var gutter:Number = 5;
+ protected var backgroundColor:int = 0xFEFEFE;
+ protected var strokeColor:int = 0x333333;
+ protected var strokeSize:int = 0;
+
+ public function SampleComponent() {
+ init();
+ }
+
+ protected function init():void {
+ }
+
+ public function draw():void {
+ graphics.clear();
+ graphics.beginFill(backgroundColor);
+ graphics.lineStyle(strokeSize, strokeColor);
+ graphics.drawRect(0, 0, getWidth(), getHeight());
+ graphics.endFill();
+ }
+
+ public function setWidth(width:Number):void {
+ _width = width;
+ }
+
+ public function getWidth():Number {
+ return _width;
+ }
+
+ public function setHeight(height:Number):void {
+ _height = height;
+ }
+
+ public function getHeight():Number {
+ return _height;
+ }
+
+ public function getX():Number {
+ return x;
+ }
+
+ public function setX(x:Number):void {
+ this.x = x;
+ }
+
+ public function getY():Number {
+ return y;
+ }
+
+ public function setY(y:Number):void {
+ this.y = y;
+ }
+
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleComponentMock.as b/ObjectOrientedProgramming/com/example/display/SampleComponentMock.as
new file mode 100644
index 0000000..d0b1689
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleComponentMock.as
@@ -0,0 +1,9 @@
+package com.example.display {
+ import com.example.display.SampleComponent;
+
+ public class SampleComponentMock extends SampleComponent {
+
+ public function SampleComponentMock() {
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleComponentTest.as b/ObjectOrientedProgramming/com/example/display/SampleComponentTest.as
new file mode 100644
index 0000000..3c1a548
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleComponentTest.as
@@ -0,0 +1,50 @@
+package com.example.display {
+ import asunit.framework.TestCase;
+
+ public class SampleComponentTest extends TestCase {
+ private var instance:SampleComponent;
+
+ public function SampleComponentTest(testMethod:String = null) {
+ super(testMethod);
+ }
+
+ protected override function setUp():Void {
+ instance = new SampleComponentMock();
+ addChild(instance);
+ }
+
+ protected override function tearDown():Void {
+ removeChild(instance);
+ delete instance;
+ }
+
+ public function testInstantiated():Void {
+ assertTrue("SampleComponent instantiated", instance is SampleComponent);
+ }
+
+ public function testDefaultWidth():Void {
+ assertTrue(instance.getWidth() == 0);
+ }
+
+ public function testDefaultHeight():Void {
+ assertTrue(instance.getHeight() == 0);
+ }
+
+ public function testSetWidth():Void {
+ instance.setWidth(100);
+ assertTrue(instance.getWidth() == 100);
+ }
+
+ public function testSetHeight():Void {
+ instance.setHeight(100);
+ assertTrue(instance.getHeight() == 100);
+ }
+
+ public function testDisplayMock():Void {
+ instance.setWidth(200);
+ instance.setHeight(100);
+ instance.draw();
+
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleFoundation.as b/ObjectOrientedProgramming/com/example/display/SampleFoundation.as
new file mode 100644
index 0000000..3c2f048
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleFoundation.as
@@ -0,0 +1,84 @@
+package com.example.display {
+ import com.example.display.SampleComponent;
+ import flash.display.DisplayObject;
+ import com.example.display.SampleHeader;
+ import flash.events.Event;
+ import flash.events.EventType;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.system.fscommand;
+
+ public class SampleFoundation extends SampleComponent {
+ protected var header:SampleHeader;
+ protected var content:ISampleComponent;
+ protected var title:String = "Default Title";
+ protected var description:String = "Default Description";
+
+ public function SampleFoundation() {
+ configureResize();
+ }
+
+ protected override function init():void {
+ super.init();
+ header = new SampleHeader();
+ header.setTitle(title);
+ header.setDescription(description);
+ addChild(header);
+
+ addChild(DisplayObject(content));
+ fscommand("showmenu", "false");
+ configureResize();
+ }
+
+ protected function configureResize():void {
+ stage.align = StageAlign.TOP_LEFT;
+ stage.scaleMode = StageScaleMode.NO_SCALE;
+ stage.addEventListener(Event.RESIZE, resizeHandler);
+ resizeHandler(new Event(Event.RESIZE));
+ }
+
+ public function setTitle(title:String):void {
+ header.setTitle(title);
+ }
+
+ public function setDescription(description:String):void {
+ header.setDescription(description);
+ }
+
+ public function getTitle():String {
+ return header.getTitle();
+ }
+
+ public function getDescription(description:String):String {
+ return header.getDescription();
+ }
+
+ public override function setWidth(width:Number):void {
+ super.setWidth(width);
+ var availableWidth:int = int(_width - (gutter*2));
+ header.setX(gutter);
+ header.setWidth(availableWidth);
+ content.setX(gutter);
+ content.setWidth(availableWidth);
+ }
+
+ public override function setHeight(height:Number):void {
+ super.setHeight(height);
+ header.setY(gutter);
+ content.setY(header.getHeight() + (gutter*2));
+ content.setHeight(_height - (header.getHeight() + (gutter*3)));
+ }
+
+ public override function draw():void {
+ header.draw();
+ content.draw();
+ }
+
+ private function resizeHandler(e:Event):void {
+ setWidth(stage.stageWidth - 5);
+ setHeight(stage.stageHeight - 5);
+ x = y = 0;
+ draw();
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleFoundationMock.as b/ObjectOrientedProgramming/com/example/display/SampleFoundationMock.as
new file mode 100644
index 0000000..a397c7e
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleFoundationMock.as
@@ -0,0 +1,20 @@
+package com.example.display {
+ import com.example.display.SampleFoundation;
+ import flash.events.Event;
+
+ public class SampleFoundationMock extends SampleFoundation {
+
+ public function SampleFoundationMock() {
+ }
+
+ protected override function init():Void {
+ title = "Chapter Four: Object-Oriented Programming";
+ description = "This is a description about OOP.";
+ content = new ChapterFourContent();
+ super.init();
+ }
+
+ protected override function configureResize():Void {
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleFoundationTest.as b/ObjectOrientedProgramming/com/example/display/SampleFoundationTest.as
new file mode 100644
index 0000000..589b93f
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleFoundationTest.as
@@ -0,0 +1,43 @@
+package com.example.display {
+ import asunit.framework.TestCase;
+
+ public class SampleFoundationTest extends TestCase {
+ private var instance:SampleFoundationMock;
+ private var width:Number = 400;
+ private var height:Number = 300;
+
+ public function SampleFoundationTest(testMethod:String = null) {
+ super(testMethod);
+ }
+
+ protected override function setUp():Void {
+ instance = new SampleFoundationMock();
+ addChild(instance);
+ }
+
+ protected override function tearDown():Void {
+ removeChild(instance);
+ delete instance;
+ }
+
+ public function testInstantiated():Void {
+ assertTrue("SampleFoundationMock instantiated", instance is SampleFoundationMock);
+ }
+
+ public function testSetWidth():Void {
+ instance.setWidth(100);
+ assertTrue(instance.getWidth() == 100);
+ }
+
+ public function testSetHeight():Void {
+ instance.setHeight(100);
+ assertTrue(instance.getHeight() == 100);
+ }
+
+ public function testSetTitle():Void {
+ instance.setWidth(width);
+ instance.setHeight(height);
+ instance.draw();
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleHeader.as b/ObjectOrientedProgramming/com/example/display/SampleHeader.as
new file mode 100644
index 0000000..499637a
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleHeader.as
@@ -0,0 +1,86 @@
+package com.example.display {
+ import com.example.display.SampleFoundation;
+ import com.example.display.SampleComponent;
+ import flash.display.TextField;
+ import flash.text.TextFormat;
+
+ public class SampleHeader extends SampleComponent {
+ private const TITLE_HEIGHT:Number = 30;
+ private const DESCRIPTION_HEIGHT:Number = 50;
+ private var title:TextField;
+ private var description:TextField;
+
+ public function SampleHeader() {
+ }
+
+ protected override function init():void {
+ title = new TextField();
+ title.defaultTextFormat = getTitleFormat();
+ title.selectable = false;
+ title.height = TITLE_HEIGHT;
+ addChild(title);
+
+
+ description = new TextField();
+ description.defaultTextFormat = getDescriptionFormat();
+ description.wordWrap = true;
+ description.height = DESCRIPTION_HEIGHT;
+ description.selectable = false;
+ addChild(description);
+ }
+
+ private function getDescriptionFormat():TextFormat {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 11;
+ return format;
+ }
+
+ private function getTitleFormat():TextFormat {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 18;
+ format.bold = true;
+ return format;
+ }
+
+ public override function draw():void {
+ super.draw();
+ var w:Number = getWidth();
+ var h:Number = getHeight();
+
+ title.x = gutter;
+ title.y = gutter;
+
+ description.x = gutter;
+ description.y = title.y + title.height;
+ }
+
+ public override function setWidth(width:Number):void {
+ super.setWidth(width);
+ var availableWidth:Number = getWidth() - (gutter * 2);
+ title.width = availableWidth;
+ description.width = availableWidth;
+ }
+
+ public override function getHeight():Number {
+ return TITLE_HEIGHT + DESCRIPTION_HEIGHT + (gutter * 2);
+ }
+
+ public function setTitle(str:String):void {
+ title.text = str;
+ }
+
+ public function getTitle():String {
+ return title.text;
+ }
+
+ public function setDescription(str:String):void {
+ description.text = str;
+ }
+
+ public function getDescription():String {
+ return description.text;
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleHeaderMock.as b/ObjectOrientedProgramming/com/example/display/SampleHeaderMock.as
new file mode 100644
index 0000000..c9c4253
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleHeaderMock.as
@@ -0,0 +1,10 @@
+package com.example.display {
+ import com.example.display.SampleFoundation;
+ import com.example.display.SampleHeader;
+
+ public class SampleHeaderMock extends SampleHeader {
+
+ public function SampleHeaderMock() {
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/display/SampleHeaderTest.as b/ObjectOrientedProgramming/com/example/display/SampleHeaderTest.as
new file mode 100644
index 0000000..436701f
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/display/SampleHeaderTest.as
@@ -0,0 +1,58 @@
+package com.example.display {
+ import asunit.framework.TestCase;
+ import flash.util.trace;
+
+ public class SampleHeaderTest extends TestCase {
+ private var instance:SampleHeaderMock;
+ private var width:int = 400;
+ private var height:int = 80;
+ private var title:String = "Hello World";
+ private var description:String = "The following example is meant to demonstrate functionality of the XML object.";
+
+ public function SampleHeaderTest(testMethod:String = null) {
+ super(testMethod);
+ }
+
+ protected override function setUp():Void {
+ instance = new SampleHeaderMock();
+ instance.setWidth(width);
+ instance.setHeight(height);
+ instance.draw();
+ addChild(instance);
+ }
+
+ protected override function tearDown():Void {
+ removeChild(instance);
+ delete instance;
+ }
+
+ public function testInstantiated():Void {
+ assertTrue("SampleHeaderMock instantiated", instance is SampleHeaderMock);
+ }
+
+ public function testSetSize():Void {
+ var gutter:Number = 5;
+ var TITLE_HEIGHT:Number = 30;
+ var DESCRIPTION_HEIGHT:Number = 50;
+ assertTrue(instance.getWidth() == width);
+ assertTrue(instance.getHeight() == (TITLE_HEIGHT + DESCRIPTION_HEIGHT + (2 * gutter)));
+ }
+
+ public function testSetTitle():Void {
+ instance.setTitle(title);
+ assertTrue(instance.getTitle() == title);
+ }
+
+ public function testSetDescription():Void {
+ instance.setDescription(description);
+ assertTrue(instance.getDescription() == description);
+ }
+
+ public function testTitleAndDescription():Void {
+ instance.setTitle(title);
+ instance.setDescription(description);
+ assertTrue(instance.getTitle() == title);
+ assertTrue(instance.getDescription() == description);
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/LabeledTextField.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/LabeledTextField.as
new file mode 100644
index 0000000..2040072
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/LabeledTextField.as
@@ -0,0 +1,103 @@
+// ActionScript file
+package com.example.programmingas3.shared.component
+{
+ import flash.display.*;
+ import flash.text.TextFormat;
+ import flash.util.trace;
+ import com.example.programmingas3.shared.component.SampleContainer
+ import flash.events.FocusEvent;
+
+ public class LabeledTextField extends SampleContainer
+ {
+ public var labelField:SampleLabel;
+ public var inputField:SampleTextInput;
+ public var warningField:SampleLabel;
+
+ public function LabeledTextField(label:String,
+ inputText:String = "",
+ inputWidth:uint = 200,
+ inputHeight:uint = 20,
+ stackVertically:Boolean = false)
+ {
+ labelField = new SampleLabel(120, inputHeight, getLabelFormat());
+ labelField.text = label;
+
+ inputField = new SampleTextInput(inputWidth, inputHeight, getInputFormat());
+ inputField.text = inputText;
+
+ // traps and forwards focus events
+ inputField.addEventListener(FocusEvent.FOCUS_IN, onFocusEvent);
+ inputField.addEventListener(FocusEvent.FOCUS_OUT, onFocusEvent);
+
+ warningField = new SampleLabel(120, 20, getWarningFormat());
+ warningField.text = "< Invalid entry";
+ warningField.visible = false;
+ warningField.y = 0;
+
+ if (stackVertically)
+ {
+ inputField.x = 0;
+ inputField.y = labelField.h;
+ warningField.x = labelField.w;
+ }
+ else
+ {
+ inputField.x = labelField.w;
+ inputField.y = 0;
+ warningField.x = inputField.x + inputField.w;
+ }
+
+ addComponent(labelField);
+ addComponent(inputField);
+ addComponent(warningField);
+ }
+
+ public function showWarning(showOrHide:Boolean = true)
+ {
+ this.warningField.visible = showOrHide;
+ }
+
+ public override function draw():void
+ {
+ labelField.draw();
+ inputField.draw();
+ if (warningField.visible)
+ {
+ warningField.draw();
+ }
+ }
+ private function getLabelFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = true;
+ format.align = "right";
+ return format;
+ }
+
+ private function getInputFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = false;
+ return format;
+ }
+
+ private function getWarningFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = false;
+ format.color = 0xFF0000;
+ return format;
+ }
+
+ private function onFocusEvent(evt:FocusEvent):void
+ {
+ this.dispatchEvent(evt);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleComponent.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleComponent.as
new file mode 100644
index 0000000..514cf5a
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleComponent.as
@@ -0,0 +1,80 @@
+package com.example.programmingas3.shared.component
+{
+ import flash.display.Sprite;
+ import flash.text.TextFormat;
+ import flash.util.trace;
+
+ /**
+ * Defines a simple component base class, with a background color and an optional border.
+ */
+ public class SampleComponent extends Sprite
+ {
+ /**
+ * The desired width of this componenet, as opposed to the .width
+ * property which represents tha actual width.
+ */
+ public var w:uint = 120;
+
+ /**
+ * The desired height of this componenet, as opposed to the .height
+ * property which represents tha actual height.
+ */
+ public var h:uint = 20;
+
+ // policy: do away with getters and setters, unless they add some value -
+ // like validating inputs, or triggering side effects?
+
+ public var backgroundAlpha:Number = 1.0;
+
+ public var backgroundColor:Number = 0xFFFFFF;
+
+ public var borderThickness:Number = 0.5;
+
+ public var borderColor:Number = 0x000000;
+
+ private var _borderAlpha:Number = 1.0;
+ private var _showBorder:Boolean = true;
+ public function get showBorder():Boolean
+ {
+ return this._showBorder;
+ }
+ public function set showBorder(bool:Boolean)
+ {
+ this._showBorder = bool;
+ this._borderAlpha = bool ? 1 : 0;
+ }
+
+ public function SampleComponent(bgColor:Number = 0xFFFFFF,
+ bgAlpha:Number = 0xFFFFFF,
+ borderThickness:Number = 1.0,
+ borderColor:Number = 0x000000,
+ showBorder:Boolean = true)
+ {
+ this.backgroundColor = bgColor;
+ this.borderThickness = borderThickness;
+ this.borderColor = borderColor;
+
+ //init();
+ }
+
+ /**
+ * The init() method should be overridden by component subclasses.
+ */
+ public function init():void
+ {
+
+ }
+
+ public function draw():void
+ {
+ trace("SampleComponent.draw()");
+
+ // draw the border
+ graphics.clear();
+ graphics.beginFill(backgroundColor, backgroundAlpha);
+ graphics.lineStyle(borderThickness, borderColor, _borderAlpha);
+ graphics.drawRect(0, 0, this.w, this.h);
+ graphics.endFill(); // optional
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleContainer.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleContainer.as
new file mode 100644
index 0000000..2bb78e7
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleContainer.as
@@ -0,0 +1,79 @@
+package com.example.programmingas3.shared.component
+{
+ import flash.display.Sprite;
+ import flash.text.TextFormat;
+ import flash.display.DisplayObject;
+ import flash.util.trace;
+
+ /**
+ * A SampleContainer maintains a padding area around its contents.
+ * It also provides specialized methods for managing SampleComponents
+ * in the display list of its own Sprite canvas.
+ *
+ * Components added using the addComponent() method will be arranged
+ * according to the layout rules for this container. (DisplayObjects
+ * that are added using the standard addChild() method will not be
+ * subject to the layout rules).
+ */
+ public class SampleContainer extends SampleComponent
+ {
+ protected var padding:Number = 5;
+
+ protected var canvas:Sprite;
+
+ public function SampleContainer(w:uint = 550, h:uint = 400, thickness:Number = 1.0, pad:uint = 5)
+ {
+ this.w = w;
+ this.h = h;
+ this.borderThickness = thickness;
+ this.padding = padding;
+
+ this.canvas = new Sprite(); // could be a SampleComponent if we want an inner border
+ this.addChild(canvas);
+ }
+
+ /**
+ * The init() method should be overridden by component subclasses.
+ */
+ public override function init():void
+ {
+ draw();
+ }
+
+ public override function draw():void
+ {
+ // draw border and background
+ super.draw();
+ trace("SampleContainer.draw()");
+
+ this.canvas.x = padding;
+ this.canvas.y = padding;
+
+ // draw internal components
+ for (var i:uint = 0; i < this.canvas.numChildren; i++)
+ {
+ var child:DisplayObject = this.canvas.getChildAt(i);
+ trace("child " + i + "=" + flash.util.getQualifiedClassName(child));
+
+ try
+ {
+ SampleComponent(child).draw();
+ }
+ catch (err:Error)
+ {
+ trace("Error: " + err.message);
+ }
+ }
+ }
+
+ public function addComponent(comp:SampleComponent)
+ {
+ this.canvas.addChild(comp);
+ }
+
+ public function removeComponent(comp:SampleComponent)
+ {
+ this.canvas.removeChild(comp);
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleHtmlField.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleHtmlField.as
new file mode 100644
index 0000000..1ae5350
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleHtmlField.as
@@ -0,0 +1,48 @@
+package com.example.programmingas3.shared.component
+{
+ import com.example.display.SampleTextField;
+ import flash.text.TextFormat;
+ import flash.display.TextFieldType;
+
+ /**
+ * An editable text field component with a bacground and border.
+ */
+ public class SampleHtmlField extends SampleTextField
+ {
+ public function SampleHtmlField(w:uint, h:uint, textFormat:TextFormat = null)
+ {
+ // creates a selectable field by default
+ super(w, h, textFormat, true);
+
+ this.field.type = flash.display.TextFieldType.INPUT;
+ this.field.html = true;
+ this.field.multiline = true;
+
+ this.init();
+ }
+
+ public override function init():void
+ {
+ super.init();
+
+ this.draw();
+ }
+
+ /**
+ * The contents of the htmlText property of the text field.
+ * Sets it to a blank string if the value comes in as null or undefined.
+ */
+ public function get htmlText():String
+ {
+ return field.htmlText;
+ }
+ public function set htmlText(str:String):void
+ {
+ if (str == null || str == undefined)
+ {
+ str = "";
+ }
+ field.htmlText = str;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleLabel.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleLabel.as
new file mode 100644
index 0000000..8f649f2
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleLabel.as
@@ -0,0 +1,43 @@
+package com.example.programmingas3.shared.component
+{
+ import com.example.display.SampleTextField;
+
+ import flash.text.TextFormat;
+
+ /**
+ * A simple label component with no background or border.
+ */
+ public class SampleLabel extends SampleTextField
+ {
+ public function SampleLabel(w:uint, h:uint, textFormat:TextFormat = null)
+ {
+ // creates a nonselectable field
+ super(w, h, textFormat, false);
+
+ // suppresses the border
+ this.showBorder = false;
+
+ // suppresses the background
+ this.backgroundAlpha = 0x000000;
+
+ this.init();
+ }
+
+ public override function init():void
+ {
+ super.init();
+
+ this.draw();
+ }
+
+ private function getTextFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = false;
+ format.align = "right";
+ return format;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButton.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButton.as
new file mode 100644
index 0000000..ee1a15e
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButton.as
@@ -0,0 +1,27 @@
+package com.example.programmingas3.shared.component
+{
+ import flash.display.*;
+ import flash.geom.*;
+
+ public class SampleTextButton extends SimpleButton
+ {
+ public var label:String;
+
+ public function SampleTextButton(labelText:String = "Submit")
+ {
+ label = labelText;
+
+ upState = new SampleTextButtonState(label);
+ hitTestState = upState;
+ overState = upState;
+
+ downState = new SampleTextButtonState(label);
+
+ var downStateTransform:Transform = downState.transform;
+ const downShadeMultiplier:Number = 0.75;
+
+ downStateTransform.colorTransform = new ColorTransform(downShadeMultiplier, downShadeMultiplier, downShadeMultiplier);
+ downState.transform = downStateTransform;
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButtonState.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButtonState.as
new file mode 100644
index 0000000..8ad7c15
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextButtonState.as
@@ -0,0 +1,41 @@
+package com.example.programmingas3.shared.component
+{
+ import flash.display.*;
+ import flash.text.*;
+ import flash.util.trace;
+ import flash.filters.DropShadowFilter
+
+ class SampleTextButtonState extends Sprite
+ {
+ public var field:TextField;
+
+ public function SampleTextButtonState(txtString:String)
+ {
+ field = new TextField();
+
+ field.defaultTextFormat = new TextFormat("Verdana");
+ field.defaultTextFormat.size = 12;
+ field.defaultTextFormat.align = TextFormatAlign.CENTER
+ field.text = txtString;
+ addChild(field);
+
+ field.x += 4;
+ field.width = field.textWidth + 12;
+ field.height = field.textHeight + 6;
+
+ var background:Shape = new Shape;
+
+ background.graphics.beginFill(0xEEEEEE);
+ background.graphics.lineStyle(1, 0x000000);
+ background.graphics.drawRoundRect(0, 0, width, height, 8, 8);
+
+ var shadow:DropShadowFilter = new DropShadowFilter();
+ shadow.blurX = 0;
+ shadow.blurY = 0;
+ shadow.distance = 2;
+ background.filters = [shadow];
+
+ addChildAt(background, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextField.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextField.as
new file mode 100644
index 0000000..788d648
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextField.as
@@ -0,0 +1,178 @@
+package com.example.programmingas3.shared.component
+{
+ import flash.display.TextField;
+ import flash.text.TextFormat;
+ import flash.util.trace;
+
+ import com.example.programmingas3.shared.component.SampleComponent;
+
+ /**
+ * A simple text field with a border and a default TextFormat.
+ * By defualt it shows a background and a border. The background
+ * changes colors when you change the selectable property.
+ */
+ public class SampleTextField extends SampleComponent
+ {
+ /**
+ * The main TextField instance used for display.
+ */
+ public var field:TextField;
+
+ /**
+ * A static default available to all instances.
+ */
+ protected static var defaultFormat:TextFormat = null;
+
+ /**
+ * The TextFormat object of this particular SampleTextField.
+ */
+ protected var _format:TextFormat;
+
+ /**
+ * You can pass in a TextFormat object to the constructor. If none is passed in it will use
+ * the static defaultFormat object.
+ */
+ public function SampleTextField(w:uint, h:uint, textFormat:TextFormat = null, selectable:Boolean = true)
+ {
+ // defaults to a single-pixel black border
+ super();
+
+ field = new TextField();
+
+ //trace("w=" + w + ", h=" + h);
+ if (w > 0)
+ {
+ this.w = w;
+ field.width = w;
+ }
+ if (h > 0)
+ {
+ this.h = h;
+ field.height = h;
+ }
+
+ this.selectable = selectable;
+
+ field.wordWrap = true;
+ field.autoSize = TextFieldAutoSize.NONE;
+
+ // if this is the first SampleTextField to be instantiated, it must prime the default format
+ if (SampleTextField.defaultFormat == null)
+ {
+ SampleTextField.defaultFormat = getTextFormat();
+ }
+
+ // if a TextFormat object is passed in, use it, otherwise use the default
+ if (textFormat == null)
+ {
+ this._format = SampleTextField.defaultFormat;
+ }
+ else
+ {
+ this._format = textFormat;
+ }
+ }
+
+ public override function init():void
+ {
+ this.field.defaultTextFormat = this._format;
+
+ this.addChild(field);
+
+ //this.draw();
+ }
+
+
+ private function getTextFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 10;
+ format.bold = false;
+ return format;
+ }
+
+ public override function draw():void
+ {
+ //trace("SampleTextField.draw()");
+ super.draw();
+ }
+
+ /**
+ * The contents of the text field.
+ * Sets it to a blank string if the value comes in as null or undefined.
+ */
+ public function get text():String
+ {
+ return field.text;
+ }
+ public function set text(str:String):void
+ {
+ if (str == null || str == undefined)
+ {
+ str = "";
+ }
+ field.text = str;
+ }
+
+ /**
+ * The background color to use when the field contents are editable.
+ */
+ public var _selectableColor:Number = 0xFFFFFF;
+
+ public function get selectableColor():Number
+ {
+ return _selectableColor;
+ }
+ public function set selectableColor(color:Number):void
+ {
+ this._selectableColor = color;
+ if (this.field.selectable)
+ {
+ this.backgroundColor = color;
+ }
+ }
+
+ /**
+ * The background color to use when the field contents cannot be edited.
+ */
+ public var _unselectableColor:Number = 0xEEEEEE;
+
+ public function get unselectableColor():Number
+ {
+ return _unselectableColor;
+ }
+ public function set unselectableColor(color:Number):void
+ {
+ this._unselectableColor = color;
+ if (!this.field.selectable)
+ {
+ this.backgroundColor = color;
+ }
+ }
+
+ /**
+ * The background color to use when the field contents cannot be edited.
+ */
+ public function get selectable():Boolean
+ {
+ return field.selectable;
+ }
+ public function set selectable(isSelectable:Boolean):void
+ {
+ //trace("setting selectable=" + isSelectable);
+
+ field.selectable = isSelectable;
+ if (isSelectable)
+ {
+ this.backgroundColor = this._selectableColor;
+ field.backgroundColor = this._selectableColor;
+ }
+ else
+ {
+ this.backgroundColor = this._unselectableColor;
+ field.backgroundColor = this._unselectableColor;
+ }
+ }
+ }
+}
diff --git a/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextInput.as b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextInput.as
new file mode 100644
index 0000000..eaf6533
--- /dev/null
+++ b/ObjectOrientedProgramming/com/example/programmingas3/shared/component/SampleTextInput.as
@@ -0,0 +1,29 @@
+package com.example.programmingas3.shared.component
+{
+ import com.example.display.SampleTextField;
+ import flash.text.TextFormat;
+ import flash.display.TextFieldType;
+
+ /**
+ * An editable text field component with a bacground and border.
+ */
+ public class SampleTextInput extends SampleTextField
+ {
+ public function SampleTextInput(w:uint, h:uint, textFormat:TextFormat = null)
+ {
+ // creates a selectable field
+ super(w, h, textFormat, true);
+
+ this.field.type = flash.display.TextFieldType.INPUT;
+
+ this.init();
+ }
+
+ public override function init():void
+ {
+ super.init();
+
+ this.draw();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlayList/PlayList.fla b/PlayList/PlayList.fla
new file mode 100644
index 0000000..e206ed0
Binary files /dev/null and b/PlayList/PlayList.fla differ
diff --git a/PlayList/PlayListApp.mxml b/PlayList/PlayListApp.mxml
new file mode 100644
index 0000000..27b202b
--- /dev/null
+++ b/PlayList/PlayListApp.mxml
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 90's
+ Classical
+ Country
+ Hip-hop
+ Opera
+ Pop
+ Rock
+ Showtunes
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PlayList/com/example/programmingas3/playlist/PlayList.as b/PlayList/com/example/programmingas3/playlist/PlayList.as
new file mode 100644
index 0000000..abba791
--- /dev/null
+++ b/PlayList/com/example/programmingas3/playlist/PlayList.as
@@ -0,0 +1,96 @@
+package com.example.programmingas3.playlist
+{
+ import com.example.programmingas3.playlist.Song;
+ import com.example.programmingas3.playlist.SortProperty;
+
+ /**
+ * Provides functionality for managing a sortable list of songs.
+ */
+ public class PlayList
+ {
+ // ------- Private variables -------
+
+ private var _songs:Array;
+ private var _currentSort:SortProperty = null;
+ private var _needToSort:Boolean = false;
+
+
+ // ------- Constructor -------
+
+ public function PlayList()
+ {
+ this._songs = new Array();
+ // set the initial sorting
+ this.sortList(SortProperty.TITLE);
+ }
+
+
+ // ------- Public Properties -------
+
+ /**
+ * Gets the list of songs
+ */
+ public function get songList():Array
+ {
+ // Sort the songs, if needed.
+ // For efficiency this is done here rather than, for instance, each time a song is added.
+ // That way if multiple songs are added in a batch, the list won't be sorted each time.
+ if (this._needToSort)
+ {
+ // record the current sorting method
+ var oldSort:SortProperty = this._currentSort;
+ // clear out the current sort so that it will re-sort
+ this._currentSort = null;
+ this.sortList(oldSort);
+ }
+ return this._songs;
+ }
+
+
+ // ------- Public Methods -------
+
+ /**
+ * Adds a song to the playlist
+ */
+ public function addSong(song:Song):void
+ {
+ this._songs.push(song);
+ this._needToSort = true;
+ }
+
+
+ /**
+ * Sorts the list of songs according to the specified property
+ */
+ public function sortList(sortProperty:SortProperty):void
+ {
+ if (sortProperty == this._currentSort)
+ {
+ return;
+ }
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlayList/com/example/programmingas3/playlist/Song.as b/PlayList/com/example/programmingas3/playlist/Song.as
new file mode 100644
index 0000000..52af8fe
--- /dev/null
+++ b/PlayList/com/example/programmingas3/playlist/Song.as
@@ -0,0 +1,102 @@
+package com.example.programmingas3.playlist
+{
+
+ /**
+ * A simple value object containing song information
+ */
+ public class Song
+ {
+ // ------- Private variables -------
+
+ private var _title:String;
+ private var _artist:String;
+ private var _year:uint;
+ private var _filename:String;
+ private var _genres:String;
+ public var icon:Object;
+
+
+ /**
+ * Creates a new Song instance with the specified values
+ */
+ public function Song(title:String, artist:String, year:uint, filename:String, genres:Array)
+ {
+ this._title = title;
+ this._artist = artist;
+ this._year = year;
+ this._filename = filename;
+ // genres are passed in as an array,
+ // but stored as a semicolon-separated string.
+ this._genres = genres.join(";");
+ }
+
+
+ // ------- Public Accessors -------
+
+ public function get title():String
+ {
+ return this._title;
+ }
+ public function set title(value:String):void
+ {
+ this._title = value;
+ }
+
+ public function get artist():String
+ {
+ return _artist;
+ }
+ public function set artist(value:String):void
+ {
+ this._artist = value;
+ }
+
+ public function get year():uint
+ {
+ return _year;
+ }
+ public function set year(value:uint):void
+ {
+ this._year = value;
+ }
+
+ public function get filename():String
+ {
+ return _filename;
+ }
+ public function set filename(value:String):void
+ {
+ this._filename = value;
+ }
+
+ 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(";");
+ }
+
+
+ /**
+ * Provides a String representation of this instance
+ */
+ public function toString():String
+ {
+ var result:String = "";
+ result += this._title;
+ result += " (" + this._year + ")";
+ result += " - " + this._artist;
+ if (this._genres != null && this._genres.length > 0)
+ {
+ result += " [" + this._genres.replace(";", ", ") + "]";
+ }
+ return result.toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PlayList/com/example/programmingas3/playlist/SortProperty.as b/PlayList/com/example/programmingas3/playlist/SortProperty.as
new file mode 100644
index 0000000..8cdfef5
--- /dev/null
+++ b/PlayList/com/example/programmingas3/playlist/SortProperty.as
@@ -0,0 +1,38 @@
+package com.example.programmingas3.playlist
+{
+ /**
+ * A pseudo-enum representing the different properties by which a Song instance
+ * can be sorted.
+ */
+ public final class SortProperty
+ {
+ // ------- Public Constants -------
+
+ // These constants represent the different properties
+ // that the list of songs can be sorted by,
+ // providing a mechanism to map a "friendly" name to the actual property name
+ public static const TITLE:SortProperty = new SortProperty("title");
+ public static const ARTIST:SortProperty = new SortProperty("artist");
+ public static const YEAR:SortProperty = new SortProperty("year");
+
+
+ // ------- Private Variables -------
+ private var _propertyName:String;
+
+
+ // ------- Constructor -------
+
+ public function SortProperty(property:String)
+ {
+ _propertyName = property;
+ }
+
+
+ // ------- Public Properties -------
+
+ public function get propertyName():String
+ {
+ return _propertyName;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PodcastPlayer/PodcastPlayer.fla b/PodcastPlayer/PodcastPlayer.fla
new file mode 100644
index 0000000..864de8d
Binary files /dev/null and b/PodcastPlayer/PodcastPlayer.fla differ
diff --git a/PodcastPlayer/PodcastPlayer.mxml b/PodcastPlayer/PodcastPlayer.mxml
new file mode 100644
index 0000000..e25d01c
--- /dev/null
+++ b/PodcastPlayer/PodcastPlayer.mxml
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PodcastPlayer/SoundPlayer.mxml b/PodcastPlayer/SoundPlayer.mxml
new file mode 100644
index 0000000..af478c6
--- /dev/null
+++ b/PodcastPlayer/SoundPlayer.mxml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PodcastPlayer/com/example/programmingas3/podcastplayer/PlayButtonRenderer.as b/PodcastPlayer/com/example/programmingas3/podcastplayer/PlayButtonRenderer.as
new file mode 100644
index 0000000..820980c
--- /dev/null
+++ b/PodcastPlayer/com/example/programmingas3/podcastplayer/PlayButtonRenderer.as
@@ -0,0 +1,50 @@
+package com.example.programmingas3.podcastplayer
+{
+ import fl.controls.LabelButton;
+ import fl.controls.listClasses.CellRenderer;
+ import fl.controls.listClasses.ICellRenderer;
+ import fl.controls.listClasses.ListData;
+ import fl.events.DataGridEvent;
+ import flash.display.MovieClip;
+ import flash.events.MouseEvent;
+
+ public class PlayButtonRenderer extends LabelButton implements ICellRenderer
+ {
+ private var _listData:ListData;
+ private var _data:Object;
+
+ public function PlayButtonRenderer():void
+ {
+ //addEventListener(MouseEvent.CLICK, onPlayButtonClick);
+ }
+
+ public function set data(d:Object):void {
+ _data = d;
+ this.label = "Play";
+ this.setStyle("icon", playIcon);
+ }
+ public function get data():Object {
+ return _data;
+ }
+ public function set listData(ld:ListData):void {
+ _listData = ld;
+ }
+ public function get listData():ListData {
+ return _listData;
+ }
+ public override function set selected(s:Boolean):void {
+ _selected = s;
+ }
+ public override function get selected():Boolean {
+ return _selected;
+ }
+
+ public function onPlayButtonClick():void
+ {
+ var evt:DataGridEvent = new DataGridEvent("cellButtonClick", false, false, this.listData.column, this.listData.row, this);
+ this.listData.owner.dispatchEvent(evt);
+ }
+ }
+}
+
+
diff --git a/PodcastPlayer/com/example/programmingas3/podcastplayer/PodcastPlayer.as b/PodcastPlayer/com/example/programmingas3/podcastplayer/PodcastPlayer.as
new file mode 100644
index 0000000..bdc4327
--- /dev/null
+++ b/PodcastPlayer/com/example/programmingas3/podcastplayer/PodcastPlayer.as
@@ -0,0 +1,170 @@
+package com.example.programmingas3.podcastplayer
+{
+ import com.example.programmingas3.podcastplayer.RSSItem;
+ import com.example.programmingas3.utils.DateUtil;
+ import com.example.programmingas3.podcastplayer.URLService;
+ import com.example.programmingas3.podcastplayer.RSSChannel;
+ import com.example.programmingas3.podcastplayer.PlayButtonRenderer;
+
+ import fl.controls.dataGridClasses.DataGridColumn;
+ import fl.data.DataProvider;
+ import fl.events.DataGridEvent;
+ import fl.events.ListEvent;
+
+ import flash.display.MovieClip;
+ import flash.events.Event;
+ import flash.events.DataEvent;
+ import flash.events.ErrorEvent;
+ import flash.events.IOErrorEvent;
+ import flash.events.SecurityErrorEvent;
+
+ public class PodcastPlayer extends MovieClip
+ {
+ public var service:URLService;
+
+ public var feeds:XMLList;
+ public var feedArray:Array;
+ public var articles:XMLList;
+
+ public var currentFeed:RSSChannel;
+
+ public const PLAY_COLUMN:int = 2;
+
+ public function PodcastPlayer():void
+ {
+ var col1:DataGridColumn = new DataGridColumn("publishDate");
+ col1.headerText = "date";
+ col1.sortOptions = Array.NUMERIC;
+ col1.labelFunction = formatDateColumn;
+ col1.width = 90;
+
+ var col2:DataGridColumn = new DataGridColumn("title");
+ col2.headerText = "Title";
+ col2.width = 220;
+
+ var col3:DataGridColumn = new DataGridColumn("Play");
+ col3.cellRenderer = PlayButtonRenderer;
+ col3.width = 70;
+
+ this.articleGrid.addColumn(col1);
+ this.articleGrid.addColumn(col2);
+ this.articleGrid.addColumn(col3);
+
+ this.articleGrid.addEventListener(fl.events.ListEvent.ITEM_CLICK , onGridPlay);
+ this.feedList.addEventListener(Event.CHANGE, onFeedSelected);
+
+ var configService:URLService = new URLService("playerconfig.xml");
+ configService.addEventListener(DataEvent.DATA, onConfigReceived);
+ configService.addEventListener(IOErrorEvent.IO_ERROR, onFeedError);
+ configService.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFeedError);
+ configService.send();
+ }
+
+ public function onConfigReceived(event:DataEvent):void
+ {
+ var result:XML = new XML(event.data);
+ this.feeds = result.feed;
+ trace("feeds.length()=" + this.feeds.length());
+
+ // convert the XMLList to an array of objects
+ var feedObj:Object;
+ this.feedArray = new Array();
+ for each (var feed:XML in this.feeds)
+ {
+ feedObj = { label:feed.label, url:feed.url };
+ this.feedArray.push(feedObj);
+ }
+
+ this.feedList.dataProvider = new DataProvider(this.feedArray);
+ this.feedList.selectedIndex = 0;
+ this.feedList.dispatchEvent(new Event(Event.CHANGE));
+ }
+
+ public function onFeedSelected(event:Event):void
+ {
+ var url:String = feedList.selectedItem.url.toString();
+ if (url != null)
+ {
+ service = new URLService(url);
+ service.addEventListener(DataEvent.DATA, onFeedReceived);
+ service.addEventListener(IOErrorEvent.IO_ERROR, onFeedError);
+ service.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFeedError);
+ service.send();
+ }
+ }
+
+ public function onFeedReceived(event:DataEvent):void
+ {
+ var result:XML = new XML(event.data);
+
+ var channelXML:XMLList = result.child("channel");
+ if (channelXML != null)
+ {
+ this.currentFeed = RSSChannel.parseFeed(channelXML[0]);
+
+ // massage the data so the DataGrid is happy
+ for each (var item:RSSItem in this.currentFeed.items)
+ {
+ item["Play"] = "Play";
+ if (item.duration == "null")
+ {
+ item.duration = "";
+ }
+ }
+
+ if (this.currentFeed != null)
+ {
+ this.articleGrid.dataProvider = new DataProvider(this.currentFeed.items);
+ this.feedImg.source = this.currentFeed.imageUrl;
+
+ this.feedDescriptionTxt.htmlText = this.currentFeed.getDescriptionHTML();
+ }
+ }
+ }
+
+ public function onFeedError(event:ErrorEvent):void
+ {
+ showError(new Error(event.text));
+ }
+
+ /**
+ * A common method for displaying error messages.
+ * Should be enhanced to show a dialog box with an error message.
+ */
+ public function showError(e:Error):void
+ {
+ trace("Error: " + e.message);
+ }
+
+ /**
+ * Called when the Play button is clicked inside a row of the grid.
+ */
+ public function onGridPlay(evt:ListEvent):void
+ {
+ trace("onGridPlay col=" + evt.columnIndex + ", url=" + evt.item.soundUrl);
+ if (evt.columnIndex == PLAY_COLUMN)
+ {
+ var item:Object = evt.item;
+ var title:String = item.title;
+ if (item.soundUrl != null)
+ {
+ this.player.load(item.soundUrl, title);
+ }
+ }
+ }
+
+ public function formatDateColumn(data:Object):String
+ {
+ // remove milliseconds
+ var dateStr:String = DateUtil.formatShort(data.publishDate);
+ return dateStr;
+ }
+
+ public function formatDurationColumn(data:Object):String
+ {
+ var durStr:String = data.duration;
+ return durStr as String;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSBase.as b/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSBase.as
new file mode 100644
index 0000000..b421b96
--- /dev/null
+++ b/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSBase.as
@@ -0,0 +1,163 @@
+package com.example.programmingas3.podcastplayer
+{
+ import com.example.programmingas3.utils.DateUtil;
+ import flash.events.EventDispatcher;
+
+ /**
+ * This class contains properties that are common to both an RSSChannel and an RSSItem.
+ */
+ public dynamic class RSSBase extends EventDispatcher
+ {
+ // Required RSS 2.0 elements
+ public var title:String;
+ public var link:String;
+ public var description:String;
+ private var _pubDate:String;
+ private var _category:Object;
+
+ public var imageUrl:String;
+ private var _image:Object;
+
+ // derived values
+ public var categoryArray:Array = new Array();
+ public var publishDate:Date;
+
+ public function set pubDate(dateStr:String):void
+ {
+ this._pubDate = dateStr;
+ if (dateStr != null)
+ {
+ this.publishDate = DateUtil.parseRFC822(dateStr);
+ }
+ }
+
+ public function get pubDate():String
+ {
+ return _pubDate;
+ }
+
+ public function get category():Object
+ {
+ return _category;
+ }
+
+ public function set category(catObj:Object):void
+ {
+ if (catObj != null)
+ {
+ var catXml:XML = catObj as XML;
+ if (catXml != null && catXml.attributes().length() > 0)
+ {
+ var domain:String = getAttribute(catXml, "domain");
+ var cat:String = getAttribute(catXml, "text");
+ var kittens:XMLList = catXml.children();
+ for (var i:int = 0; i < kittens.length(); i++)
+ {
+ cat += " > " + getAttribute(kittens[0], "text");
+ }
+ while (kittens.length() > 0)
+ {
+ cat += " > " + getAttribute(kittens[0], "text");
+ kittens = kittens[0].children();
+ }
+
+ this._category = cat;
+ if (this.categoryArray.length > 0)
+ {
+ this.categoryArray.push(cat);
+ }
+ else
+ {
+ this.categoryArray = [ cat ];
+ }
+ }
+ else
+ {
+ this._category = catObj.toString();
+ if (this.categoryArray.length > 0)
+ {
+ this.categoryArray.push( this._category );
+ }
+ else
+ {
+ this.categoryArray = [ this._category ];
+ }
+ }
+ }
+ }
+
+ public function buildCategory(catStr:String, catObj:XML):String
+ {
+ var subCat:String = getAttribute(catObj, "text");
+ if (subCat != null)
+ {
+ catStr += " > " + subCat;
+ }
+
+ var kittens:XMLList = catObj.children();
+ if (kittens.length() > 0)
+ {
+ return buildCategory(catStr, kittens[0]);;
+ }
+
+ return catStr;
+ }
+
+ public function get image():Object
+ {
+ return this._image;
+ }
+
+ public function set image(elementObj:Object):void
+ {
+ var element:XML = elementObj as XML;
+ var imageObj:Object = new Object();
+
+ if (element.url.length() > 0)
+ {
+ imageObj.url = getElementText(element, "url");
+ imageObj.title = getElementText(element, "title");
+ imageObj.link = getElementText(element, "link");
+ imageObj.width = getElementText(element, "width");
+ imageObj.height = getElementText(element, "height");
+ imageObj.description = getElementText(element, "description");
+
+ this.imageUrl = imageObj.url;
+ this._image = imageObj;
+ }
+ else if (element.@href.length() > 0)
+ {
+ this.imageUrl = getAttribute(element, "href")
+ imageObj.url = this.imageUrl;
+ }
+ this._image = imageObj;
+ }
+
+ public function set itunes_image(elementObj:Object):void
+ {
+ this.image = elementObj;
+ }
+
+ public static function getAttribute(element:XML, attName:String):String
+ {
+ var attValue:String = "";
+ var attribs:XMLList = element.attribute(attName);
+ if (attribs.length() > 0)
+ {
+ attValue = attribs[0].toString();
+ }
+ return attValue;
+ }
+
+ public static function getElementText(parent:XML, elemName:String):String
+ {
+ var elemText:String = "";
+ var kids:XMLList = parent.child(elemName);
+ if (kids.length() > 0)
+ {
+ elemText = kids[0].toString();
+ }
+ return elemText;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSChannel.as b/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSChannel.as
new file mode 100644
index 0000000..18d155b
--- /dev/null
+++ b/PodcastPlayer/com/example/programmingas3/podcastplayer/RSSChannel.as
@@ -0,0 +1,216 @@
+package com.example.programmingas3.podcastplayer
+{
+ import com.example.programmingas3.utils.DateUtil;
+ import flash.geom.Point;
+
+ public dynamic class RSSChannel extends RSSBase
+ {
+ /*
+ * title
+ * description or itunes:summary
+ * itunes:subtitle
+ * copyright
+ * language
+ * link
+ * pubDate
+ * itunes:category (recursive)
+ * itunes:keywords
+ * image->url,title,link or itunes:image
+ * itunes:explicit
+ */
+
+ /*
+ * The following RSS 2.0 elements are defined in the RSSBase superclass
+
+ // Required RSS 2.0 elements
+ public var title:String;
+ public var link:String;
+ public var description:String;
+ public var pubDate:String;
+ public var category:String;
+
+ // derived values
+ public var categoryArray:Array;
+ public var publishDate:Date;
+ */
+
+ // ITunes RSS 2.0 elements
+ public var subtitle:String;
+ public var summary:String;
+
+ // Optional RSS 2.0 elements
+ public var author:String;
+ public var content:Object; // contains fileSize, rating, type, uid, url
+ public var contentType:String;
+ public var copyright:String;
+ public var language:String;
+ public var webMaster:String;
+ public var managingEditor:String;
+
+ public var guid:String;
+ public var _keywords:String;
+
+ public var origLink:String;
+ public var _lastBuildDate:String;
+ public var lastPublishDate:Date;
+
+ public var uid:String;
+
+ public var keywordsArray:Array;
+
+ public var items:Array;
+
+ public function get keywords():String
+ {
+ return this._keywords;
+ }
+
+ public function set keywords(keywordStr:String):void
+ {
+ this._keywords = keywordStr;
+
+ var delim:String = " ";
+ if (keywordStr.indexOf(", ") > 0)
+ {
+ delim = ", ";
+ }
+ else if (keywordStr.indexOf(", ") > 0)
+ {
+ delim = ",";
+ }
+ this.keywordArray = keywordStr.split(delim);
+ }
+
+ public function get lastBuildDate():String
+ {
+ return this._lastBuildDate;
+ }
+
+ public function set lastBuildDate(dateStr:String):void
+ {
+ if (dateStr != null)
+ {
+ this._lastBuildDate = dateStr;
+ this.lastPublishDate = DateUtil.parseRFC822(dateStr);
+ }
+ }
+
+ public static function parseFeed(channelXml:XML):RSSChannel
+ {
+ var feed:RSSChannel = new RSSChannel();
+
+ var kids:XMLList = channelXml.children();
+ var elementName:String;
+ var propName:String;
+ for each (var element:XML in kids)
+ {
+ elementName = element.localName();
+ if (elementName != "item")
+ {
+ // handle itunes names
+ var iTunesIndex:int = element.name().toString().indexOf("itunes");
+ if (iTunesIndex >= 0)
+ {
+ propName = "itunes_" + elementName;
+ }
+ else
+ {
+ propName = elementName;
+ }
+
+ if (element.hasSimpleContent() && element.attributes().length() == 0)
+ {
+ feed[propName] = element.toString();
+ }
+ else
+ {
+ feed[propName] = element;
+ }
+ }
+ }
+
+ var itemArray:Array = new Array();
+ var itemObj:Object;
+ var itemList:XMLList = channelXml.item;
+ for each (var itemXml:XML in itemList)
+ {
+ itemObj = RSSItem.parseItem(itemXml);
+ itemArray.push(itemObj);
+ }
+ feed.items = itemArray;
+
+ return feed;
+ }
+
+ public function getFullHTML():String
+ {
+ var html:String = "";
+ if (this.imageUrl != null)
+ {
+ html += "";
+ }
+
+ if (this.subtitle != null)
+ {
+ html += "
" + this.subtitle + "
";
+ }
+
+ if (this.description != null)
+ {
+ html += "
" + this.description + "
";
+ }
+ else if (this.summary != null)
+ {
+ html += "
+ *
+ */
+ private function buildItemHTML(itemTitle:String,
+ itemDescription:String,
+ itemLink:String):XMLList {
+ default xml namespace = new Namespace();
+ var body:XMLList = new XMLList();
+ body += new XML("" + itemTitle + "");
+ var p:XML = new XML("
" + itemDescription + "
");
+
+ var link:XML = ;
+ link.@href = itemLink; //
+ link.font.@color = "#008000"; //
+ // 0x008000 = green
+ link.font = "More...";
+
+ p.appendChild( );
+ p.appendChild(link);
+ body += p;
+ return body;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ReorderByZ/Box3D.as b/ReorderByZ/Box3D.as
new file mode 100644
index 0000000..c6cfbfb
--- /dev/null
+++ b/ReorderByZ/Box3D.as
@@ -0,0 +1,77 @@
+
+package {
+ import flash.display.DisplayObject;
+ import flash.display.Sprite;
+ import flash.display.Stage;
+ import flash.geom.Rectangle;
+ import ThreeDSortElement;
+
+ public class Box3D extends Sprite {
+ public var faces:Array = new Array(6);
+
+ public function Box3D(backSrc:DisplayObject, bottomSrc:DisplayObject, frontSrc:DisplayObject, leftSrc:DisplayObject, rightSrc:DisplayObject, topSrc:DisplayObject)
+ {
+ // initialize faces vector
+ for (var ind:uint = 0; ind < 6; ind++)
+ faces[ind] = new ThreeDSortElement(0,new Sprite());
+
+ faces[0].child.addChild(backSrc);
+ faces[1].child.addChild(bottomSrc);
+ faces[2].child.addChild(frontSrc);
+ faces[3].child.addChild(leftSrc);
+ faces[4].child.addChild(rightSrc);
+ faces[5].child.addChild(topSrc);
+
+ // sorting usually works best when the position (or regPoint) is a point in the center of the face.
+ // Offset child x/y to make that so.
+ var localRect:Rectangle = backSrc.getBounds(backSrc); // assumes all faces are the same size
+ var centerOffsetX:Number = localRect.x + localRect.width/2;
+ var centerOffsetY:Number = localRect.y + localRect.height/2;
+ for (ind = 0; ind < 6; ind++) {
+ var offsetChild:DisplayObject = faces[ind].child.getChildAt(0);
+ offsetChild.x = -centerOffsetX;
+ offsetChild.y = -centerOffsetY;
+
+ // while we're here, add each face as a child.
+ this.addChild(faces[ind].child);
+ }
+ // left face
+ faces[3].child.rotationY = 90;
+ faces[3].child.x = -centerOffsetX;
+ // right face
+ faces[4].child.rotationY = -90;
+ faces[4].child.x = centerOffsetX;
+ // back face
+ faces[0].child.z = centerOffsetX;
+ faces[0].child.rotationY = 180;
+ // front face
+ faces[2].child.z = -centerOffsetX;
+ // bottom face
+ faces[1].child.rotationX = 90;
+ faces[1].child.y = centerOffsetY;
+ // top face
+ faces[5].child.rotationX = -90;
+ faces[5].child.y = -centerOffsetY;
+
+ this.z = 0; // make "this" 3d as well
+ }
+
+ // Remove all children, sort them by global z distance, then add them back in sorted order.
+ public function ReorderChildren()
+ {
+ // Reorder all the children based on Z.
+ for(var ind:uint = 0; ind < 6; ind++) {
+ // getRelativeMatrix3D gets a Matrix3D reprsenting the pos/rot/scale of one DisplayObject
+ // relative to another. Get the Matrix3D relative to the root to get the global position.
+ faces[ind].z = faces[ind].child.transform.getRelativeMatrix3D(root).position.z;
+
+ this.removeChild(faces[ind].child);
+ }
+ // sort them on z and add them back (in reverse order).
+ faces.sortOn("z", Array.NUMERIC | Array.DESCENDING);
+ for (ind = 0; ind < 6; ind++) {
+ this.addChild(faces[ind].child);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ReorderByZ/ReorderByZDepthExample.as b/ReorderByZ/ReorderByZDepthExample.as
new file mode 100644
index 0000000..54b2917
--- /dev/null
+++ b/ReorderByZ/ReorderByZDepthExample.as
@@ -0,0 +1,62 @@
+package {
+ import flash.display.MovieClip;
+ import flash.display.Graphics;
+ import flash.events.MouseEvent;
+ import flash.display.Stage;
+ import flash.geom.Matrix3D;
+ import flash.geom.Vector3D;
+ import flash.display.GradientType;
+
+ public class ReorderByZDepthExample extends MovieClip {
+
+ public var box:Box3D; // the 3d box
+ private var boxSides:Array = new Array(6); // the 6 faces of the box
+ private var boxMat3D:Matrix3D; // cached referece to box's .transform.matrix3D
+
+ private var savedStageX:Number; // last position of the mouse in stage coordiantes.
+ private var savedStageY:Number;
+
+ public function ReorderByZDepthExample()
+ {
+ for (var side:uint = 0; side < 6; side++) { // create 6 circles to use as sides.
+ var s:MovieClip = new MovieClip();
+ s.graphics.beginFill(0x9ff << (side*4), 1); // vary the color
+ s.graphics.drawCircle(256,256,256);
+ boxSides[side] = s;
+ }
+ // note: we could use any DisplayObject for the sides. Video, Vector graphics, arbitrary MovieClips, etc.
+ // Box3D assumes they all have the same dimensions and regpoint, however.
+ box = new Box3D( boxSides[0], boxSides[1], boxSides[2],
+ boxSides[3], boxSides[4], boxSides[5] );
+
+ // place ourselves in the middle of the stage, back 500 in z.
+ this.addChild(box);
+ this.x = 300;
+ this.y = 300;
+ this.z = 500;
+
+ // save reference to box's matrix3D for use later.
+ boxMat3D = box.transform.matrix3D; // its a real reference, unlike ".transform.matrix", which is a value copy
+
+ savedStageX = stage.mouseX;
+ savedStageY = stage.mouseY;
+ stage.addEventListener(flash.events.MouseEvent.MOUSE_MOVE, mouseMoveH);
+ }
+
+ private function mouseMoveH(e:MouseEvent)
+ {
+ var diffY:Number = savedStageY - e.stageY; // store stageX/stageY before modifying box
+ var diffX:Number = savedStageX - e.stageX; // Its computed dynamically can can change (!)
+ savedStageX = e.stageX; // after we rotate boxMat3D.
+ savedStageY = e.stageY;
+
+ // move in stage Y, then rotate about our parent's Xaxis so that movement matches mouse
+ boxMat3D.appendRotation(-diffY, Vector3D.X_AXIS);
+ // move in stage X, then rotate about parent's Yaxis.
+ boxMat3D.appendRotation(diffX, Vector3D.Y_AXIS);
+ // z ordering of faces may have changed. Re-layer children to match z order.
+ box.ReorderChildren();
+ }
+ }
+}
+
diff --git a/ReorderByZ/ReorderByZDepthExample.fla b/ReorderByZ/ReorderByZDepthExample.fla
new file mode 100644
index 0000000..a196ec2
Binary files /dev/null and b/ReorderByZ/ReorderByZDepthExample.fla differ
diff --git a/ReorderByZ/ThreeDSortElement.as b/ReorderByZ/ThreeDSortElement.as
new file mode 100644
index 0000000..98dc173
--- /dev/null
+++ b/ReorderByZ/ThreeDSortElement.as
@@ -0,0 +1,11 @@
+package {
+ import flash.display.Sprite;
+
+ public final class ThreeDSortElement
+ {
+ public var z:Number;
+ public var child:Sprite;
+
+ public function ThreeDSortElement(depth:Number, s:Sprite) { z = depth; child = s; };
+ }
+}
\ No newline at end of file
diff --git a/RuntimeAssetsExplorer/GeometricAssets.as b/RuntimeAssetsExplorer/GeometricAssets.as
new file mode 100644
index 0000000..7329f99
--- /dev/null
+++ b/RuntimeAssetsExplorer/GeometricAssets.as
@@ -0,0 +1,16 @@
+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" ];
+ }
+ }
+}
\ No newline at end of file
diff --git a/RuntimeAssetsExplorer/GeometricAssets.fla b/RuntimeAssetsExplorer/GeometricAssets.fla
new file mode 100644
index 0000000..8dc95e7
Binary files /dev/null and b/RuntimeAssetsExplorer/GeometricAssets.fla differ
diff --git a/RuntimeAssetsExplorer/RuntimeAssetsExample.as b/RuntimeAssetsExplorer/RuntimeAssetsExample.as
new file mode 100644
index 0000000..7533881
--- /dev/null
+++ b/RuntimeAssetsExplorer/RuntimeAssetsExample.as
@@ -0,0 +1,79 @@
+package {
+ import fl.data.DataProvider;
+ import flash.display.Sprite;
+ import flash.display.Shape;
+ import flash.display.MovieClip;
+ import flash.display.Loader;
+ import flash.system.LoaderContext;
+ import flash.system.ApplicationDomain;
+ import flash.net.URLRequest;
+ import flash.events.*;
+ import flash.utils.*;
+ import flash.geom.ColorTransform;
+
+ import com.example.programmingas3.runtimeassetexplorer.RuntimeLibrary;
+
+ public class RuntimeAssetExplorer extends Sprite {
+
+ public function RuntimeAssetExplorer() {
+ var path:URLRequest = new URLRequest("GeometricAssets.swf");
+ var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
+ var loader:Loader = new Loader();
+ loader.contentLoaderInfo.addEventListener(Event.COMPLETE,runtimeAssetsLoadComplete);
+ loader.load(path,context);
+ }
+ private function runtimeAssetsLoadComplete(event:Event):void {
+ var ra:* = event.target.content;
+ populateDropdown(ra.getAssets());
+
+ btn1.label = "Add";
+ btn1.addEventListener(MouseEvent.CLICK, addAssetToStage);
+ }
+ private function populateDropdown(assetList:Array):void {
+ var shortNameList:Array = new Array();
+ for(var i:uint = 0; i < assetList.length; i++) {
+ var className:String = assetList[i];
+ var splicePoint:Number = className.lastIndexOf(".")+1;
+ var shortName:String = splicePoint ? className.substr(splicePoint) : className;
+ shortNameList.push( { label:shortName, data:className } );
+ }
+ cb.dataProvider = new DataProvider(shortNameList);
+ cb.setSize(150,20);
+ }
+ private function drawAsset(AssetClass:Class, posX:uint, posY:uint):MovieClip {
+ var mc:MovieClip = new AssetClass();
+ mc.transform.colorTransform = getRandomColor();
+ mc.rotation = Math.random() * 360;
+ mc.addEventListener(MouseEvent.CLICK,rotate);
+ mc.addEventListener(MouseEvent.MOUSE_DOWN,startDragAsset);
+ mc.addEventListener(MouseEvent.MOUSE_UP,stopDragAsset);
+ mc.stop();
+ mc.x = posX;
+ mc.y = posY;
+ addChild(mc);
+ return mc;
+ }
+ private function rotate(event:MouseEvent):void {
+ var target:MovieClip = MovieClip(event.currentTarget);
+ target.play();
+ }
+ private function getRandomColor():ColorTransform {
+ return new ColorTransform(Math.random(),Math.random(),Math.random(),1,(Math.random() * 512)-255,(Math.random() * 512)-255,(Math.random() * 512)-255,0);
+ }
+ private function startDragAsset(event:MouseEvent):void {
+ var target:MovieClip = MovieClip(event.currentTarget);
+ target.startDrag();
+ }
+ private function stopDragAsset(event:MouseEvent):void {
+ var target:MovieClip = MovieClip(event.currentTarget);
+ target.stopDrag();
+ }
+
+ private function addAssetToStage(me:MouseEvent):void {
+ var AssetClass:Class = getDefinitionByName(cb.selectedItem.data) as Class;
+ if(AssetClass) {
+ drawAsset(AssetClass, 200, 75);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/RuntimeAssetsExplorer/RuntimeAssetsExample.fla b/RuntimeAssetsExplorer/RuntimeAssetsExample.fla
new file mode 100644
index 0000000..1b3ca58
Binary files /dev/null and b/RuntimeAssetsExplorer/RuntimeAssetsExample.fla differ
diff --git a/RuntimeAssetsExplorer/RuntimeAssetsExplorer.mxml b/RuntimeAssetsExplorer/RuntimeAssetsExplorer.mxml
new file mode 100644
index 0000000..4453068
--- /dev/null
+++ b/RuntimeAssetsExplorer/RuntimeAssetsExplorer.mxml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingBox.as b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingBox.as
new file mode 100644
index 0000000..401ac4b
--- /dev/null
+++ b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingBox.as
@@ -0,0 +1,10 @@
+package com.example.programmingas3.runtimeassetexplorer
+{
+ import flash.display.MovieClip;
+
+ public class AnimatingBox extends MovieClip
+ {
+ public function AnimatingBox() {
+ }
+ }
+}
\ No newline at end of file
diff --git a/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingStar.as b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingStar.as
new file mode 100644
index 0000000..45f0063
--- /dev/null
+++ b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/AnimatingStar.as
@@ -0,0 +1,10 @@
+package com.example.programmingas3.runtimeassetexplorer
+{
+ import flash.display.MovieClip;
+
+ public class AnimatingStar extends MovieClip
+ {
+ public function AnimatingStar() {
+ }
+ }
+}
\ No newline at end of file
diff --git a/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/RuntimeLibrary.as b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/RuntimeLibrary.as
new file mode 100644
index 0000000..d89d0eb
--- /dev/null
+++ b/RuntimeAssetsExplorer/com/example/programmingas3/runtimeassetexplorer/RuntimeLibrary.as
@@ -0,0 +1,7 @@
+package com.example.programmingas3.runtimeassetexplorer
+{
+ public interface RuntimeLibrary
+ {
+ function getAssets():Array
+ }
+}
\ No newline at end of file
diff --git a/SimpleClock/flash pro version/SimpleClock.fla b/SimpleClock/flash pro version/SimpleClock.fla
new file mode 100644
index 0000000..10b56a1
Binary files /dev/null and b/SimpleClock/flash pro version/SimpleClock.fla differ
diff --git a/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/AnalogClockFace.as b/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/AnalogClockFace.as
new file mode 100644
index 0000000..057eca4
--- /dev/null
+++ b/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/AnalogClockFace.as
@@ -0,0 +1,210 @@
+package com.example.programmingas3.clock
+{
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.text.StaticText;
+ import flash.events.*;
+ import flash.text.TextField;
+ import flash.text.TextFormat;
+
+ /**
+ * Displays a round clock face with an hour hand, a minute hand, and a second hand.
+ */
+ public class AnalogClockFace extends Sprite
+ {
+ /**
+ * The desired width of this component, as opposed to the .width
+ * property which represents tha actual width.
+ */
+ public var w:uint = 200;
+
+ /**
+ * The desired height of this component, as opposed to the .height
+ * property which represents tha actual height.
+ */
+ public var h:uint = 200;
+
+ /**
+ * The radius from the center of the clock to the
+ * outer edge of the circular face outline.
+ */
+ public var radius:uint;
+
+ /**
+ * The coordinates of the center of the face.
+ */
+ public var centerX:int;
+ public var centerY:int;
+
+ /**
+ * The three hands of the clock.
+ */
+ public var hourHand:Shape;
+ public var minuteHand:Shape;
+ public var secondHand:Shape;
+
+ /**
+ * The colors of the background and each hand.
+ * These could be set using parameters or
+ * styles in the future.
+ */
+ public var bgColor:uint = 0xEEEEFF;
+ public var hourHandColor:uint = 0x003366;
+ public var minuteHandColor:uint = 0x000099;
+ public var secondHandColor:uint = 0xCC0033;
+
+ /**
+ * Stores a snapshot of the current time, so it
+ * doesn't change while in the middle of drawing the
+ * three hands.
+ */
+ public var currentTime:Date;
+
+ /**
+ * Contructs a new clock face. The width and
+ * height will be equal.
+ */
+ public function AnalogClockFace(w:uint)
+ {
+ this.w = w;
+ this.h = w;
+
+ // Rounds to the nearest pixel
+ this.radius = Math.round(this.w / 2);
+
+ // The face is always square now, so the
+ // distance to the center is the same
+ // horizontally and vertically
+ this.centerX = this.radius;
+ this.centerY = this.radius;
+ }
+
+ /**
+ * Creates the outline, hour labels, and clock hands.
+ */
+ public function init():void
+ {
+ // draws the circular clock outline
+ drawBorder();
+
+ // draws the hour numbers
+ drawLabels();
+
+ // creates the three clock hands
+ createHands();
+ }
+
+ /**
+ * Draws a circular border.
+ */
+ public function drawBorder():void
+ {
+ graphics.lineStyle(0.5, 0x999999);
+ graphics.beginFill(bgColor);
+ graphics.drawCircle(centerX, centerY, radius);
+ graphics.endFill();
+ }
+
+ /**
+ * Puts numeric labels at the hour points.
+ */
+ public function drawLabels():void
+ {
+ for (var i:Number = 1; i <= 12; i++)
+ {
+ // Creates a new TextField showing the hour number
+ var label:TextField = new TextField();
+ label.text = i.toString();
+
+ // Places hour labels around the clock face.
+ // The sin() and cos() functions both operate on angles in radians.
+ var angleInRadians:Number = i * 30 * (Math.PI/180)
+
+ // Place the label using the sin() and cos() functions to get the x,y coordinates.
+ // The multiplier value of 0.9 puts the labels inside the outline of the clock face.
+ // The integer value at the end of the equation adjusts the label position,
+ // since the x,y coordinate is in the upper left corner.
+ label.x = centerX + (0.9 * radius * Math.sin( angleInRadians )) - 5;
+ label.y = centerY - (0.9 * radius * Math.cos( angleInRadians )) - 9;
+
+ // Formats the label text.
+ var tf:TextFormat = new TextFormat();
+ tf.font = "Arial";
+ tf.bold = "true";
+ tf.size = 12;
+ label.setTextFormat(tf);
+
+ // Adds the label to the clock face display list.
+ addChild(label);
+ }
+ }
+
+ /**
+ * Creates hour, minute, and second hands using the 2D drawing API.
+ */
+ public function createHands():void
+ {
+ // Uses a Shape since it's the simplest component that supports
+ // the 2D drawing API.
+ var hourHandShape:Shape = new Shape();
+ drawHand(hourHandShape, Math.round(radius * 0.5), hourHandColor, 3.0);
+ this.hourHand = Shape(addChild(hourHandShape));
+ this.hourHand.x = centerX;
+ this.hourHand.y = centerY;
+
+ var minuteHandShape:Shape = new Shape();
+ drawHand(minuteHandShape, Math.round(radius * 0.8), minuteHandColor, 2.0);
+ this.minuteHand = Shape(addChild(minuteHandShape));
+ this.minuteHand.x = centerX;
+ this.minuteHand.y = centerY;
+
+ var secondHandShape:Shape = new Shape();
+ drawHand(secondHandShape, Math.round(radius * 0.9), secondHandColor, 0.5);
+ this.secondHand = Shape(addChild(secondHandShape));
+ this.secondHand.x = centerX;
+ this.secondHand.y = centerY;
+ }
+
+ /**
+ * Draws a clock hand with a given size, color, and thickness.
+ */
+ public function drawHand(hand:Shape, distance:uint, color:uint, thickness:Number):void
+ {
+ hand.graphics.lineStyle(thickness, color);
+ hand.graphics.moveTo(0, distance);
+ hand.graphics.lineTo(0, 0);
+ }
+
+ /**
+ * Called by the parent container when the display is being drawn.
+ */
+ public function draw():void
+ {
+ // Stores the current date and time in an instance variable
+ currentTime = new Date();
+ showTime(currentTime);
+ }
+
+ /**
+ * 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();
+
+ // Multiplies by 6 to get degrees
+ this.secondHand.rotation = 180 + (seconds * 6);
+ this.minuteHand.rotation = 180 + (minutes * 6);
+
+ // Multiplies by 30 to get basic degrees, and then
+ // adds up to 29.5 degrees (59 * 0.5) to account
+ // for the minutes
+ this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/SimpleClock.as b/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/SimpleClock.as
new file mode 100644
index 0000000..3081688
--- /dev/null
+++ b/SimpleClock/flash pro version/com/example/programmingas3/simpleclock/SimpleClock.as
@@ -0,0 +1,67 @@
+package com.example.programmingas3.clock {
+
+ import flash.display.Sprite;
+
+ public class SimpleClock extends Sprite
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ /**
+ * The time display component.
+ */
+ public var face:AnalogClockFace;
+
+ /**
+ * The Timer that acts like a heartbeat for the application.
+ */
+ public var ticker:Timer;
+
+ public static const millisecondsPerMinute:int = 1000 * 60;
+ public static const millisecondsPerHour:int = 1000 * 60 * 60;
+ public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+
+ /**
+ * Sets up a SimpleClock instance.
+ */
+ public function initClock(faceSize:Number = 200):void
+ {
+ // sets the invoice date to today’s date
+ var invoiceDate:Date = new Date();
+
+ // adds 30 days to get the due date
+ var millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+ var dueDate:Date = new Date(invoiceDate.getTime() + (30 * millisecondsPerDay));
+
+ var oneHourFromNow:Date = new Date(); // starts at the current time
+ oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour);
+
+ // 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();
+
+ // 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();
+ }
+
+ /**
+ * Called once per second when the Timer event is received.
+ */
+ public function onTick(evt:TimerEvent):void
+ {
+ // Updates the clock display.
+ face.draw();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleClock/flex_flashbuilder version/SimpleClockApp.mxml b/SimpleClock/flex_flashbuilder version/SimpleClockApp.mxml
new file mode 100644
index 0000000..5f1cb52
--- /dev/null
+++ b/SimpleClock/flex_flashbuilder version/SimpleClockApp.mxml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/AnalogClockFace.as b/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/AnalogClockFace.as
new file mode 100644
index 0000000..4594f81
--- /dev/null
+++ b/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/AnalogClockFace.as
@@ -0,0 +1,212 @@
+package com.example.programmingas3.clock
+{
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.text.StaticText;
+ import flash.events.*;
+ import flash.text.TextField;
+ import flash.text.TextFormat;
+
+ import mx.core.UIComponent;
+
+ /**
+ * Displays a round clock face with an hour hand, a minute hand, and a second hand.
+ */
+ public class AnalogClockFace extends UIComponent
+ {
+ /**
+ * The desired width of this component, as opposed to the .width
+ * property which represents tha actual width.
+ */
+ public var w:uint = 200;
+
+ /**
+ * The desired height of this component, as opposed to the .height
+ * property which represents tha actual height.
+ */
+ public var h:uint = 200;
+
+ /**
+ * The radius from the center of the clock to the
+ * outer edge of the circular face outline.
+ */
+ public var radius:uint;
+
+ /**
+ * The coordinates of the center of the face.
+ */
+ public var centerX:int;
+ public var centerY:int;
+
+ /**
+ * The three hands of the clock.
+ */
+ public var hourHand:Shape;
+ public var minuteHand:Shape;
+ public var secondHand:Shape;
+
+ /**
+ * The colors of the background and each hand.
+ * These could be set using parameters or
+ * styles in the future.
+ */
+ public var bgColor:uint = 0xEEEEFF;
+ public var hourHandColor:uint = 0x003366;
+ public var minuteHandColor:uint = 0x000099;
+ public var secondHandColor:uint = 0xCC0033;
+
+ /**
+ * Stores a snapshot of the current time, so it
+ * doesn't change while in the middle of drawing the
+ * three hands.
+ */
+ public var currentTime:Date;
+
+ /**
+ * Contructs a new clock face. The width and
+ * height will be equal.
+ */
+ public function AnalogClockFace(w:uint)
+ {
+ this.w = w;
+ this.h = w;
+
+ // Rounds to the nearest pixel
+ this.radius = Math.round(this.w / 2);
+
+ // The face is always square now, so the
+ // distance to the center is the same
+ // horizontally and vertically
+ this.centerX = this.radius;
+ this.centerY = this.radius;
+ }
+
+ /**
+ * Creates the outline, hour labels, and clock hands.
+ */
+ public function init():void
+ {
+ // draws the circular clock outline
+ drawBorder();
+
+ // draws the hour numbers
+ drawLabels();
+
+ // creates the three clock hands
+ createHands();
+ }
+
+ /**
+ * Draws a circular border.
+ */
+ public function drawBorder():void
+ {
+ graphics.lineStyle(0.5, 0x999999);
+ graphics.beginFill(bgColor);
+ graphics.drawCircle(centerX, centerY, radius);
+ graphics.endFill();
+ }
+
+ /**
+ * Puts numeric labels at the hour points.
+ */
+ public function drawLabels():void
+ {
+ for (var i:Number = 1; i <= 12; i++)
+ {
+ // Creates a new TextField showing the hour number
+ var label:TextField = new TextField();
+ label.text = i.toString();
+
+ // Places hour labels around the clock face.
+ // The sin() and cos() functions both operate on angles in radians.
+ var angleInRadians:Number = i * 30 * (Math.PI/180)
+
+ // Place the label using the sin() and cos() functions to get the x,y coordinates.
+ // The multiplier value of 0.9 puts the labels inside the outline of the clock face.
+ // The integer value at the end of the equation adjusts the label position,
+ // since the x,y coordinate is in the upper left corner.
+ label.x = centerX + (0.9 * radius * Math.sin( angleInRadians )) - 5;
+ label.y = centerY - (0.9 * radius * Math.cos( angleInRadians )) - 9;
+
+ // Formats the label text.
+ var tf:TextFormat = new TextFormat();
+ tf.font = "Arial";
+ tf.bold = "true";
+ tf.size = 12;
+ label.setTextFormat(tf);
+
+ // Adds the label to the clock face display list.
+ addChild(label);
+ }
+ }
+
+ /**
+ * Creates hour, minute, and second hands using the 2D drawing API.
+ */
+ public function createHands():void
+ {
+ // Uses a Shape since it's the simplest component that supports
+ // the 2D drawing API.
+ var hourHandShape:Shape = new Shape();
+ drawHand(hourHandShape, Math.round(radius * 0.5), hourHandColor, 3.0);
+ this.hourHand = Shape(addChild(hourHandShape));
+ this.hourHand.x = centerX;
+ this.hourHand.y = centerY;
+
+ var minuteHandShape:Shape = new Shape();
+ drawHand(minuteHandShape, Math.round(radius * 0.8), minuteHandColor, 2.0);
+ this.minuteHand = Shape(addChild(minuteHandShape));
+ this.minuteHand.x = centerX;
+ this.minuteHand.y = centerY;
+
+ var secondHandShape:Shape = new Shape();
+ drawHand(secondHandShape, Math.round(radius * 0.9), secondHandColor, 0.5);
+ this.secondHand = Shape(addChild(secondHandShape));
+ this.secondHand.x = centerX;
+ this.secondHand.y = centerY;
+ }
+
+ /**
+ * Draws a clock hand with a given size, color, and thickness.
+ */
+ public function drawHand(hand:Shape, distance:uint, color:uint, thickness:Number):void
+ {
+ hand.graphics.lineStyle(thickness, color);
+ hand.graphics.moveTo(0, distance);
+ hand.graphics.lineTo(0, 0);
+ }
+
+ /**
+ * Called by the parent container when the display is being drawn.
+ */
+ public function draw():void
+ {
+ // Stores the current date and time in an instance variable
+ currentTime = new Date();
+ showTime(currentTime);
+ }
+
+ /**
+ * 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();
+
+ // Multiplies by 6 to get degrees
+ this.secondHand.rotation = 180 + (seconds * 6);
+ this.minuteHand.rotation = 180 + (minutes * 6);
+
+ // Multiplies by 30 to get basic degrees, and then
+ // adds up to 29.5 degrees (59 * 0.5) to account
+ // for the minutes
+ this.hourHand.rotation = 180 + (hours * 30) + (minutes * 0.5);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/SimpleClock.as b/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/SimpleClock.as
new file mode 100644
index 0000000..95e06c8
--- /dev/null
+++ b/SimpleClock/flex_flashbuilder version/com/example/programmingas3/simpleclock/SimpleClock.as
@@ -0,0 +1,66 @@
+package com.example.programmingas3.clock {
+ import mx.core.UIComponent;
+
+ public class SimpleClock extends UIComponent
+ {
+ import com.example.programmingas3.clock.AnalogClockFace;
+ import flash.events.TimerEvent;
+ import flash.utils.Timer;
+
+ /**
+ * The time display component.
+ */
+ public var face:AnalogClockFace;
+
+ /**
+ * The Timer that acts like a heartbeat for the application.
+ */
+ public var ticker:Timer;
+
+ public static const millisecondsPerMinute:int = 1000 * 60;
+ public static const millisecondsPerHour:int = 1000 * 60 * 60;
+ public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+
+ /**
+ * Sets up a SimpleClock instance.
+ */
+ public function initClock(faceSize:Number = 200):void
+ {
+ // sets the invoice date to today’s date
+ var invoiceDate:Date = new Date();
+
+ // adds 30 days to get the due date
+ var millisecondsPerDay:int = 1000 * 60 * 60 * 24;
+ var dueDate:Date = new Date(invoiceDate.getTime() + (30 * millisecondsPerDay));
+
+ var oneHourFromNow:Date = new Date(); // starts at the current time
+ oneHourFromNow.setTime(oneHourFromNow.getTime() + millisecondsPerHour);
+
+ // 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();
+
+ // 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();
+ }
+
+ /**
+ * Called once per second when the Timer event is received.
+ */
+ public function onTick(evt:TimerEvent):void
+ {
+ // Updates the clock display.
+ face.draw();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpinningMoon/SpinningMoon.fla b/SpinningMoon/SpinningMoon.fla
new file mode 100644
index 0000000..7f49479
Binary files /dev/null and b/SpinningMoon/SpinningMoon.fla differ
diff --git a/SpinningMoon/SpinningMoon.mxml b/SpinningMoon/SpinningMoon.mxml
new file mode 100644
index 0000000..b125377
--- /dev/null
+++ b/SpinningMoon/SpinningMoon.mxml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/SpinningMoon/com/example/programmingas3/moon/MoonSphere.as b/SpinningMoon/com/example/programmingas3/moon/MoonSphere.as
new file mode 100644
index 0000000..a0c8f6a
--- /dev/null
+++ b/SpinningMoon/com/example/programmingas3/moon/MoonSphere.as
@@ -0,0 +1,154 @@
+package com.example.programmingas3.moon
+{
+ import flash.display.Bitmap;
+ import flash.display.BitmapData;
+ import flash.display.BitmapDataChannel;
+ import flash.display.Loader;
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.TimerEvent;
+ import flash.filters.BitmapFilterQuality;
+ import flash.filters.DisplacementMapFilter;
+ import flash.filters.GlowFilter;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+ import flash.net.URLRequest;
+ import flash.utils.Timer;
+
+ public class MoonSphere extends Sprite
+ {
+ // The Bitmap containing the moon map that's actually displayed on the screen.
+ private var sphere:Bitmap;
+
+ // The moon map "source" -- pixels from this map are copied onto sphere
+ // to create the animated motion of the moon.
+ private var textureMap:BitmapData;
+
+ // The radius of the moon.
+ private var radius:int;
+
+ // The current x position on textureMap from which the pixels are copied onto sphere.
+ private var sourceX:int = 0;
+
+ // MoonSphere constructor
+ // Starts loading the moon image.
+ public function MoonSphere()
+ {
+ var imageLoader:Loader = new Loader();
+ imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadComplete);
+ imageLoader.load(new URLRequest("moonMap.png"));
+ }
+
+ // Movement (a'la rotation) routine
+ private function rotateMoon(event:TimerEvent):void
+ {
+ sourceX += 1;
+ if (sourceX >= textureMap.width / 2)
+ {
+ sourceX = 0;
+ }
+
+ sphere.bitmapData.copyPixels(textureMap,
+ new Rectangle(sourceX, 0, sphere.width, sphere.height),
+ new Point(0, 0));
+
+ event.updateAfterEvent();
+ }
+
+ // Creates the displacement map image that's used to create the fisheye lens effect
+ private function createFisheyeMap(radius:int):BitmapData
+ {
+ var diameter:int = 2 * radius;
+
+ var result:BitmapData = new BitmapData(diameter,
+ diameter,
+ false,
+ 0x808080);
+
+ // Loop through the pixels in the image one by one
+ for (var i:int = 0; i < diameter; i++)
+ {
+ for (var j:int = 0; j < diameter; j++)
+ {
+ // Calculate the x and y distances of this pixel from
+ // the center of the circle (as a percentage of the radius).
+ var pctX:Number = (i - radius) / radius;
+ var pctY:Number = (j - radius) / radius;
+
+ // Calculate the linear distance of this pixel from
+ // the center of the circle (as a percentage of the radius).
+ var pctDistance:Number = Math.sqrt(pctX * pctX + pctY * pctY);
+
+ // If the current pixel is inside the circle,
+ // set its color.
+ if (pctDistance < 1)
+ {
+ // Calculate the appropriate color depending on the
+ // distance of this pixel from the center of the circle.
+ var red:int;
+ var green:int;
+ var blue:int;
+ var rgb:uint;
+ red = 128 * (1 + 0.75 * pctX * pctX * pctX / (1 - pctY * pctY));
+ green = 0;
+ blue = 0;
+ rgb = (red << 16 | green << 8 | blue);
+ // Set the pixel to the calculated color.
+ result.setPixel(i, j, rgb);
+ }
+ }
+ }
+ return result;
+ }
+
+ // Called when the moon map image finishes loading.
+ // Sets up the on-screen elements (moon image with fisheye filter and its mask);
+ // starts the Timer that creates the animation effect.
+ private function imageLoadComplete(event:Event):void
+ {
+ textureMap = event.target.content.bitmapData;
+ radius = textureMap.height / 2;
+
+ sphere = new Bitmap();
+ sphere.bitmapData = new BitmapData(textureMap.width / 2, textureMap.height);
+ sphere.bitmapData.copyPixels(textureMap,
+ new Rectangle(0, 0, sphere.width, sphere.height),
+ new Point(0, 0));
+
+ // Create the BitmapData instance that's used as the displacement map image
+ // to create the fisheye lens effect.
+ var fisheyeLens:BitmapData = createFisheyeMap(radius);
+
+ // Create the fisheye filter
+ var displaceFilter:DisplacementMapFilter;
+ displaceFilter = new DisplacementMapFilter(fisheyeLens,
+ new Point(radius, 0),
+ BitmapDataChannel.RED,
+ BitmapDataChannel.BLUE,
+ radius, 0);
+
+ // Apply the filter
+ sphere.filters = [displaceFilter];
+
+ this.addChild(sphere);
+
+ // Create and apply the image mask
+ var moonMask:Shape = new Shape();
+ moonMask.graphics.beginFill(0);
+ moonMask.graphics.drawCircle(radius * 2, radius, radius);
+ this.addChild(moonMask);
+ this.mask = moonMask;
+
+ // Set up the timer to start the animation that 'spins' the moon
+ var rotationTimer:Timer = new Timer(15);
+ rotationTimer.addEventListener(TimerEvent.TIMER, rotateMoon);
+ rotationTimer.start();
+
+ // add a slight atmospheric glow effect
+ this.filters = [new GlowFilter(0xC2C2C2, .75, 20, 20, 2, BitmapFilterQuality.HIGH, true)];
+
+ dispatchEvent(event);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpinningMoon/moonMap.png b/SpinningMoon/moonMap.png
new file mode 100644
index 0000000..ac30fd0
Binary files /dev/null and b/SpinningMoon/moonMap.png differ
diff --git a/SpriteArranger/flash pro version/SpriteArranger.fla b/SpriteArranger/flash pro version/SpriteArranger.fla
new file mode 100644
index 0000000..0deb168
Binary files /dev/null and b/SpriteArranger/flash pro version/SpriteArranger.fla differ
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Circle.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Circle.as
new file mode 100644
index 0000000..9dc4294
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Circle.as
@@ -0,0 +1,33 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class Circle implements IGeometricShape
+ {
+ public var diameter:Number;
+
+ public function Circle(diam:Number = 100):void
+ {
+ this.diameter = diam;
+ }
+
+ public function getArea():Number
+ {
+ // the formula is Pi * radius^2
+ return Math.PI * ((diameter / 2)^2);
+ }
+
+ public function getCircumference():Number
+ {
+ // the formula is Pi * radius * 2
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
new file mode 100644
index 0000000..207ec02
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class EquilateralTriangle extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from 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;
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
new file mode 100644
index 0000000..9b3f3c8
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
@@ -0,0 +1,29 @@
+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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IGeometricShape.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IGeometricShape.as
new file mode 100644
index 0000000..56605be
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IGeometricShape.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IGeometricShape
+ {
+ function getArea():Number;
+ function describe():String;
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IPolygon.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IPolygon.as
new file mode 100644
index 0000000..2aef5af
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/IPolygon.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IPolygon extends IGeometricShape
+ {
+ function getPerimeter():Number;
+ function getSumOfAngles():Number;
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/RegularPolygon.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/RegularPolygon.as
new file mode 100644
index 0000000..0f8b920
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/RegularPolygon.as
@@ -0,0 +1,51 @@
+package com.example.programmingas3.geometricshapes
+{
+ /**
+ * A regular polygon is equilateral (all sides are the same length)
+ * and equiangular (all interior angles are the same).
+ */
+ public class RegularPolygon implements IPolygon
+ {
+ public var numSides:int;
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Square.as b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Square.as
new file mode 100644
index 0000000..66a4470
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/geometricshapes/Square.as
@@ -0,0 +1,30 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class Square extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from RegularPolygon
+
+ public function Square(len:Number = 100):void
+ {
+ super(len, 4);
+ }
+
+ public override function getArea():Number
+ {
+ return (this.sideLength * this.sideLength);
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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 a Square.\n";
+ desc += super.describe();
+ return desc;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/CircleSprite.as b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/CircleSprite.as
new file mode 100644
index 0000000..73c7dfd
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/CircleSprite.as
@@ -0,0 +1,28 @@
+package com.example.programmingas3.SpriteArranger
+{
+ import com.example.programmingas3.geometricshapes.Circle;
+
+ public class CircleSprite extends GeometricSprite
+ {
+ public function CircleSprite(diam:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xEECCCC)
+ {
+ super(diam, lColor, fColor);
+
+ this.shapeType = "Circle";
+ this.geometricShape = new Circle(diam);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/DrawingCanvas.as b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/DrawingCanvas.as
new file mode 100644
index 0000000..4d69ce1
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/DrawingCanvas.as
@@ -0,0 +1,131 @@
+package com.example.programmingas3.SpriteArranger
+{
+ import flash.display.Sprite;
+ import flash.display.DisplayObject;
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+
+ public class DrawingCanvas extends Sprite
+ {
+ public var bounds:Rectangle;
+ public var lineColor:Number;
+ public var fillColor:Number;
+
+ public function DrawingCanvas(w:Number = 500, h:Number = 200, fillColor:Number = 0xFFFFFF, lineColor:Number = 0x000000)
+ {
+ super();
+ this.bounds = new Rectangle(0, 0, w, h);
+
+ this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ }
+
+ public function initCanvas(fillColor:Number = 0xFFFFFF, lineColor:Number = 0x000000):void
+ {
+ this.lineColor = lineColor;
+ this.fillColor = fillColor;
+ drawBounds();
+ }
+
+ public function drawBounds():void
+ {
+ var w:Number = 500;
+ var h:Number = 200;
+
+ this.graphics.clear();
+
+ this.graphics.lineStyle(1.0, this.lineColor, 1.0);
+ this.graphics.beginFill(this.fillColor, 1.0);
+ this.graphics.drawRect(-1, 1, w + 2, h + 2);
+ this.graphics.endFill();
+ }
+
+ 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;
+ }
+ // makes the shapes slightly transparent, so you can see what's behind them
+ newShape.alpha = 0.8;
+
+ this.addChild(newShape);
+ }
+
+ public function describeChildren():String
+ {
+ var desc:String = "";
+ var child:DisplayObject;
+ for (var i:int=0; i < this.numChildren; i++)
+ {
+ child = this.getChildAt(i);
+ desc += i + ": " + child + '\n';
+ }
+ return desc;
+ }
+
+ public function moveToBack(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index > 0)
+ {
+ this.setChildIndex(shape, 0);
+ }
+ }
+
+ public function moveDown(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index > 0)
+ {
+ this.setChildIndex(shape, index - 1);
+ }
+ }
+
+ public function moveToFront(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index != -1 && index < (this.numChildren - 1))
+ {
+ this.setChildIndex(shape, this.numChildren - 1);
+ }
+ }
+
+ public function moveUp(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index != -1 && index < (this.numChildren - 1))
+ {
+ this.setChildIndex(shape, index + 1);
+ }
+ }
+
+ /**
+ * Traps all mouseUp events and sends them to the selected shape.
+ * Useful when you release the mouse while the selected shape is
+ * underneath another one (which prevents the selected shape from
+ * receiving the mouseUp event).
+ */
+ public function onMouseUp(evt:MouseEvent):void
+ {
+ var selectedSprite:GeometricSprite = GeometricSprite.selectedSprite;
+ if (selectedSprite != null && selectedSprite.isSelected())
+ {
+ selectedSprite.onMouseUp(evt);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/GeometricSprite.as b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/GeometricSprite.as
new file mode 100644
index 0000000..5616878
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/GeometricSprite.as
@@ -0,0 +1,113 @@
+package com.example.programmingas3.SpriteArranger
+{
+ import com.example.programmingas3.geometricshapes.IGeometricShape;
+
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.events.MouseEvent;
+ import flash.geom.Rectangle;
+
+ public class GeometricSprite extends Sprite
+ {
+ public var size:Number;
+ public var lineColor:Number = 0x000000;
+ public var fillColor:Number = 0xDDDDEE;
+
+ public var shapeType:String = "GeometricSprite";
+
+ /**
+ * An instance of a purely geometric shape, that is, one that defines
+ * a shape mathematically but not visually.
+ */
+ public var geometricShape:IGeometricShape;
+
+ /**
+ * Keeps track of the currently selected shape.
+ * This is a static property, so there can only be one GeometricSprite
+ * selected at any given time.
+ */
+ public static var selectedSprite:GeometricSprite;
+
+ /**
+ * Holds a border rectangle that is shown when this GeometricSprite instance is selected.
+ */
+ public var selectionIndicator:Shape;
+
+ public function GeometricSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xDDDDEE)
+ {
+ this.size = size;
+ this.lineColor = lColor;
+ this.fillColor = fColor;
+ this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ }
+
+ public function drawShape():void
+ {
+ // to be overridden in subclasses
+ }
+
+ private function onMouseDown(evt:MouseEvent):void
+ {
+ this.showSelected();
+
+ // limits dragging to the area inside the canvas
+ var boundsRect:Rectangle = this.parent.getRect(this.parent);
+ boundsRect.width -= this.size;
+ boundsRect.height -= this.size;
+ this.startDrag(false, boundsRect);
+ }
+
+ public function onMouseUp(evt:MouseEvent):void
+ {
+ this.stopDrag();
+ }
+
+ private function showSelected():void
+ {
+ if (this.selectionIndicator == null)
+ {
+ // draws a red rectangle around the selected shape
+ 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);
+ }
+ else
+ {
+ this.selectionIndicator.visible = true;
+ }
+
+ if (GeometricSprite.selectedSprite != this)
+ {
+ if (GeometricSprite.selectedSprite != null)
+ {
+ GeometricSprite.selectedSprite.hideSelected();
+ }
+ GeometricSprite.selectedSprite = this;
+ }
+ }
+
+ private function hideSelected():void
+ {
+ if (this.selectionIndicator != null)
+ {
+ this.selectionIndicator.visible = false;
+ }
+ }
+
+ /**
+ * Returns true if this shape's selection rectangle is currently showing.
+ */
+ public function isSelected():Boolean
+ {
+ return !(this.selectionIndicator == null || this.selectionIndicator.visible == false);
+ }
+
+
+ public override function toString():String
+ {
+ return this.shapeType + " of size " + this.size + " at " + this.x + ", " + this.y;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/SquareSprite.as b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/SquareSprite.as
new file mode 100644
index 0000000..c8f4e2a
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/SquareSprite.as
@@ -0,0 +1,27 @@
+package com.example.programmingas3.SpriteArranger
+{
+ import com.example.programmingas3.geometricshapes.Square;
+ import flash.events.MouseEvent;
+
+ public class SquareSprite extends GeometricSprite
+ {
+ public function SquareSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xCCEECC)
+ {
+ super(size, lColor, fColor);
+ this.shapeType = "Square";
+ this.geometricShape = new Square(size);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ this.graphics.clear();
+
+ this.graphics.lineStyle(1.0, this.lineColor, 1.0);
+ this.graphics.beginFill(this.fillColor, 1.0);
+
+ this.graphics.drawRect(0, 0, this.size, this.size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/TriangleSprite.as b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/TriangleSprite.as
new file mode 100644
index 0000000..f767278
--- /dev/null
+++ b/SpriteArranger/flash pro version/com/example/programmingas3/spritearranger/TriangleSprite.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.SpriteArranger
+{
+ import com.example.programmingas3.geometricshapes.EquilateralTriangle;
+
+ public class TriangleSprite extends GeometricSprite
+ {
+ //public var triangle:EquilateralTriangle;
+
+ public function TriangleSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xCCCCEE)
+ {
+ super(size, lColor, fColor);
+ this.shapeType = "Triangle";
+ this.geometricShape = new EquilateralTriangle(size);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ this.graphics.clear();
+
+ this.graphics.lineStyle(1.0, this.lineColor, 1.0);
+ this.graphics.beginFill(this.fillColor, 1.0);
+
+ this.graphics.moveTo(0, this.size);
+ this.graphics.lineTo((this.size / 2), 0);
+ this.graphics.lineTo(this.size, this.size);
+ this.graphics.lineTo(0, this.size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/SpriteArranger.mxml b/SpriteArranger/flex_flashbuilder version/SpriteArranger.mxml
new file mode 100644
index 0000000..2d6e8c9
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/SpriteArranger.mxml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Circle
+ Triangle
+ Square
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Circle.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Circle.as
new file mode 100644
index 0000000..9dc4294
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Circle.as
@@ -0,0 +1,33 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class Circle implements IGeometricShape
+ {
+ public var diameter:Number;
+
+ public function Circle(diam:Number = 100):void
+ {
+ this.diameter = diam;
+ }
+
+ public function getArea():Number
+ {
+ // the formula is Pi * radius^2
+ return Math.PI * ((diameter / 2)^2);
+ }
+
+ public function getCircumference():Number
+ {
+ // the formula is Pi * radius * 2
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
new file mode 100644
index 0000000..207ec02
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/EquilateralTriangle.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class EquilateralTriangle extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from 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;
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
new file mode 100644
index 0000000..9b3f3c8
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/GeometricShapeFactory.as
@@ -0,0 +1,29 @@
+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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IGeometricShape.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IGeometricShape.as
new file mode 100644
index 0000000..56605be
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IGeometricShape.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IGeometricShape
+ {
+ function getArea():Number;
+ function describe():String;
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IPolygon.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IPolygon.as
new file mode 100644
index 0000000..2aef5af
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/IPolygon.as
@@ -0,0 +1,8 @@
+package com.example.programmingas3.geometricshapes
+{
+ public interface IPolygon extends IGeometricShape
+ {
+ function getPerimeter():Number;
+ function getSumOfAngles():Number;
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/RegularPolygon.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/RegularPolygon.as
new file mode 100644
index 0000000..0f8b920
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/RegularPolygon.as
@@ -0,0 +1,51 @@
+package com.example.programmingas3.geometricshapes
+{
+ /**
+ * A regular polygon is equilateral (all sides are the same length)
+ * and equiangular (all interior angles are the same).
+ */
+ public class RegularPolygon implements IPolygon
+ {
+ public var numSides:int;
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Square.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Square.as
new file mode 100644
index 0000000..66a4470
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/geometricshapes/Square.as
@@ -0,0 +1,30 @@
+package com.example.programmingas3.geometricshapes
+{
+ public class Square extends RegularPolygon
+ {
+ // Inherits the numSides and sideLength properties from RegularPolygon
+
+ public function Square(len:Number = 100):void
+ {
+ super(len, 4);
+ }
+
+ public override function getArea():Number
+ {
+ return (this.sideLength * this.sideLength);
+ }
+
+ // Inherits the getPerimeter() method from RegularPolygon
+
+ // Inherits the getSumOfAngles() method from RegularPolygon
+
+ 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 a Square.\n";
+ desc += super.describe();
+ return desc;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/CircleSprite.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/CircleSprite.as
new file mode 100644
index 0000000..ca7c110
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/CircleSprite.as
@@ -0,0 +1,28 @@
+package com.example.programmingas3.spritearranger
+{
+ import com.example.programmingas3.geometricshapes.Circle;
+
+ public class CircleSprite extends GeometricSprite
+ {
+ public function CircleSprite(diam:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xEECCCC)
+ {
+ super(diam, lColor, fColor);
+
+ this.shapeType = "Circle";
+ this.geometricShape = new Circle(diam);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/DrawingCanvas.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/DrawingCanvas.as
new file mode 100644
index 0000000..8d6f018
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/DrawingCanvas.as
@@ -0,0 +1,131 @@
+package com.example.programmingas3.spritearranger
+{
+ import flash.display.DisplayObject;
+ import flash.events.MouseEvent;
+ import flash.geom.Point;
+ import flash.geom.Rectangle;
+
+ import mx.core.UIComponent;
+
+ public class DrawingCanvas extends UIComponent
+ {
+ public var bounds:Rectangle;
+ public var lineColor:Number;
+ public var fillColor:Number;
+
+ public function DrawingCanvas(w:Number = 500, h:Number = 200, fillColor:Number = 0xFFFFFF, lineColor:Number = 0x000000)
+ {
+ super();
+ this.bounds = new Rectangle(0, 0, w, h);
+
+ this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
+ }
+
+ public function initCanvas(fillColor:Number = 0xFFFFFF, lineColor:Number = 0x000000):void
+ {
+ this.lineColor = lineColor;
+ this.fillColor = fillColor;
+ this.width=500;
+ this.height=200;
+ drawBounds();
+ }
+
+ public function drawBounds():void
+ {
+ 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();
+ }
+
+ 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;
+ }
+ // makes the shapes slightly transparent, so you can see what's behind them
+ newShape.alpha = 0.8;
+
+ this.addChild(newShape);
+ }
+
+ public function describeChildren():String
+ {
+ var desc:String = "";
+ var child:DisplayObject;
+ for (var i:int=0; i < this.numChildren; i++)
+ {
+ child = this.getChildAt(i);
+ desc += i + ": " + child + '\n';
+ }
+ return desc;
+ }
+
+ public function moveToBack(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index > 0)
+ {
+ this.setChildIndex(shape, 0);
+ }
+ }
+
+ public function moveDown(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index > 0)
+ {
+ this.setChildIndex(shape, index - 1);
+ }
+ }
+
+ public function moveToFront(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index != -1 && index < (this.numChildren - 1))
+ {
+ this.setChildIndex(shape, this.numChildren - 1);
+ }
+ }
+
+ public function moveUp(shape:GeometricSprite):void
+ {
+ var index:int = this.getChildIndex(shape);
+ if (index != -1 && index < (this.numChildren - 1))
+ {
+ this.setChildIndex(shape, index + 1);
+ }
+ }
+
+ /**
+ * Traps all mouseUp events and sends them to the selected shape.
+ * Useful when you release the mouse while the selected shape is
+ * underneath another one (which prevents the selected shape from
+ * receiving the mouseUp event).
+ */
+ public function onMouseUp(evt:MouseEvent):void
+ {
+ var selectedSprite:GeometricSprite = GeometricSprite.selectedSprite;
+ if (selectedSprite != null && selectedSprite.isSelected())
+ {
+ selectedSprite.onMouseUp(evt);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/GeometricSprite.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/GeometricSprite.as
new file mode 100644
index 0000000..fe0d67b
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/GeometricSprite.as
@@ -0,0 +1,112 @@
+package com.example.programmingas3.spritearranger
+{
+ import com.example.programmingas3.geometricshapes.IGeometricShape;
+
+ import flash.display.Shape;
+ import flash.display.Sprite;
+ import flash.events.MouseEvent;
+ import flash.geom.Rectangle;
+
+ public class GeometricSprite extends Sprite
+ {
+ public var size:Number;
+ public var lineColor:Number = 0x000000;
+ public var fillColor:Number = 0xDDDDEE;
+
+ public var shapeType:String = "GeometricSprite";
+
+ /**
+ * An instance of a purely geometric shape, that is, one that defines
+ * a shape mathematically but not visually.
+ */
+ public var geometricShape:IGeometricShape;
+
+ /**
+ * Keeps track of the currently selected shape.
+ * This is a static property, so there can only be one GeometricSprite
+ * selected at any given time.
+ */
+ public static var selectedSprite:GeometricSprite;
+
+ /**
+ * Holds a border rectangle that is shown when this GeometricSprite instance is selected.
+ */
+ public var selectionIndicator:Shape;
+
+ public function GeometricSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xDDDDEE)
+ {
+ this.size = size;
+ this.lineColor = lColor;
+ this.fillColor = fColor;
+ this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
+ }
+
+ public function drawShape():void
+ {
+ // to be overridden in subclasses
+ }
+
+ private function onMouseDown(evt:MouseEvent):void
+ {
+ this.showSelected();
+
+ // limits dragging to the area inside the canvas
+ var boundsRect:Rectangle = this.parent.getRect(this.parent);
+ boundsRect.width -= this.size;
+ boundsRect.height -= this.size;
+ this.startDrag(false, boundsRect);
+ }
+
+ public function onMouseUp(evt:MouseEvent):void
+ {
+ this.stopDrag();
+ }
+
+ private function showSelected():void
+ {
+ if (this.selectionIndicator == null)
+ {
+ // draws a red rectangle around the selected shape
+ 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);
+ }
+ else
+ {
+ this.selectionIndicator.visible = true;
+ }
+
+ if (GeometricSprite.selectedSprite != this)
+ {
+ if (GeometricSprite.selectedSprite != null)
+ {
+ GeometricSprite.selectedSprite.hideSelected();
+ }
+ GeometricSprite.selectedSprite = this;
+ }
+ }
+
+ private function hideSelected():void
+ {
+ if (this.selectionIndicator != null)
+ {
+ this.selectionIndicator.visible = false;
+ }
+ }
+
+ /**
+ * Returns true if this shape's selection rectangle is currently showing.
+ */
+ public function isSelected():Boolean
+ {
+ return !(this.selectionIndicator == null || this.selectionIndicator.visible == false);
+ }
+
+
+ public override function toString():String
+ {
+ return this.shapeType + " of size " + this.size + " at " + this.x + ", " + this.y;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/SquareSprite.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/SquareSprite.as
new file mode 100644
index 0000000..30224f6
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/SquareSprite.as
@@ -0,0 +1,27 @@
+package com.example.programmingas3.spritearranger
+{
+ import com.example.programmingas3.geometricshapes.Square;
+ import flash.events.MouseEvent;
+
+ public class SquareSprite extends GeometricSprite
+ {
+ public function SquareSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xCCEECC)
+ {
+ super(size, lColor, fColor);
+ this.shapeType = "Square";
+ this.geometricShape = new Square(size);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ this.graphics.clear();
+
+ this.graphics.lineStyle(1.0, this.lineColor, 1.0);
+ this.graphics.beginFill(this.fillColor, 1.0);
+
+ this.graphics.drawRect(0, 0, this.size, this.size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/TriangleSprite.as b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/TriangleSprite.as
new file mode 100644
index 0000000..cef4f6a
--- /dev/null
+++ b/SpriteArranger/flex_flashbuilder version/com/example/programmingas3/spritearranger/TriangleSprite.as
@@ -0,0 +1,31 @@
+package com.example.programmingas3.spritearranger
+{
+ import com.example.programmingas3.geometricshapes.EquilateralTriangle;
+
+ public class TriangleSprite extends GeometricSprite
+ {
+ //public var triangle:EquilateralTriangle;
+
+ public function TriangleSprite(size:Number = 100, lColor:Number = 0x000000, fColor:Number = 0xCCCCEE)
+ {
+ super(size, lColor, fColor);
+ this.shapeType = "Triangle";
+ this.geometricShape = new EquilateralTriangle(size);
+
+ drawShape();
+ }
+
+ public override function drawShape():void
+ {
+ this.graphics.clear();
+
+ this.graphics.lineStyle(1.0, this.lineColor, 1.0);
+ this.graphics.beginFill(this.fillColor, 1.0);
+
+ this.graphics.moveTo(0, this.size);
+ this.graphics.lineTo((this.size / 2), 0);
+ this.graphics.lineTo(this.size, this.size);
+ this.graphics.lineTo(0, this.size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TLFNewsLayout/TLFNewsFlashCS5.fla b/TLFNewsLayout/TLFNewsFlashCS5.fla
new file mode 100644
index 0000000..5880f4a
Binary files /dev/null and b/TLFNewsLayout/TLFNewsFlashCS5.fla differ
diff --git a/TLFNewsLayout/TLFNewsLayout.as b/TLFNewsLayout/TLFNewsLayout.as
new file mode 100644
index 0000000..578dc0d
--- /dev/null
+++ b/TLFNewsLayout/TLFNewsLayout.as
@@ -0,0 +1,111 @@
+package
+{
+ import flash.display.Sprite;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.events.Event;
+ import flash.geom.Rectangle;
+
+ import flashx.textLayout.compose.StandardFlowComposer;
+ import flashx.textLayout.container.ContainerController;
+ import flashx.textLayout.container.ScrollPolicy;
+ import flashx.textLayout.conversion.TextConverter;
+ import flashx.textLayout.elements.TextFlow;
+ import flashx.textLayout.formats.TextLayoutFormat;
+
+ public class TLFNewsLayout extends Sprite
+ {
+ private var hTextFlow:TextFlow;
+ private var headContainer:Sprite;
+ private var headlineController:ContainerController;
+ private var hContainerFormat:TextLayoutFormat;
+
+ private var bTextFlow:TextFlow;
+ private var bodyTextContainer:Sprite;
+ private var bodyController:ContainerController;
+ private var bodyTextContainerFormat:TextLayoutFormat;
+
+ private const headlineMarkup:String = "TLF News Layout ExampleThis example formats text like a newspaper page with a headline, a subtitle, and multiple columns";
+
+ private const bodyMarkup:String = "There are many such lime-kilns in that tract of country, for the purpose of burning the white marble which composes a large part of the substance of the hills. Some of them, built years ago, and long deserted, with weeds growing in the vacant round of the interior, which is open to the sky, and grass and wild-flowers rooting themselves into the chinks of the stones, look already like relics of antiquity, and may yet be overspread with the lichens of centuries to come. Others, where the lime-burner still feeds his daily and nightlong fire, afford points of interest to the wanderer among the hills, who seats himself on a log of wood or a fragment of marble, to hold a chat with the solitary man. It is a lonesome, and, when the character is inclined to thought, may be an intensely thoughtful occupation; as it proved in the case of Ethan Brand, who had mused to such strange purpose, in days gone by, while the fire in this very kiln was burning.The man who now watched the fire was of a different order, and troubled himself with no thoughts save the very few that were requisite to his business. At frequent intervals, he flung back the clashing weight of the iron door, and, turning his face from the insufferable glare, thrust in huge logs of oak, or stirred the immense brands with a long pole. Within the furnace were seen the curling and riotous flames, and the burning marble, almost molten with the intensity of heat; while without, the reflection of the fire quivered on the dark intricacy of the surrounding forest, and showed in the foreground a bright and ruddy little picture of the hut, the spring beside its door, the athletic and coal-begrimed figure of the lime-burner, and the half-frightened child, shrinking into the protection of his father's shadow. And when again the iron door was closed, then reappeared the tender light of the half-full moon, which vainly strove to trace out the indistinct shapes of the neighboring mountains; and, in the upper sky, there was a flitting congregation of clouds, still faintly tinged with the rosy sunset, though thus far down into the valley the sunshine had vanished long and long ago.";
+
+ public function TLFNewsLayout()
+ {
+ //wait for stage to exist
+ addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
+ }
+
+ private function onAddedToStage(evtObj:Event):void
+ {
+ removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
+ stage.scaleMode = StageScaleMode.NO_SCALE;
+ stage.align = StageAlign.TOP_LEFT;
+
+ // Headline text flow and flow composer
+ hTextFlow = TextConverter.importToFlow(headlineMarkup, TextConverter.TEXT_LAYOUT_FORMAT);
+ hTextFlow.flowComposer = new StandardFlowComposer();
+
+ // initialize the headline container and controller objects
+ headContainer = new Sprite();
+ headlineController = new ContainerController(headContainer);
+ headlineController.verticalScrollPolicy = ScrollPolicy.OFF;
+ hContainerFormat = new TextLayoutFormat();
+ hContainerFormat.paddingTop = 4;
+ hContainerFormat.paddingRight = 4;
+ hContainerFormat.paddingBottom = 4;
+ hContainerFormat.paddingLeft = 4;
+
+ headlineController.format = hContainerFormat;
+ hTextFlow.flowComposer.addController(headlineController);
+ addChild(headContainer);
+ stage.addEventListener(flash.events.Event.RESIZE, resizeHandler);
+
+ // Body text TextFlow and flow composer
+ bTextFlow = TextConverter.importToFlow(bodyMarkup, TextConverter.TEXT_LAYOUT_FORMAT);
+ bTextFlow.flowComposer = new StandardFlowComposer();
+
+ // The body text container is below, and has three columns
+ bodyTextContainer = new Sprite();
+ bodyController = new ContainerController(bodyTextContainer);
+ bodyTextContainerFormat = new TextLayoutFormat();
+ bodyTextContainerFormat.columnCount = 3;
+ bodyTextContainerFormat.columnGap = 30;
+
+ bodyController.format = bodyTextContainerFormat;
+ bTextFlow.flowComposer.addController(bodyController);
+ addChild(bodyTextContainer);
+ resizeHandler(null);
+ }
+
+ private function resizeHandler(event:Event):void
+ {
+ const verticalGap:Number = 25;
+ const stagePadding:Number = 16;
+ var stageWidth:Number = stage.stageWidth - stagePadding;
+ var stageHeight:Number = stage.stageHeight - stagePadding;
+ var headlineWidth:Number = stageWidth;
+ var headlineContainerHeight:Number = stageHeight;
+
+ // Initial compose to get height of headline after resize
+ headlineController.setCompositionSize(headlineWidth, headlineContainerHeight);
+ hTextFlow.flowComposer.compose();
+ var rect:Rectangle = headlineController.getContentBounds();
+ headlineContainerHeight = rect.height;
+
+ // Resize and place headline text container
+ // Call setCompositionSize() again with updated headline height
+ headlineController.setCompositionSize(headlineWidth, headlineContainerHeight );
+ headlineController.container.x = stagePadding / 2;
+ headlineController.container.y = stagePadding / 2;
+ hTextFlow.flowComposer.updateAllControllers();
+
+ // Resize and place body text container
+ var bodyContainerHeight:Number = (stageHeight - verticalGap - headlineContainerHeight);
+ bodyController.format = bodyTextContainerFormat;
+ bodyController.setCompositionSize(stageWidth, bodyContainerHeight );
+ bodyController.container.x = (stagePadding/2);
+ bodyController.container.y = (stagePadding/2) + headlineContainerHeight + verticalGap;
+ bTextFlow.flowComposer.updateAllControllers();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TelnetSocket/flash pro version/TelnetSocket.as b/TelnetSocket/flash pro version/TelnetSocket.as
new file mode 100644
index 0000000..532e3fe
--- /dev/null
+++ b/TelnetSocket/flash pro version/TelnetSocket.as
@@ -0,0 +1,29 @@
+package
+{
+ import flash.display.Sprite;
+ import flash.utils.ByteArray;
+ import flash.events.MouseEvent;
+ import com.example.programmingas3.socket.Telnet;
+
+ public class TelnetSocket extends Sprite
+ {
+ private var telnetClient:Telnet;
+
+ public function TelnetSocket() {
+ setupUI();
+ }
+ private function connect(e:MouseEvent):void {
+ telnetClient = new Telnet(serverName.text, int(portNumber.text), output);
+ }
+ private function sendCommand(e:MouseEvent):void {
+ var ba:ByteArray = new ByteArray();
+ ba.writeMultiByte(command.text + "\n", "UTF-8");
+ telnetClient.writeBytesToSocket(ba);
+ command.text = "";
+ }
+ private function setupUI():void {
+ loginBtn.addEventListener(MouseEvent.CLICK,connect)
+ sendBtn.addEventListener(MouseEvent.CLICK,sendCommand);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TelnetSocket/flash pro version/TelnetSocket.fla b/TelnetSocket/flash pro version/TelnetSocket.fla
new file mode 100644
index 0000000..981355e
Binary files /dev/null and b/TelnetSocket/flash pro version/TelnetSocket.fla differ
diff --git a/TelnetSocket/flash pro version/com/example/programmingas3/socket/Telnet.as b/TelnetSocket/flash pro version/com/example/programmingas3/socket/Telnet.as
new file mode 100644
index 0000000..4f6b2e1
--- /dev/null
+++ b/TelnetSocket/flash pro version/com/example/programmingas3/socket/Telnet.as
@@ -0,0 +1,159 @@
+package com.example.programmingas3.socket {
+ import flash.events.*;
+ import flash.net.Socket;
+ import flash.system.Security;
+ import flash.utils.ByteArray;
+ import flash.utils.setTimeout;
+
+ import fl.controls.TextArea;
+ import fl.core.UIComponent;
+
+ public class Telnet {
+ private const CR:int = 13; // Carriage Return (CR)
+ private const WILL:int = 0xFB; // 251 - WILL (option code)
+ private const WONT:int = 0xFC; // 252 - WON'T (option code)
+ private const DO:int = 0xFD; // 253 - DO (option code)
+ private const DONT:int = 0xFE; // 254 - DON'T (option code)
+ private const IAC:int = 0xFF; // 255 - Interpret as Command (IAC)
+
+ private var serverURL:String;
+ private var portNumber:int;
+ private var socket:Socket;
+ private var ta:TextArea;
+ private var state:int = 0;
+
+ public function Telnet(server:String, port:int, output:TextArea) {
+ // set class variables to the values passed to the constructor.
+ serverURL = server;
+ portNumber = port;
+ ta = output;
+
+ // Create a new Socket object and assign event listeners.
+ socket = new Socket();
+ socket.addEventListener(Event.CONNECT, connectHandler);
+ socket.addEventListener(Event.CLOSE, closeHandler);
+ socket.addEventListener(ErrorEvent.ERROR, errorHandler);
+ socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
+ socket.addEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
+
+ // Load policy file from remote server.
+ Security.loadPolicyFile("http://" + serverURL + "/crossdomain.xml");
+ // Attempt to connect to remote socket server.
+ try {
+ msg("Trying to connect to " + serverURL + ":" + portNumber + "\n");
+ socket.connect(serverURL, portNumber);
+ } catch (error:Error) {
+ /*
+ Unable to connect to remote server, display error
+ message and close connection.
+ */
+ msg(error.message + "\n");
+ socket.close();
+ }
+ }
+
+ /**
+ * This method is called if the socket encounters an ioError event.
+ */
+ public function ioErrorHandler(event:IOErrorEvent):void {
+ msg("Unable to connect: socket error.\n");
+ }
+
+ /**
+ * This method is called by our application and is used to send data
+ * to the server.
+ */
+ public function writeBytesToSocket(ba:ByteArray):void {
+ socket.writeBytes(ba);
+ socket.flush();
+ }
+
+ private function connectHandler(event:Event):void {
+ if (socket.connected) {
+ msg("connected...\n");
+ } else {
+ msg("unable to connect\n");
+ }
+ }
+
+ /**
+ * This method is called when the socket connection is closed by
+ * the server.
+ */
+ private function closeHandler(event:Event):void {
+ msg("closed...\n");
+ }
+
+ /**
+ * This method is called if the socket throws an error.
+ */
+ private function errorHandler(event:ErrorEvent):void {
+ msg(event.text + "\n");
+ }
+
+ /**
+ * This method is called when the socket receives data from the server.
+ */
+ private function dataHandler(event:ProgressEvent):void {
+ var n:int = socket.bytesAvailable;
+ // Loop through each available byte returned from the socket connection.
+ while (--n >= 0) {
+ // Read next available byte.
+ var b:int = socket.readUnsignedByte();
+ switch (state) {
+ case 0:
+ // If the current byte is the "Interpret as Command" code, set the state to 1.
+ if (b == IAC) {
+ state = 1;
+ // Else, if the byte is not a carriage return, display the character using the msg() method.
+ } else if (b != CR) {
+ msg(String.fromCharCode(b));
+ }
+ break;
+ case 1:
+ // If the current byte is the "DO" code, set the state to 2.
+ if (b == DO) {
+ state = 2;
+ } else {
+ state = 0;
+ }
+ break;
+ // Blindly reject the option.
+ case 2:
+ /*
+ Write the "Interpret as Command" code, "WONT" code,
+ and current byte to the socket and send the contents
+ to the server by calling the flush() method.
+ */
+ socket.writeByte(IAC);
+ socket.writeByte(WONT);
+ socket.writeByte(b);
+ socket.flush();
+ state = 0;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Append message to the TextArea component on the display list.
+ * After appending text, call the setScroll() method which controls
+ * the scrolling of the TextArea.
+ */
+ private function msg(value:String):void {
+ ta.text += value;
+ ta.dispatchEvent(new Event(Event.CHANGE));
+ setTimeout(setScroll, 100);
+ }
+
+ /**
+ * Scroll the TextArea component to its maximum vertical scroll
+ * position so that the TextArea always shows the last line returned
+ * from the server.
+ */
+ public function setScroll():void {
+// ta.verticalScrollPosition = ta.maxVerticalScrollPosition;
+ ta.verticalScrollPosition = ta.maxVerticalScrollPosition;
+ }
+ }
+}
diff --git a/TelnetSocket/flex_flashbuilder version/TelnetSocket.mxml b/TelnetSocket/flex_flashbuilder version/TelnetSocket.mxml
new file mode 100644
index 0000000..5e61374
--- /dev/null
+++ b/TelnetSocket/flex_flashbuilder version/TelnetSocket.mxml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TelnetSocket/flex_flashbuilder version/com/example/programmingas3/socket/Telnet.as b/TelnetSocket/flex_flashbuilder version/com/example/programmingas3/socket/Telnet.as
new file mode 100644
index 0000000..7010c7e
--- /dev/null
+++ b/TelnetSocket/flex_flashbuilder version/com/example/programmingas3/socket/Telnet.as
@@ -0,0 +1,158 @@
+package com.example.programmingas3.socket {
+ import flash.events.*;
+ import flash.net.Socket;
+ import flash.system.Security;
+ import flash.utils.ByteArray;
+ import flash.utils.setTimeout;
+
+ import mx.controls.TextArea;
+ import mx.core.UIComponent;
+
+ public class Telnet extends UIComponent{
+ private const CR:int = 13; // Carriage Return (CR)
+ private const WILL:int = 0xFB; // 251 - WILL (option code)
+ private const WONT:int = 0xFC; // 252 - WON'T (option code)
+ private const DO:int = 0xFD; // 253 - DO (option code)
+ private const DONT:int = 0xFE; // 254 - DON'T (option code)
+ private const IAC:int = 0xFF; // 255 - Interpret as Command (IAC)
+
+ private var serverURL:String;
+ private var portNumber:int;
+ private var socket:Socket;
+ private var ta:TextArea;
+ private var state:int = 0;
+
+ public function Telnet(server:String, port:int, output:TextArea) {
+ // set class variables to the values passed to the constructor.
+ serverURL = server;
+ portNumber = port;
+ ta = output;
+
+ // Create a new Socket object and assign event listeners.
+ socket = new Socket();
+ socket.addEventListener(Event.CONNECT, connectHandler);
+ socket.addEventListener(Event.CLOSE, closeHandler);
+ socket.addEventListener(ErrorEvent.ERROR, errorHandler);
+ socket.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
+ socket.addEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
+
+ // Load policy file from remote server.
+ Security.loadPolicyFile("http://" + serverURL + "/crossdomain.xml");
+ // Attempt to connect to remote socket server.
+ try {
+ msg("Trying to connect to " + serverURL + ":" + portNumber + "\n");
+ socket.connect(serverURL, portNumber);
+ } catch (error:Error) {
+ /*
+ Unable to connect to remote server, display error
+ message and close connection.
+ */
+ msg(error.message + "\n");
+ socket.close();
+ }
+ }
+
+ /**
+ * This method is called if the socket encounters an ioError event.
+ */
+ public function ioErrorHandler(event:IOErrorEvent):void {
+ msg("Unable to connect: socket error.\n");
+ }
+
+ /**
+ * This method is called by our application and is used to send data
+ * to the server.
+ */
+ public function writeBytesToSocket(ba:ByteArray):void {
+ socket.writeBytes(ba);
+ socket.flush();
+ }
+
+ private function connectHandler(event:Event):void {
+ if (socket.connected) {
+ msg("connected...\n");
+ } else {
+ msg("unable to connect\n");
+ }
+ }
+
+ /**
+ * This method is called when the socket connection is closed by
+ * the server.
+ */
+ private function closeHandler(event:Event):void {
+ msg("closed...\n");
+ }
+
+ /**
+ * This method is called if the socket throws an error.
+ */
+ private function errorHandler(event:ErrorEvent):void {
+ msg(event.text + "\n");
+ }
+
+ /**
+ * This method is called when the socket receives data from the server.
+ */
+ private function dataHandler(event:ProgressEvent):void {
+ var n:int = socket.bytesAvailable;
+ // Loop through each available byte returned from the socket connection.
+ while (--n >= 0) {
+ // Read next available byte.
+ var b:int = socket.readUnsignedByte();
+ switch (state) {
+ case 0:
+ // If the current byte is the "Interpret as Command" code, set the state to 1.
+ if (b == IAC) {
+ state = 1;
+ // Else, if the byte is not a carriage return, display the character using the msg() method.
+ } else if (b != CR) {
+ msg(String.fromCharCode(b));
+ }
+ break;
+ case 1:
+ // If the current byte is the "DO" code, set the state to 2.
+ if (b == DO) {
+ state = 2;
+ } else {
+ state = 0;
+ }
+ break;
+ // Blindly reject the option.
+ case 2:
+ /*
+ Write the "Interpret as Command" code, "WONT" code,
+ and current byte to the socket and send the contents
+ to the server by calling the flush() method.
+ */
+ socket.writeByte(IAC);
+ socket.writeByte(WONT);
+ socket.writeByte(b);
+ socket.flush();
+ state = 0;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Append message to the TextArea component on the display list.
+ * After appending text, call the setScroll() method which controls
+ * the scrolling of the TextArea.
+ */
+ private function msg(value:String):void {
+ ta.text += value;
+ ta.dispatchEvent(new Event(Event.CHANGE));
+ setTimeout(setScroll, 100);
+ }
+
+ /**
+ * Scroll the TextArea component to its maximum vertical scroll
+ * position so that the TextArea always shows the last line returned
+ * from the server.
+ */
+ public function setScroll():void {
+ ta.verticalScrollPosition = ta.maxVerticalScrollPosition;
+ }
+ }
+}
diff --git a/TouchPointID/TouchPointIDExampleFlashCS5.fla b/TouchPointID/TouchPointIDExampleFlashCS5.fla
new file mode 100644
index 0000000..736c38d
Binary files /dev/null and b/TouchPointID/TouchPointIDExampleFlashCS5.fla differ
diff --git a/UsingRegularExpressions/ExpressionValidator.as b/UsingRegularExpressions/ExpressionValidator.as
new file mode 100644
index 0000000..7b6b261
--- /dev/null
+++ b/UsingRegularExpressions/ExpressionValidator.as
@@ -0,0 +1,292 @@
+// Copyright (c) 2005 Adobe, Inc.
+package
+{
+ import com.example.programmingas3.shared.component.LabeledTextField;
+ import com.example.programmingas3.shared.component.SampleComponent;
+ import com.example.programmingas3.shared.component.SampleContainer;
+ import com.example.programmingas3.shared.component.SampleLabel;
+ import com.example.programmingas3.shared.component.SampleTextField;
+ import com.example.programmingas3.shared.component.SampleTextInput;
+
+ import flash.display.*;
+ import flash.events.*;
+ import flash.text.TextFormat;
+ import flash.util.trace;
+
+ /**
+ * Demonstrates different matching and replacement techniques using
+ * Regular expressions.
+ *
+ * Creates a number of text fields and lets you validate or convert their contents.
+ */
+ public class ExpressionValidator extends SampleContainer
+ {
+ /**
+ * A simple name text field.
+ */
+ public var nameField:LabeledTextField;
+ /**
+ * Used for validating an email format
+ */
+ public var emailField:LabeledTextField;
+ /**
+ * Used for validating a phone number format
+ */
+ public var phoneNumberField:LabeledTextField;
+ /**
+ * Used for converting a list of dollar values into euro values.
+ */
+ public var donationField:LabeledTextField;
+ /**
+ * Used for validating HTML tags.
+ */
+ public var sourceField:LabeledTextField;
+ /**
+ * Displays the formatted HTML being validated.
+ */
+ public var htmlField:SampleHtmlField;
+ /**
+ * The initial HTML text used to populate the sourceField.
+ */
+ public var htmlFieldSrc:String = "Check this for HTML validity.";
+ /**
+ * The button that triggers the validations and conversions.
+ */
+ public var submitButton:SampleTextButton;
+
+ public function ExpressionValidator(w:uint = 500, h:uint = 400, thickness:Number = 0.0, pad:uint = 5)
+ {
+ super(w, h, thickness, pad);
+
+ this.init();
+ }
+
+ public override function init():void
+ {
+ this.backgroundColor = 0xDDDDEE;
+
+ var instructions:SampleLabel = new SampleLabel(400, 20, getTipTextFormat());
+ instructions.text = "Complete each of the following fields, and then click the Submit button.";
+ addComponent(instructions);
+
+ nameField = new LabeledTextField("Name:", "Bob");
+ addComponent(nameField);
+
+ emailField = new LabeledTextField("E-mail Address:", "bob@mail.example.com");
+ addComponent(emailField);
+
+ phoneNumberField = new LabeledTextField("Phone Number:", "(888)555-5271");
+ addComponent(phoneNumberField);
+
+ var phoneTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ phoneTips.x = 120;
+ // lets this label size to fit the text as needed
+ phoneTips.field.autoSize = TextFieldAutoSize.LEFT;
+ phoneTips.text = "Examples: 1-415-555-1212 or 415-555-1212 or (415)555-1212 or 555-1212";
+ addComponent(phoneTips);
+
+ donationField = new LabeledTextField("Donation Amounts:", "$955.95, $1,200, $14.22");
+ addComponent(donationField);
+
+ var donationTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ donationTips.x = 120;
+ // lets this label size to fit the text as needed
+ donationTips.field.autoSize = TextFieldAutoSize.LEFT;
+ donationTips.text = "Donations entered with a leading $ will be converted to Euros.";
+ addComponent(donationTips);
+
+ sourceField = new LabeledTextField("HTML Source:", htmlFieldSrc, 200, 60);
+ sourceField.inputField.field.multiline = true;
+ sourceField.inputField.field.html = false;
+ addComponent(sourceField);
+
+ var htmlTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ htmlTips.x = 120;
+ // lets this label size to fit the text as needed
+ htmlTips.field.autoSize = TextFieldAutoSize.LEFT;
+ htmlTips.text = "Enter HTML text above. The formatted version is displayed below.";
+ addComponent(htmlTips);
+
+ htmlField = new SampleHtmlField(200, 60);
+ htmlField.x = 120;
+ htmlField.selectable = false;
+ htmlField.htmlText = htmlFieldSrc;
+ addComponent(htmlField);
+
+ // When the sourceField loses focus, update the htmlField
+ sourceField.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
+
+ // When you click outside of the sourceField, update the htmlField.
+ // Don't want to do this on the change event, because when you're
+ // in the middle of typing HTML tags you would be passing invalid
+ // HTML to the htmlField with each keystroke.
+ this.addEventListener(MouseEvent.CLICK, onBackgroundClick);
+
+ arrangeTextFields();
+
+ submitButton = new SampleTextButton("Submit");
+ submitButton.y = getChildAt(numChildren - 1).y + getChildAt(numChildren - 1).height + 10;
+ submitButton.x = 120;
+ addChild(submitButton);
+ submitButton.addEventListener(MouseEvent.CLICK, validate);
+
+ draw();
+ }
+
+ /**
+ * Stacks the components vertically, with a gap between them.
+ */
+ private function arrangeTextFields()
+ {
+ var verticalGap:uint = 4;
+ var displayObject:DisplayObject;
+ var prevDisplayObject:DisplayObject;
+
+ for (var i:uint = 1; i < this.canvas.numChildren; i++)
+ {
+ displayObject = this.canvas.getChildAt(i);
+ prevDisplayObject = this.canvas.getChildAt(i - 1);
+
+ if ((displayObject is SampleComponent) || (displayObject is SampleTextButton))
+ {
+ displayObject.y = prevDisplayObject.y + prevDisplayObject.height + verticalGap;
+ trace("new Y=" + displayObject.y);
+ }
+ }
+ }
+
+ /**
+ * Updates the formatted HTML display when the HTML sourceField loses focus.
+ */
+ private function onFocusOut(evt:FocusEvent):void
+ {
+ htmlField.htmlText = sourceField.inputField.text;
+ }
+
+ /**
+ * Updates the formatted HTML display when you click outside the sourceField.
+ */
+ private function onBackgroundClick(evt:MouseEvent):void
+ {
+ //trace("onClick() target=" + evt.target);
+ if (! sourceField.inputField.hitTestPoint(evt.localX, evt.localY, true))
+ {
+ htmlField.htmlText = sourceField.inputField.text;
+ }
+ }
+
+ private function validate(e:Event)
+ {
+ var validNamePattern:RegExp = /^[ a-zA-Z,.]+$/;
+ var validEmailPattern:RegExp = /^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/;
+ var validPhoneNumberPattern:RegExp = /^(((1-)?\d{3}-)|(\(\d{3}\)))?\d{3}-\d{4}$/;
+
+ simpleRegExpTest(nameField, validNamePattern);
+ simpleRegExpTest(emailField, validEmailPattern);
+ simpleRegExpTest(phoneNumberField, validPhoneNumberPattern);
+
+ if (validHTML(sourceField.inputField.text))
+ {
+ sourceField.showWarning(false);
+ }
+ else
+ {
+ trace ("Invalid comments.");
+ sourceField.showWarning();
+ }
+
+ var usdPrice:RegExp = /\$([\d,]+.\d+)+/i;
+ donationField.inputField.text = donationField.inputField.text.replace(usdPrice, usdToEuro);
+ }
+
+ private function simpleRegExpTest(field:LabeledTextField, regExp:RegExp)
+ {
+ if (!field.inputField.text.match(regExp))
+ {
+ field.showWarning(true);
+ }
+ else
+ {
+ field.showWarning(false);
+ }
+
+ }
+ private function validHTML(str:String):Boolean
+ {
+ var validHtmlTagPattern:RegExp = /^([^<]*)<(\w+)(\W.*)*>(.*?)<\/(\2)>(.*)/;
+
+ if (str == null)
+ {
+ return true; // Empty strings are valid HTML
+ }
+ else if ((!str.match(/<\w+.*>/)) && (!str.match(/<\w+.*>/)))
+ {
+ return true; // Strings with no tags are valid HTML.
+ }
+ else
+ {
+ var execArray:Array = validHtmlTagPattern.exec(str);
+ if (execArray == null)
+ {
+ return false;
+ }
+ else if (execArray[2] != execArray[5])
+ {
+ return false;
+ }
+ else if ( validTag(execArray[2]) && validTag(execArray[5]))
+ {
+ return validHTML(execArray[1])
+ && validHTML(execArray[4])
+ && validHTML(execArray[6]);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ private function validTag(tag:String):Boolean
+ {
+ var validTags:Array = ["a", "b", "br", "font", "img", "i", "li", "p", "textformat", "u"];
+ var matching:Boolean = false;
+
+ for (var i:uint = 0; i < validTags.length; i++)
+ {
+ if (tag == validTags[i])
+ {
+ matching = true;
+ }
+ }
+ return matching;
+ }
+
+ /**
+ * Replaces a Dollar value with an equivalent Euro value.
+ */
+ private function usdToEuro(...args):String
+ {
+ var usd:String = args[1];
+ usd = usd.replace(",", "");
+ var exchangeRate:Number = 0.853690;
+ var euroValue:Number = usd * exchangeRate;
+
+ const euroSymbol:String = String.fromCharCode(8364);
+ return euroValue.toFixed(2) + euroSymbol;
+ }
+
+ /**
+ * Defines a small text format for instruction labels.
+ */
+ private function getTipTextFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 8;
+ format.bold = false;
+ format.color = 0x000000;
+ return format;
+ }
+ }
+}
diff --git a/UsingRegularExpressions/ExpressionValidator2.as b/UsingRegularExpressions/ExpressionValidator2.as
new file mode 100644
index 0000000..1f6ac5d
--- /dev/null
+++ b/UsingRegularExpressions/ExpressionValidator2.as
@@ -0,0 +1,234 @@
+// Copyright (c) 2005 Adobe, Inc.
+package
+{
+ import com.example.programmingas3.shared.component.LabeledTextField;
+ import com.example.programmingas3.shared.component.SampleComponent;
+ import com.example.programmingas3.shared.component.SampleContainer;
+ import com.example.programmingas3.shared.component.SampleLabel;
+ import com.example.programmingas3.shared.component.SampleTextField;
+ import com.example.programmingas3.shared.component.SampleTextInput;
+
+ import flash.display.*;
+ import flash.events.*;
+ import flash.text.TextFormat;
+ import flash.util.trace;
+ import com.example.programmingas3.regexp.PatternValidator;
+ import com.example.programmingas3.regexp.HtmlTagValidator;
+ import com.example.programmingas3.regexp.EuroConvertor;
+
+ /**
+ * Demonstrates different matching and replacement techniques using
+ * Regular expressions.
+ *
+ * Creates a number of text fields and lets you validate or convert their contents.
+ */
+ public class ExpressionValidator2 extends SampleContainer
+ {
+ /**
+ * A simple name text field.
+ */
+ public var nameField:LabeledTextField;
+ /**
+ * Used for validating an email format
+ */
+ public var emailField:LabeledTextField;
+ /**
+ * Used for validating a phone number format
+ */
+ public var phoneNumberField:LabeledTextField;
+ /**
+ * Used for converting a list of dollar values into euro values.
+ */
+ public var donationField:LabeledTextField;
+ /**
+ * Used for validating HTML tags.
+ */
+ public var sourceField:LabeledTextField;
+ /**
+ * Displays the formatted HTML being validated.
+ */
+ public var htmlField:SampleHtmlField;
+ /**
+ * The initial HTML text used to populate the sourceField.
+ */
+ public var htmlFieldSrc:String = "Check this for HTML validity.";
+ /**
+ * The button that triggers the validations and conversions.
+ */
+ public var submitButton:SampleTextButton;
+
+ public var nameValidator:PatternValidator;
+ public var emailValidator:PatternValidator;
+ public var phoneValidator:PatternValidator;
+
+ public var htmlValidator:HtmlTagValidator;
+
+ public var euroConvertor:EuroConvertor;
+
+ public function ExpressionValidator2(w:uint = 500, h:uint = 400, thickness:Number = 0.0, pad:uint = 5)
+ {
+ super(w, h, thickness, pad);
+
+ // allows spaces, letters, commas, and periods only
+ var validNamePattern:RegExp = /^[ a-zA-Z,.]+$/;
+ nameValidator = new PatternValidator(validNamePattern);
+
+ // allows for standard email address formatting, like name@domain.com
+ var validEmailPattern:RegExp = /^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/;
+ emailValidator = new PatternValidator(validEmailPattern);
+
+ // allows formatted phone US numbers only, in the format 1(800)555-1212
+ var validPhoneNumberPattern:RegExp = /^(((1-)?\d{3}-)|(\(\d{3}\)))?\d{3}-\d{4}$/;
+ phoneValidator = new PatternValidator(validPhoneNumberPattern);
+
+ // allows basic HTML tags; the pattern is set internally in the HtmlTagValidator() constructor
+ htmlValidator = new HtmlTagValidator();
+
+ // converts dollar values into euro values
+ euroConvertor = new EuroConvertor();
+
+ this.init();
+ }
+
+ public override function init():void
+ {
+ this.backgroundColor = 0xDDDDEE;
+
+ var instructions:SampleLabel = new SampleLabel(400, 20, getTipTextFormat());
+ instructions.text = "Complete each of the following fields, and then click the Submit button.";
+ addComponent(instructions);
+
+ nameField = new LabeledTextField("Name:", "Bob");
+ addComponent(nameField);
+
+ emailField = new LabeledTextField("E-mail Address:", "bob@mail.example.com");
+ addComponent(emailField);
+
+ phoneNumberField = new LabeledTextField("Phone Number:", "(888)555-5271");
+ addComponent(phoneNumberField);
+
+ var phoneTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ phoneTips.x = 120;
+ // lets this label size to fit the text as needed
+ phoneTips.field.autoSize = TextFieldAutoSize.LEFT;
+ phoneTips.text = "Examples: 1-415-555-1212 or 415-555-1212 or (415)555-1212 or 555-1212";
+ addComponent(phoneTips);
+
+ donationField = new LabeledTextField("Donation Amounts:", "$955.95, $1,200, $14.22");
+ addComponent(donationField);
+
+ var donationTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ donationTips.x = 120;
+ // lets this label size to fit the text as needed
+ donationTips.field.autoSize = TextFieldAutoSize.LEFT;
+ donationTips.text = "Donations entered with a leading $ will be converted to Euros.";
+ addComponent(donationTips);
+
+ sourceField = new LabeledTextField("HTML Source:", htmlFieldSrc, 200, 60);
+ sourceField.inputField.field.multiline = true;
+ sourceField.inputField.field.html = false;
+ addComponent(sourceField);
+
+ var htmlTips:SampleLabel = new SampleLabel(200, 20, getTipTextFormat());
+ htmlTips.x = 120;
+ // lets this label size to fit the text as needed
+ htmlTips.field.autoSize = TextFieldAutoSize.LEFT;
+ htmlTips.text = "Enter HTML text above. The formatted version is displayed below.";
+ addComponent(htmlTips);
+
+ htmlField = new SampleHtmlField(200, 60);
+ htmlField.x = 120;
+ htmlField.selectable = false;
+ htmlField.htmlText = htmlFieldSrc;
+ addComponent(htmlField);
+
+ // When the sourceField loses focus, update the htmlField
+ sourceField.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
+
+ // When you click outside of the sourceField, update the htmlField.
+ // Don't want to do this on the change event, because when you're
+ // in the middle of typing HTML tags you would be passing invalid
+ // HTML to the htmlField with each keystroke.
+ this.addEventListener(MouseEvent.CLICK, onBackgroundClick);
+
+ arrangeTextFields();
+
+ submitButton = new SampleTextButton("Submit");
+ submitButton.y = getChildAt(numChildren - 1).y + getChildAt(numChildren - 1).height + 10;
+ submitButton.x = 120;
+ addChild(submitButton);
+ submitButton.addEventListener(MouseEvent.CLICK, validate);
+
+ draw();
+ }
+
+ /**
+ * Stacks the components vertically, with a gap between them.
+ */
+ private function arrangeTextFields()
+ {
+ var verticalGap:uint = 4;
+ var displayObject:DisplayObject;
+ var prevDisplayObject:DisplayObject;
+
+ for (var i:uint = 1; i < this.canvas.numChildren; i++)
+ {
+ displayObject = this.canvas.getChildAt(i);
+ prevDisplayObject = this.canvas.getChildAt(i - 1);
+
+ if ((displayObject is SampleComponent) || (displayObject is SampleTextButton))
+ {
+ displayObject.y = prevDisplayObject.y + prevDisplayObject.height + verticalGap;
+ trace("new Y=" + displayObject.y);
+ }
+ }
+ }
+
+ /**
+ * Updates the formatted HTML display when the HTML sourceField loses focus.
+ */
+ private function onFocusOut(evt:FocusEvent):void
+ {
+ htmlField.htmlText = sourceField.inputField.text;
+ }
+
+ /**
+ * Updates the formatted HTML display when you click outside the sourceField.
+ */
+ private function onBackgroundClick(evt:MouseEvent):void
+ {
+ //trace("onClick() target=" + evt.target);
+ if (! sourceField.inputField.hitTestPoint(evt.localX, evt.localY, true))
+ {
+ htmlField.htmlText = sourceField.inputField.text;
+ }
+ }
+
+ private function validate(e:Event)
+ {
+ // performs basic pattern matching
+ nameField.showWarning( !nameValidator.validate(nameField.inputField.text) );
+ emailField.showWarning( !emailValidator.validate(emailField.inputField.text) );
+ phoneNumberField.showWarning( !phoneValidator.validate(phoneNumberField.inputField.text) );
+
+ // checks for matching HTML tags
+ sourceField.showWarning( !htmlValidator.validate(sourceField.inputField.text) );
+
+ // converts dollar values to euro values
+ donationField.inputField.text = euroConvertor.convert(donationField.inputField.text);
+ }
+
+ /**
+ * Defines a small text format for instruction labels.
+ */
+ private function getTipTextFormat():TextFormat
+ {
+ var format:TextFormat = new TextFormat();
+ format.font = "Verdana";
+ format.size = 8;
+ format.bold = false;
+ format.color = 0x000000;
+ return format;
+ }
+ }
+}
diff --git a/UsingRegularExpressions/RegExpExample.as b/UsingRegularExpressions/RegExpExample.as
new file mode 100644
index 0000000..da6b362
--- /dev/null
+++ b/UsingRegularExpressions/RegExpExample.as
@@ -0,0 +1,232 @@
+// Copyright (c) 2005 Adobe, Inc.
+package {
+ import flash.display.*;
+ import flash.events.*;
+ import flash.events.MouseEventType;
+ import flash.text.TextFormat;
+ import flash.events.*;
+ import com.example.display.StandardTextButton;
+ import flash.util.trace;
+
+ public class RegExpExample extends Sprite {
+ public var nameField:LabelTextField;
+ public var emailField:LabelTextField;
+ public var phoneNumberField:LabelTextField;
+ public var donationField:LabelTextField;
+ public var commentsField:LabelTextField;
+
+ public var commentsFieldSrc:String = "Check this for HTML validity.";
+ public var controlButton:StandardTextButton;
+
+ public var mainFormat:TextFormat;
+ public function RegExpExample() {
+ init();
+ }
+ private function init():void {
+ mainFormat = new MainFormat();
+
+ var instructions:TextField = new TextField();
+ instructions.text = "Complete each of the following fields, and then click the Submit button.";
+ instructions.wordWrap = true;
+ instructions.width = 400;
+ instructions.height = instructions.textHeight + 15;
+ addChild(instructions);
+
+ nameField = new LabelTextField("Name:", "Bob");
+ addChild(nameField);
+
+ emailField = new LabelTextField("E-mail Address:", "bob@mail.example.com");
+ addChild(emailField);
+
+ phoneNumberField = new LabelTextField("Phone Number:", "(888)555-5271");
+ addChild(phoneNumberField);
+
+ var phoneTips:TipTextField;
+ var tipText:String = "Examples: 1-415-555-1212 or 415-555-1212 or (415)555-1212 or 555-1212";
+ phoneTips = new TipTextField(phoneNumberField, tipText);
+ phoneTips.height = phoneTips.textHeight + 5;
+ addChild(phoneTips);
+
+ donationField = new LabelTextField("Donation Amounts:", "$955.95, $1,200, $14.22");
+ addChild(donationField);
+
+ commentsField = new LabelTextField("Comments:", commentsFieldSrc, 100);
+ phoneTips.height = phoneTips.textHeight + 5;
+ addChild(commentsField);
+
+
+ var commentsTips:TipTextField;
+ var tipText:String = "Enter HTML formatted comments.";
+ commentsTips = new TipTextField(commentsField, tipText);
+ commentsTips.height = commentsTips.textHeight + 5;
+ addChild(commentsTips);
+
+ arrangeTextFields();
+
+ controlButton = new StandardTextButton("Submit");
+ controlButton.y = getChildAt(numChildren - 1).y
+ + getChildAt(numChildren - 1).height + 10;
+ controlButton.x = 100;
+ addChild(controlButton);
+ controlButton.addEventListener(MouseEvent.CLICK, validate);
+
+ commentsField.addEventListener(FocusEvent.FOCUS_IN, commentsFocused);
+ // flash.events.FocusEvent.FOCUS_IN doesn't work.
+ //commentsField.addEventListener("click", commentsFocused);
+ }
+ private function arrangeTextFields() {
+ for (var i:uint = 1; i < numChildren; i++) {
+ var displayObject:DisplayObject = getChildAt(i);
+ var prevDisplayObject:DisplayObject = getChildAt(i - 1);
+ if ((displayObject is LabelTextField) || (displayObject is TipTextField)) {
+ displayObject.y = prevDisplayObject.y + prevDisplayObject.height + 10;
+ } else {
+ y = getChildAt(numChildren - 1).y ;
+ x = getChildAt(numChildren - 1).x + getChildAt(numChildren - 1).width + 4;
+ }
+ }
+ }
+ private function commentsFocused(e:Event):void {
+ commentsField.inputTextField.text = commentsFieldSrc;
+ commentsField.inputTextField.setTextFormat(mainFormat);
+ commentsField.inputTextField.html = false;
+ commentsField.inputTextField.wordWrap = true;
+ }
+ private function validate(e:Event) {
+ var validNamePattern:RegExp = /^[ a-zA-Z,.]+$/;
+ var validEmailPattern:RegExp = /^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/;
+ var validPhoneNumberPattern:RegExp = /^(((1-)?\d{3}-)|(\(\d{3}\)))?\d{3}-\d{4}$/;
+
+ simpleRegExpTest(nameField, validNamePattern);
+ simpleRegExpTest(emailField, validEmailPattern);
+ simpleRegExpTest(phoneNumberField, validPhoneNumberPattern);
+
+ commentsFieldSrc = commentsField.inputTextField.text;
+ if (validCommentsHTML(commentsField.inputTextField.text)) {
+ commentsField.inputTextField.html = true;
+ commentsField.inputTextField.htmlText = commentsField.inputTextField.text;
+ commentsField.invalidFlag.visible = false;
+ } else {
+ trace ("Invalid comments.");
+ commentsField.invalidFlag.visible = true;
+ }
+
+ var usdPrice:RegExp = /\$([\d,]+.\d+)+/i;
+ donationField.inputTextField.text = donationField.inputTextField.text.replace(usdPrice, usdToEuro);
+ }
+ private function simpleRegExpTest(field:LabelTextField, regExp:RegExp) {
+ if (!field.inputTextField.text.match(regExp)) {
+ field.invalidFlag.visible = true
+ } else {
+ field.invalidFlag.visible = false
+ }
+
+ }
+ private function validCommentsHTML(str:String):Boolean {
+ var validHtmlTagPattern:RegExp = /^([^<]*)<(\w+)(\W.*)*>(.*?)<\/(\2)>(.*)/;
+ if (str == null) {
+ return true // Empty strings are valid HTML
+ } else if ((!str.match(/<\w+.*>/)) && (!str.match(/<\w+.*>/))) {
+ return true; // Strings with no tags are valid HTML.
+ } else {
+ var execArray:Array = validHtmlTagPattern.exec(str);
+ if (execArray == null) {
+ return false
+ } else if (execArray[2] != execArray[5]) {
+ return false
+ } else if ( validTag(execArray[2])
+ && validTag(execArray[5])) {
+ return validCommentsHTML(execArray[1])
+ && validCommentsHTML(execArray[4])
+ && validCommentsHTML(execArray[6]);
+ } else {
+ return false
+ }
+ }
+ }
+ private function validTag(tag:String):Boolean {
+ var validTags:Array = ["a", "b", "br", "font", "img", "i", "li", "p", "textformat", "u"];
+ var matching:Boolean = false;
+ for (var i:uint = 0; i < validTags.length; i++) {
+ if (tag == validTags[i]) {
+ matching = true;
+ }
+ }
+ return matching;
+ }
+ private function usdToEuro(...args):String {
+ var usd:String = args[1];
+ usd = usd.replace(",", "");
+ var exchangeRate:Number = 0.853690;
+ var euro:Number = usd * exchangeRate;
+ const euroSymbol:String = String.fromCharCode(8364);
+ return euro.toFixed(2) + euroSymbol;
+ }
+ }
+}
+
+import flash.display.*;
+import flash.text.TextFormat;
+import flash.util.trace;
+
+class LabelTextField extends Sprite {
+ public var mainFormat:MainFormat;
+ public var labelField:TextField
+ public var inputTextField:TextField
+ public var invalidFlag:InvalidFlag;
+ public function LabelTextField(label:String, inputText:String = "", inputHeight:Number = 20) {
+ mainFormat = new MainFormat();
+ labelField = new TextField();
+ labelField.defaultTextFormat = new TextFormat("Verdana", 8, 0x000000, true);
+ labelField.text = label;
+ labelField.width = labelField.textWidth + 4;
+ labelField.height = labelField.textHeight + 4;
+
+ inputTextField = new TextField();
+ inputTextField.text = inputText;
+ inputTextField.defaultTextFormat = mainFormat;
+ inputTextField.border = true;
+ inputTextField.type = TextFieldType.INPUT;
+ inputTextField.x = 100;
+ inputTextField.y = labelField.y;
+ inputTextField.width = 200;
+ inputTextField.height = inputHeight;
+
+ invalidFlag = new InvalidFlag();
+ invalidFlag.x = inputTextField.x + inputTextField.width;
+ invalidFlag.y = inputTextField.y;
+
+ addChild(labelField);
+ addChild(inputTextField);
+ addChild(invalidFlag);
+ }
+}
+
+class TipTextField extends TextField {
+ public function TipTextField(describedField:LabelTextField, label:String, height:Number = 20) {
+ defaultTextFormat = new TextFormat("Times", 8);
+ text = label;
+ y = describedField.y + describedField.height;
+ x = describedField.inputTextField.x;
+ width = describedField.inputTextField.width;
+ height = textHeight + 5;
+ }
+
+}
+
+class InvalidFlag extends TextField {
+ public function InvalidFlag() {
+ defaultTextFormat = new TextFormat("Verdana", 8, 0xCC2222, true);
+ text = "< Invalid entry";
+ height = 20;
+ visible = false;
+ }
+}
+
+class MainFormat extends TextFormat {
+ public function MainFormat() {
+ bold = false;
+ size = 10;
+ italic = false;
+ }
+}
diff --git a/UsingRegularExpressions/com/example/display/DropDownList.as b/UsingRegularExpressions/com/example/display/DropDownList.as
new file mode 100644
index 0000000..37083a3
--- /dev/null
+++ b/UsingRegularExpressions/com/example/display/DropDownList.as
@@ -0,0 +1,199 @@
+package com.examples.display {
+ import flash.display.*;
+ import flash.geom.*;
+ import flash.text.*;
+ import flash.util.*;
+ import flash.ui.Keyboard;
+ import flash.events.*;
+
+ public class DropDownList extends Sprite{
+ public var selectionList:Sprite;
+ public var currentLine:uint = 0;
+ public var currentSelection:ListEntry;
+ public var label:TextField;
+ public var listExpanded:Boolean = false;
+ private var dropDownButton:DropDownButton;
+ public function DropDownList(entryList:Array, labelText:String = "label") {
+ label = new TextField;
+ label.defaultTextFormat = new TextFormat("Verdana", 10, 0x000000, true);
+ label.text = labelText + ":";
+ addChild(label);
+
+ currentSelection = new ListEntry();
+ currentSelection.border = true;
+ currentSelection.selectable = true;
+ currentSelection.text = entryList[0].toString();
+ currentSelection.height = currentSelection.textHeight + 6;
+ currentSelection.width = 125;
+ currentSelection.addEventListener(MouseEvent.CLICK, textSelect);
+ addChild(currentSelection);
+
+ selectionList = new SelectionList(entryList);
+ for (var i:uint = 0; i < entryList.length; i++) {
+ SelectionList(selectionList).entries[i].addEventListener(MouseEvent.MOUSE_DOWN, listEntryClicked);
+ }
+ selectionList.x = currentSelection.x;
+ selectionList.y = currentSelection.height + 2;
+
+ dropDownButton = new DropDownButton();
+ repositionDropDownList(0);
+ dropDownButton.addEventListener(MouseEvent.CLICK, expandList)
+ addChild(dropDownButton);
+ }
+ public function listEntryClicked(e:Event):void {
+ var index:uint = e.target.parent.getChildIndex(e.target);
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ TextField(selectionList.getChildAt(index)).backgroundColor = 0xFFFF00;
+ currentLine = index;
+ selectionList.addEventListener(MouseEvent.MOUSE_UP, collapseList);
+ selectionList.addEventListener(MouseEvent.MOUSE_MOVE, checkMousePosition);
+ }
+ public function checkMousePosition(e:Event):void {
+ var mousePt:Point = new Point(stage.mouseX, stage.mouseY);
+ var selectedLine:uint;
+ for (var i:uint = 0; i < selectionList.numChildren - 1; i++) {
+ if (selectionList.getChildAt(i).hitTestPoint(mousePt.x, mousePt.y)) {
+ highlightLine(i);
+ trace(i);
+ }
+ }
+ }
+ public function repositionDropDownList(yOffset:Number):void {
+ dropDownButton.x = width - 20;
+ selectionList.y = yOffset + currentSelection.height;
+ }
+ public function textSelect(e:Event):void {
+ highlightCurrentSelection();
+ currentSelection.addEventListener(KeyboardEvent.KEY_DOWN, arrowTextSelect);
+ }
+ public function arrowTextSelect(e:KeyboardEvent):void {
+ var key:uint = e.keyCode;
+ if (key == Keyboard.DOWN) {
+ currentLine = Math.min(selectionList.numChildren - 2, currentLine + 1);
+ } else if (key == Keyboard.UP) {
+ currentLine = Math.max(0, currentLine - 1);
+ }
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ setInterval(highlightCurrentSelection, 50);
+ if (key == Keyboard.ENTER) {
+ setInterval(unhighlightCurrentSelection, 50);
+ currentSelection.removeEventListener(KeyboardEvent.KEY_DOWN, arrowTextSelect);
+ }
+ }
+ public function highlightCurrentSelection():void {
+ currentSelection.setSelection(0, currentSelection.length);
+ }
+ public function unhighlightCurrentSelection():void {
+ currentSelection.setSelection(0, 0);
+ }
+ public function expandList(e:Event) {
+ listExpanded = true;
+ stage.addEventListener(MouseEvent.MOUSE_DOWN, clearDropDownLists)
+ parent.addChildAt(selectionList, parent.numChildren - 1);
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ stage.focus = currentSelection;
+ currentSelection.addEventListener(KeyboardEvent.KEY_DOWN, arrowListSelect);
+ var clickPt:Point = new Point(stage.mouseX, stage.mouseY);
+ }
+ public function clearDropDownLists(e:Event):void {
+ if (!hitTestPoint(stage.mouseX, stage.mouseY)) {
+ collapseList();
+ }
+ }
+ public function arrowListSelect(e:KeyboardEvent):void {
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ var key:uint = e.keyCode;
+ if (key == Keyboard.DOWN) {
+ currentLine = Math.min(selectionList.numChildren - 2, currentLine + 1);
+ } else if (key == Keyboard.UP) {
+ currentLine = Math.max(0, currentLine - 1);
+ }
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ setInterval(highlightCurrentSelection, 50);
+ if (key == Keyboard.ENTER) {
+ setInterval(unhighlightCurrentSelection, 50);
+ currentSelection.removeEventListener(KeyboardEvent.KEY_DOWN, arrowListSelect);
+ collapseList();
+ }
+ }
+ public function collapseList(...args) {
+ if (listExpanded) parent.removeChild(selectionList);
+ listExpanded = false;
+ selectionList.removeEventListener(MouseEvent.MOUSE_MOVE, checkMousePosition);
+ currentSelection.text = TextField(selectionList.getChildAt(currentLine)).text;
+ }
+ public function highlightLine(n:uint):void {
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFFFF;
+ currentLine = n;
+ TextField(selectionList.getChildAt(currentLine)).backgroundColor = 0xFFFF00;
+ }
+ }
+}
+
+import flash.display.*;
+import flash.geom.*;
+import flash.util.trace;
+import flash.text.TextFormat;
+
+class ListEntry extends TextField {
+ public function ListEntry() {
+ defaultTextFormat = new TextFormat("Courier", 12);
+ background = true;
+ backgroundColor = 0xFFFFFF;
+ width = 125;
+ type = TextFieldType.DYNAMIC;
+ wordWrap = true;
+ height = 20;
+ x = 80;
+ }
+}
+
+class SelectionList extends Sprite {
+ public var entries:Array;
+ public function SelectionList(entryList:Array):void {
+ entries = new Array();
+ for (var i:uint=0; i < entryList.length ; i++) {
+ entries[i] = new ListEntry();
+ entries[i].x = 0;
+ entries[i].text += entryList[i].toString();
+ if (i > 0) {
+ entries[i].y = entries[i - 1].y + entries[i - 1].height;
+ }
+ addChild(entries[i]);
+ }
+ var outline:Shape = new Shape();
+ outline.graphics.lineStyle(1, 0x000000);
+ outline.graphics.drawRect(0, 0, width, height);
+ addChild(outline);
+ }
+}
+class DropDownButton extends SimpleButton {
+ public function DropDownButton() {
+ upState = new DropDownButtonState();
+ hitTestState = upState;
+ overState = upState;
+ downState = new DropDownButtonState();
+ var downStateTransform:Transform = downState.transform;
+ const downShadeMultiplier:Number = 0.75;
+ downStateTransform.colorTransform = new ColorTransform(downShadeMultiplier, downShadeMultiplier, downShadeMultiplier);
+ downState.transform = downStateTransform;
+
+ }
+}
+
+class DropDownButtonState extends Sprite {
+ public function DropDownButtonState() {
+ const size:Number = 19;
+ const sideMargin:Number = 7;
+ const topMargin:Number = 8;
+ graphics.lineStyle(1, 0x000000);
+ graphics.beginFill(0xDDDDDD);
+ graphics.drawRect(1, 1, size, size);
+ graphics.moveTo(sideMargin, topMargin);
+ graphics.beginFill(0x000000);
+ graphics.lineTo(size - sideMargin, topMargin);
+ graphics.lineTo(size / 2, size - topMargin);
+ graphics.lineTo(sideMargin, topMargin);
+ }
+}
\ No newline at end of file
diff --git a/UsingRegularExpressions/com/example/display/StandardTextButton.as b/UsingRegularExpressions/com/example/display/StandardTextButton.as
new file mode 100644
index 0000000..cdd22d6
--- /dev/null
+++ b/UsingRegularExpressions/com/example/display/StandardTextButton.as
@@ -0,0 +1,55 @@
+package com.example.display {
+ import flash.display.*;
+ import flash.geom.*;
+
+ public class StandardTextButton extends SimpleButton {
+ public var label:String;
+ public function StandardTextButton(txtString:String="label") {
+ label = txtString;
+ upState = new StandardTextButtonState(label);
+ hitTestState = upState;
+ overState = upState;
+ downState = new StandardTextButtonState(label);
+ var downStateTransform:Transform = downState.transform;
+ const downShadeMultiplier:Number = 0.75;
+ downStateTransform.colorTransform = new ColorTransform(downShadeMultiplier, downShadeMultiplier, downShadeMultiplier);
+ downState.transform = downStateTransform;
+
+ }
+
+ }
+}
+
+import flash.display.*;
+import flash.text.*;
+import flash.util.trace;
+import flash.filters.DropShadowFilter
+
+class StandardTextButtonState extends Sprite {
+ public var txt:TextField;
+ public function StandardTextButtonState(txtString:String) {
+ txt = new TextField();
+ txt.defaultTextFormat = new TextFormat("Verdana");
+ txt.defaultTextFormat.size = 12;
+ txt.defaultTextFormat.align = TextFormatAlign.CENTER
+ txt.text = txtString;
+ addChild(txt);
+ txt.x += 4;
+ txt.width = txt.textWidth + 12;
+ txt.height = txt.textHeight + 6;
+
+ var background:Shape = new Shape;
+
+ background.graphics.beginFill(0xEEEEEE);
+ background.graphics.lineStyle(1, 0x000000);
+ background.graphics.drawRoundRect(0, 0, width, height, 8, 8);
+
+ var shadow:DropShadowFilter = new DropShadowFilter();
+ shadow.blurX = 0;
+ shadow.blurY = 0;
+ shadow.distance = 2;
+ background.filters = [shadow];
+
+ addChildAt(background, 0);
+ }
+}
diff --git a/UsingRegularExpressions/com/example/programmingas3/regexp/EuroConvertor.as b/UsingRegularExpressions/com/example/programmingas3/regexp/EuroConvertor.as
new file mode 100644
index 0000000..4d94b50
--- /dev/null
+++ b/UsingRegularExpressions/com/example/programmingas3/regexp/EuroConvertor.as
@@ -0,0 +1,26 @@
+package com.example.programmingas3.regexp {
+
+ import com.example.programmingas3.regexp.PatternConvertor;
+
+ public class EuroConvertor extends PatternConvertor {
+
+ public function EuroConvertor() {
+ var usdPricePattern:RegExp = /\$([\d,]+.\d+)+/i;
+ super(usdPricePattern);
+ }
+
+ /**
+ * Replaces a Dollar value with an equivalent Euro value.
+ */
+ protected override function convertFunction(...args):String
+ {
+ var usd:String = args[1];
+ usd = usd.replace(",", "");
+ var exchangeRate:Number = 0.853690;
+ var euroValue:Number = usd * exchangeRate;
+
+ const euroSymbol:String = String.fromCharCode(8364);
+ return euroValue.toFixed(2) + euroSymbol;
+ }
+ }
+}
\ No newline at end of file
diff --git a/UsingRegularExpressions/com/example/programmingas3/regexp/HtmlTagValidator.as b/UsingRegularExpressions/com/example/programmingas3/regexp/HtmlTagValidator.as
new file mode 100644
index 0000000..170a98e
--- /dev/null
+++ b/UsingRegularExpressions/com/example/programmingas3/regexp/HtmlTagValidator.as
@@ -0,0 +1,57 @@
+package com.example.programmingas3.regexp {
+
+ public class HtmlTagValidator extends PatternValidator {
+
+ public function HtmlTagValidator() {
+ var validHtmlTagPattern:RegExp = /^([^<]*)<(\w+)(\W.*)*>(.*?)<\/(\2)>(.*)/;
+ super(validHtmlTagPattern);
+ }
+
+ public override function validate(text:String):Boolean {
+
+ if (text == null) {
+ return true; // Empty strings are valid HTML
+ }
+ else if ((!text.match(/<\w+.*>/)) && (!text.match(/<\w+.*>/))) {
+ return true; // Strings with no tags are valid HTML.
+ }
+ else {
+ var execArray:Array = pattern.exec(text);
+ if (execArray == null)
+ {
+ return false;
+ }
+ else if (execArray[2] != execArray[5])
+ {
+ return false;
+ }
+ else if ( validTag(execArray[2]) && validTag(execArray[5]))
+ {
+ return validate(execArray[1])
+ && validate(execArray[4])
+ && validate(execArray[6]);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ private function validTag(tag:String):Boolean
+ {
+ var validTags:Array = ["a", "b", "br", "font", "img", "i", "li", "p", "textformat", "u"];
+ var matching:Boolean = false;
+
+ for (var i:uint = 0; i < validTags.length; i++)
+ {
+ if (tag == validTags[i])
+ {
+ matching = true;
+ }
+ }
+ return matching;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/UsingRegularExpressions/com/example/programmingas3/regexp/PatternConvertor.as b/UsingRegularExpressions/com/example/programmingas3/regexp/PatternConvertor.as
new file mode 100644
index 0000000..54e6e38
--- /dev/null
+++ b/UsingRegularExpressions/com/example/programmingas3/regexp/PatternConvertor.as
@@ -0,0 +1,25 @@
+package com.example.programmingas3.regexp {
+
+ public class PatternConvertor {
+
+ public var pattern:RegExp;
+
+ public function PatternConvertor(pat:RegExp) {
+ pattern = pat;
+ }
+
+ public function convert(text:String):String {
+ return text.replace(pattern, convertFunction);
+ }
+
+ /**
+ * Just echoes the original text. This method is meant to be overridden
+ * in a subclass.
+ */
+ protected function convertFunction(...args):String
+ {
+ var text:String = args[1];
+ return text;
+ }
+ }
+}
\ No newline at end of file
diff --git a/UsingRegularExpressions/com/example/programmingas3/regexp/PatternValidator.as b/UsingRegularExpressions/com/example/programmingas3/regexp/PatternValidator.as
new file mode 100644
index 0000000..d22b4e0
--- /dev/null
+++ b/UsingRegularExpressions/com/example/programmingas3/regexp/PatternValidator.as
@@ -0,0 +1,15 @@
+package com.example.programmingas3.regexp {
+
+ public class PatternValidator {
+
+ public var pattern:RegExp;
+
+ public function PatternValidator(pat:RegExp) {
+ pattern = pat;
+ }
+
+ public function validate(text:String):Boolean {
+ return text.match(pattern);
+ }
+ }
+}
\ No newline at end of file
diff --git a/VideoJukebox/VideoJukebox.as b/VideoJukebox/VideoJukebox.as
new file mode 100644
index 0000000..635a3e2
--- /dev/null
+++ b/VideoJukebox/VideoJukebox.as
@@ -0,0 +1 @@
+package {
import fl.controls.*;
import fl.events.SliderEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.TimerEvent;
import flash.media.SoundTransform;
import flash.media.Video;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Timer;
public class VideoJukebox extends Sprite {
/**
* The amount of time between calls to update the playhead timer, in
* milliseconds.
*/
private const PLAYHEAD_UPDATE_INTERVAL_MS:uint = 10;
/**
* The path to the XML file containing the video playlist.
*/
private const PLAYLIST_XML_URL:String = "playlist.xml";
/**
* The client object to use for the NetStream object.
*/
private var client:Object;
/**
* The index of the currently playing video.
*/
private var idx:uint = 0;
/**
* A copy of the current video's metadata object.
*/
private var meta:Object;
private var nc:NetConnection;
private var ns:NetStream;
private var playlist:XML;
private var t:Timer;
private var uldr:URLLoader;
private var vid:Video;
private var videosXML:XMLList;
/**
* The SoundTransform object used to set the volume for the NetStream.
*/
private var volumeTransform:SoundTransform;
/**
* Constructor
*/
public function VideoJukebox() {
// Initialize the uldr variable which will be used to load the external
// playlist XML file.
uldr = new URLLoader();
uldr.addEventListener(Event.COMPLETE, xmlCompleteHandler);
uldr.load(new URLRequest(PLAYLIST_XML_URL));
}
/**
* Once the XML file has loaded, parse the file contents into an XML object,
* and create an XMList for the video nodes in the XML.
*/
private function xmlCompleteHandler(event:Event):void {
playlist = XML(event.target.data);
videosXML = playlist.video;
main();
}
/**
* The main application.
*/
private function main():void {
volumeTransform = new SoundTransform();
// Create the client object for the NetStream, and set up a callback
// handler for the onMetaData event.
client = new Object();
client.onMetaData = metadataHandler;
nc = new NetConnection();
nc.connect(null);
// Initialize the NetSteam object, add a listener for the netStatus
// event, and set the client for the NetStream.
ns = new NetStream(nc);
ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
ns.client = client;
// Initialize the Video object, attach the NetStram, and add the Video
// object to the display list.
vid = new Video();
vid.x = 20;
vid.y = 75;
vid.attachNetStream(ns);
addChild(vid);
// Begin playback of the first video.
playVideo();
// Initialize the Timer object and set the delay to
// PLAYHEAD_UPDATE_INTERVAL_MS milliseconds.
t = new Timer(PLAYHEAD_UPDATE_INTERVAL_MS);
t.addEventListener(TimerEvent.TIMER, timerHandler);
// Configure the positionBar ProgressBar instance and set the mode to
// MANUAL. Progress bar values will be explicitly set using the
// setProgress() method.
positionBar.mode = ProgressBarMode.MANUAL;
// Configure the volumeSlider Slider component instance. The maximum
// value is set to 1 because the volume in the SoundTransform object
// is set to a number between 0 and 1. The snapInterval and tickInterval
// properties are set to 0.1 which allows users to set the volume to
// 0, 0.1 - 0.9, 1.0 which allows users to increment or decrement the
// volume by 10%.
volumeSlider.value = volumeTransform.volume;
volumeSlider.minimum = 0;
volumeSlider.maximum = 1;
volumeSlider.snapInterval = 0.1;
volumeSlider.tickInterval = volumeSlider.snapInterval;
// Setting the liveDragging property to true causes the Slider
// instance's change event to be dispatched whenever the slider is
// moved, rather than when the user releases the slider thumb.
volumeSlider.liveDragging = true;
volumeSlider.addEventListener(SliderEvent.CHANGE, volumeChangeHandler);
// Configure the various Button instances. Each Button instance uses
// the same click handler.
playButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
pauseButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
stopButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
backButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
forwardButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
}
/**
* Event listener for the volumeSlider instance. Called when the user
* changes the value of the volume slider.
*/
private function volumeChangeHandler(event:SliderEvent):void {
// Set the volumeTransform's volume property to the current value of the
// Slider and set the NetStream object's soundTransform property.
volumeTransform.volume = event.value;
ns.soundTransform = volumeTransform;
}
/**
* Event listener for the ns object. Called when the net stream's status
* changes.
*/
private function netStatusHandler(event:NetStatusEvent):void {
try {
switch (event.info.code) {
case "NetStream.Play.Start" :
// If the current code is Start, start the timer object.
t.start();
break;
case "NetStream.Play.StreamNotFound" :
case "NetStream.Play.Stop" :
// If the current code is Stop or StreamNotFound, stop
// the timer object and play the next video in the playlist.
t.stop();
playNextVideo();
break;
}
} catch (error:TypeError) {
// Ignore any errors.
}
}
/**
* Event listener for the ns object's client property. This method is called
* when the net stream object receives metadata information for a video.
*/
private function metadataHandler(metadataObj:Object):void {
// Store the metadata information in the meta object.
meta = metadataObj;
// Resize the Video instance on the display list with the video's width
// and height from the metadata object.
vid.width = meta.width;
vid.height = meta.height;
// Reposition and resize the positionBar progress bar based on the
// current video's dimensions.
positionBar.move(vid.x, vid.y + vid.height);
positionBar.width = vid.width;
}
/**
* Retrieve the current video from the playlist XML object.
*/
private function getVideo():String {
return videosXML[idx].@url;
}
/**
* Play the currently selected video.
*/
private function playVideo():void {
var url:String = getVideo();
ns.play(url);
}
/**
* Decrease the current video index and begin playback of the video.
*/
private function playPreviousVideo():void {
if (idx > 0) {
idx--;
playVideo();
// Make sure the positionBar progress bar is visible.
positionBar.visible = true;
}
}
/**
* Increase the current video index and begin playback of the video.
*/
private function playNextVideo():void {
if (idx < (videosXML.length() - 1)) {
// If this is not the last video in the playlist increase the
// video index and play the next video.
idx++;
playVideo();
// Make sure the positionBar progress bar is visible.
positionBar.visible = true;
} else {
// If this is the last video in the playlist increase the video
// index, clear the contents of the Video object and hide the
// positionBar progress bar. The video index is increased so that
// when the video ends, clicking the backButton will play the
// correct video.
idx++;
vid.clear();
positionBar.visible = false;
}
}
/**
* Click handler for each of the video playback buttons.
*/
private function buttonClickHandler(event:MouseEvent):void {
// Use a switch statement to determine which button was clicked.
switch (event.currentTarget) {
case playButton :
// If the play button was clicked, resume the video playback.
// If the video was already playing, this has no effect.
ns.resume();
break;
case pauseButton :
// If the pause button was clicked, pause the video playback.
// If the video was already playing, the video will be paused.
// If the video was already paused, the video will be resumed.
ns.togglePause();
break;
case stopButton :
// If the stop button was clicked, pause the video playback
// and reset the playhead back to the beginning of the video.
ns.pause();
ns.seek(0);
break;
case backButton :
// If the back button was clicked, play the previous video in
// the playlist.
playPreviousVideo();
break;
case forwardButton :
// If the forward button was clicked, play the next video in
// the playlist.
playNextVideo();
break;
}
}
/**
* Event handler for the timer object. This method is called every
* PLAYHEAD_UPDATE_INTERVAL_MS milliseconds as long as the timer is running.
*/
private function timerHandler(event:TimerEvent):void {
try {
// Update the progress bar and label based on the amount of video
// that has played back.
positionBar.setProgress(ns.time, meta.duration);
positionLabel.text = ns.time.toFixed(1) + " of " + meta.duration.toFixed(1) + " seconds";
} catch (error:Error) {
// Ignore this error.
}
}
}
}
\ No newline at end of file
diff --git a/VideoJukebox/VideoJukebox.fla b/VideoJukebox/VideoJukebox.fla
new file mode 100644
index 0000000..2c8f679
Binary files /dev/null and b/VideoJukebox/VideoJukebox.fla differ
diff --git a/VideoJukebox/VideoJukebox.mxml b/VideoJukebox/VideoJukebox.mxml
new file mode 100644
index 0000000..6811ab0
--- /dev/null
+++ b/VideoJukebox/VideoJukebox.mxml
@@ -0,0 +1,304 @@
+
+
+
+
+ 0) {
+ idx--;
+ playVideo();
+ // Make sure the positionBar progress bar is visible.
+ positionBar.visible = true;
+ }
+ }
+
+ /**
+ * Increase the current video index and begin playback of the video.
+ */
+ private function playNextVideo():void {
+ if (idx < (videosXML.length() - 1)) {
+ // If this is not the last video in the playlist increase the
+ // video index and play the next video.
+ idx++;
+ playVideo();
+ // Make sure the positionBar progress bar is visible.
+ positionBar.visible = true;
+ } else {
+ // If this is the last video in the playlist increase the video
+ // index, clear the contents of the Video object and hide the
+ // positionBar progress bar. The video index is increased so that
+ // when the video ends, clicking the backButton will play the
+ // correct video.
+ idx++;
+ vid.clear();
+ positionBar.visible = false;
+ }
+ }
+
+ /**
+ * Click handler for each of the video playback buttons.
+ */
+ private function buttonClickHandler(event:MouseEvent):void {
+ // Use a switch statement to determine which button was clicked.
+ switch (event.currentTarget) {
+ case playButton :
+ // If the play button was clicked, resume the video playback.
+ // If the video was already playing, this has no effect.
+ ns.resume();
+ break;
+ case pauseButton :
+ // If the pause button was clicked, pause the video playback.
+ // If the video was already playing, the video will be paused.
+ // If the video was already paused, the video will be resumed.
+ ns.togglePause();
+ break;
+ case stopButton :
+ // If the stop button was clicked, pause the video playback
+ // and reset the playhead back to the beginning of the video.
+ ns.pause();
+ ns.seek(0);
+ break;
+ case backButton :
+ // If the back button was clicked, play the previous video in
+ // the playlist.
+ playPreviousVideo();
+ break;
+ case forwardButton :
+ // If the forward button was clicked, play the next video in
+ // the playlist.
+ playNextVideo();
+ break;
+ }
+ }
+
+ /**
+ * Event handler for the timer object. This method is called every
+ * PLAYHEAD_UPDATE_INTERVAL_MS milliseconds as long as the timer is running.
+ */
+ private function timerHandler(event:TimerEvent):void {
+ try {
+ // Update the progress bar and label based on the amount of video
+ // that has played back.
+ positionBar.setProgress(ns.time, meta.duration);
+ positionLabel.text = ns.time.toFixed(1) + " of " + meta.duration.toFixed(1) + " seconds";
+ } catch (error:Error) {
+ // Ignore this error.
+ }
+ }
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/VideoJukebox/playlist.xml b/VideoJukebox/playlist.xml
new file mode 100644
index 0000000..7c34e6a
--- /dev/null
+++ b/VideoJukebox/playlist.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WikiEditor/WikiEditor.fla b/WikiEditor/WikiEditor.fla
new file mode 100644
index 0000000..c284ea8
Binary files /dev/null and b/WikiEditor/WikiEditor.fla differ
diff --git a/WikiEditor/WikiEditor.mxml b/WikiEditor/WikiEditor.mxml
new file mode 100644
index 0000000..5572021
--- /dev/null
+++ b/WikiEditor/WikiEditor.mxml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WikiEditor/com/example/programmingas3/regExpExamples/CurrencyConverter.as b/WikiEditor/com/example/programmingas3/regExpExamples/CurrencyConverter.as
new file mode 100644
index 0000000..48d0839
--- /dev/null
+++ b/WikiEditor/com/example/programmingas3/regExpExamples/CurrencyConverter.as
@@ -0,0 +1,53 @@
+package com.example.programmingas3.regExpExamples {
+
+ /**
+ * Displays a round clock face with an hour hand, a minute hand, and a second hand.
+ */
+ public class CurrencyConverter {
+ /**
+ * Converts strings of US dollar values (such as "$9.95")
+ * to Euro strings (such as "8.24 €".
+ */
+ public static function usdToEuro(input:String):String {
+
+ /**
+ * A regular expression matching a pattern for a US dollar value.
+ * A $ symbol (\$) followed by a number pattern. The number pattern
+ * matches a string of one or more characters that are either digits
+ * or commas ( [\d,]+) followed by at least one digit (\d+).
+ * Note that parentheses are used to capture the number pattern.
+ */
+ var usdPrice:RegExp = /\$([\d,]+.\d+)+/g;
+
+ /**
+ * Replaces the matching dollar strings with the Euro equivalent string.
+ * The second parameter defines a function, used to define the
+ * replacement string.
+ */
+ return input.replace(usdPrice, usdStrToEuroStr);
+ }
+
+ /**
+ * Called as the second parameter of the replace() method of a String.
+ * As such, the call passes the following parameters:
+ *
+ * - The matching portion of the string, such as "$9.95"
+ * - The parenthetical match, such as "9.95"
+ * - The index position in the string where the match begins
+ * - The complete string
+ *
+ * This method takes the second parameter (args[1]), converts it to
+ * a number, and then converts it to a Euro string, by applying a
+ * conversion factor and then appending the € character.
+ */
+ private static function usdStrToEuroStr(...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);
+ const euroSymbol:String = String.fromCharCode(8364);
+ return euro.toFixed(2) + " " + euroSymbol;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WikiEditor/com/example/programmingas3/regExpExamples/URLParser.as b/WikiEditor/com/example/programmingas3/regExpExamples/URLParser.as
new file mode 100644
index 0000000..fdd920b
--- /dev/null
+++ b/WikiEditor/com/example/programmingas3/regExpExamples/URLParser.as
@@ -0,0 +1,92 @@
+package com.example.programmingas3.regExpExamples {
+
+ /**
+ * A utility class that converts URL strings, such as "http://example.com" to
+ * HTML anchor links, such as "http://example.com.
+ */
+ public class URLParser
+ {
+ /**
+ * Converts HTTP and FTP URLs to anchor links. This function assembles a
+ * RegExp pattern out of multiple parts: protocol, urlPart, and optionalUrlPart.
+ */
+ public static function urlToATag(input:String):String {
+
+ /**
+ * Matches either http:// or ftp://. (?: indicates that the interior group
+ * is not a capturing group.
+ */
+ var protocol:String = "((?:http|ftp)://)";
+
+ /**
+ * For the URL http://foo.example.com/foo, matches foo.example.
+ */
+ var urlPart:String = "([a-z0-9_-]+\.[a-z0-9_-]+)";
+
+ /**
+ * For the URL http://example.com/foo, matches example.
+ */
+ var optionalUrlPart:String = "(\.[a-z0-9_-]*)";
+
+ /**
+ * Assembles the pattern from its component parts.
+ */
+ var urlPattern:RegExp = new RegExp (protocol + urlPart + optionalUrlPart, "ig");
+
+ /**
+ * Replaces matching URL strings with a replacement string. The call to
+ * the replace() method uses references to captured groups (such as $1)
+ * to assemble the replacement string.
+ */
+ var result:String = input.replace(urlPattern, "$1$2$3");
+
+ /**
+ * Next, find e-mail patterns and replace them with hyperlinks.
+ */
+ result = emailToATag(result);
+ return result;
+ }
+
+ /**
+ * Replaces an e-mail pattern with a corresponding HTML anchor hyperlink.
+ * Like the urlToATag() method, this method assembles a regular expression out
+ * of constituent parts.
+ */
+ public static function emailToATag(input:String):String {
+ /**
+ * Isolates the mailto: part of the string.
+ */
+ var protocol:String = "(mailto:)";
+
+ /**
+ * Matches the name and @ symbol, such as bob.fooman@.
+ */
+ var name:String = "([a-z0-9_-]+(?:\.[a-z0-9_-])*@)";
+
+ /**
+ * For the e-mail pattern bob.fooman@mail.example.com, matches
+ * mail.example. (including the trailing dot).
+ */
+ var domain:String = "((?:[a-z0-9_-].)*)";
+
+ /**
+ * Matches the superdomain, such as com, uk, or org., which is 2 - 4 letters.
+ */
+ var superDomain:String = "([a-z]{2,4})";
+
+ /**
+ * Assembles the matching regular expression out of constituent parts.
+ */
+ var emailPattern:RegExp = new RegExp (protocol + name + domain + superDomain, "ig");
+
+ /**
+ * Replaces matching e-mail strings with a replacement string. The call to
+ * the replace() method uses references to captured groups (such as $1)
+ * to assemble the replacement string.
+ */
+ var result:String = input.replace(emailPattern, "$1$2$3$4");
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WikiEditor/com/example/programmingas3/regExpExamples/WikiParser.as b/WikiEditor/com/example/programmingas3/regExpExamples/WikiParser.as
new file mode 100644
index 0000000..696801a
--- /dev/null
+++ b/WikiEditor/com/example/programmingas3/regExpExamples/WikiParser.as
@@ -0,0 +1,111 @@
+package com.example.programmingas3.regExpExamples
+{
+ /**
+ * Used to define the main functionality of the WikiParser application:
+ *
+ * - Provides a sample wiki string.
+ * - Uses regular expressions to convert a Wiki string into HTML text.
+ *
+ */
+ public class WikiParser {
+
+ /**
+ * The string that contains the wiki text.
+ */
+ public var wikiData:String;
+
+ /**
+ * The constructor function, which initializes the wiki text with new sample data.
+ */
+ public function WikiParser() {
+ wikiData = setWikiData();
+ }
+
+ /**
+ * Returns sample wiki data.
+ */
+ private function setWikiData():String {
+ var str:String = "'''Test wiki data'''\n" +
+ "\n" +
+ "This is a test. This is ''only'' a test.\n" +
+ "Basic rules:\n" +
+ "* 3 single quote marks indicates '''bold'''.\n" +
+ "* 2 single quote marks indicates ''italics''.\n" +
+ "* An asterisk creates a bulleted list item.\n" +
+ "* Use blank lines as paragraph separators.\n" +
+ "\n" +
+ "You can convert a dollar value like this: $9.95.\n" +
+ "\n" +
+ "Here's a URL to convert: http://www.adobe.com.\n" +
+ "\n" +
+ "Here's an e-mail address to convert: mailto:bob@example.com.";
+
+ return str;
+ }
+
+ /**
+ * Converts a wiki string to HTML text.
+ */
+ public function parseWikiString (wikiString:String):String {
+ var result:String = parseBold(wikiString);
+ result = parseItalic(result);
+ result = linesToParagraphs(result);
+ result = parseBullets(result);
+ return result;
+ }
+
+ /**
+ * Replaces a bold pattern in a wiki string with the
+ * HTML equivalent. For example '''foo''' becomes
+ * foo.
+ */
+ private function parseBold(input:String):String {
+ var pattern:RegExp = /'''(.*?)'''/g;
+ return input.replace(pattern, "$1");
+ }
+
+ /**
+ * Replaces an italic pattern in a wiki string with the
+ * HTML equivalent. For example ''foo'' becomes
+ * foo.
+ */
+ private function parseItalic(input:String):String {
+ var pattern:RegExp = /''(.*?)''/g;
+ return input.replace(pattern, "$1");
+ }
+
+ /**
+ * Replaces a bold pattern in a wiki string with the
+ * HTML equivalent. For example the following line:
+ * * foo
+ * Becomes this:
+ *
+ *
foo
+ */
+ private function parseBullets(input:String):String {
+ var pattern:RegExp = /^\*(.*)/gm;
+ return input.replace(pattern, "
$1
");
+ }
+
+ /**
+ * Replaces a newline in a wiki string with the
+ *
HTML tag.
+ */
+ private function linesToParagraphs(input:String):String {
+
+ /**
+ * Strips out empty lines, which match /^$/gm
+ */
+ var pattern:RegExp = /^$/gm;
+ var result:String = input.replace(pattern, "");
+
+ /**
+ * Adds
tags around all remaining lines.
+ * However, not those that begin with an asterisk,
+ * which are considered