Archive

You are currently browsing the Scott Morgan blog archives for July, 2006.

Jul

21

MySpace.com forcing Flash 9 on users.

By scott

Looks like MySpace is already pushing Flash 9. This should help increase the penetration rates very quickly. Check out tiara.org for more info.

Jul

17

Actionscript Credit Card Validation - Luhn Check

By scott

By no means did I write this, if you do a search for Luhn Check in google you will come with a million results. However, most of them are Java implementations, JavaScript, ASP, JSP, but never ActionScript. Below is a version I converted from Javascript into Actionscript 2.0. This Luhn Check validates the users credit card numbers and makes sure A) there are enough for the given card, and B) matches the number pattern of a given card. This example only checks Visa, MasterCard, American Express, and Diners Club.

BY NO MEANS IS THIS THE ONLY CHECK YOU NEED TO DO TO PROCESS AN ORDER.

You must use a valid payment gateway that does proper server side credit card validation. The purpose of this script is to reduce traffic to the server by weeding out the invalid credit card numbers. If the client side tests are successful the application then has to send the Credit Card information to the server for proper Credit Card Validation and order processing.

lass com.scottgmorgan.utils.CreditCardValidation {

   /**
   * checks for datein the format MM/YY or MM/YYYY against the current date
   * @param month a number representing the month to be validated against.
   * @param year a number representing the year to be validated against.
   */
   public function isValidExpDate(month:Number,year:Number):Boolean {
      var result:Boolean = true;
      var now:Date = new Date();
      var nowMonth:Number = now.getMonth() + 1;
      var nowYear:Number = now.getFullYear();
      if ((nowYear > year) || ((nowYear == year ) && (nowMonth > month))){
         result = false;
      } 

      return result;
   }

   /**
   * checks for valid credit card format using the Luhn check and known digit
   * about various cards
   * @ccType a string indicating the entered credit card name
   * @ccNum a string indicating the credit card number being used.
   */
   public function isValidCreditCardNumber(ccType:String,ccNum:String):Boolean {
      var result:Boolean = true;
      if (ccNum.length>0){
         if (isNaN(ccNum)){
            result = false;
         }
         if (result){
            if (!luhnCheck(ccNum) || !validateCCNum(ccType,ccNum)){
               result = false;
            }
         }
      }
      return result;
   }

   private function luhnCheck(str:String){
      var result:Boolean = true;
      var sum:Number = 0;
      var mul:Number = 1;
      var strLen:Number = str.length;
      for (var i:Number = 0; i < strLen; i++){
         var digit:String = str.substring(strLen-i-1,strLen-i);
         var tproduct:Number = parseInt(digit ,10)*mul;
         if (tproduct >= 10){
            sum += (tproduct % 10) + 1;
         } else {
             sum += tproduct;
         }

         if (mul == 1){
            mul++;
         } else {
            mul--;
         }
      }

      if ((sum % 10) != 0){
         result = false;
      }

      return result;
   }

   private function validateCCNum(cardType:String,cardNum:String):Boolean{
      var result:Boolean = false;
      cardType = cardType.toUpperCase();
      var cardLen:Number = cardNum.length;
      var firstdig:String = cardNum.substring(0,1);
      var seconddig:String = cardNum.substring(1,2);
      var first4digs:String = cardNum.substring(0,4);

      switch (cardType){
         case "VISA":
            result = ((cardLen == 16) || (cardLen == 13)) && (firstdig == "4");
            break;
         case "AMEX":
            var validNums = "47";
            result = (cardLen == 15) && (firstdig == "3") && (validNums.indexOf(seconddig)>=0);
            break;
         case "MASTERCARD":
            var validNums = "12345";
            result = (cardLen == 16) && (firstdig == "5") && (validNums.indexOf(seconddig)>=0);
            break;
         case "DISCOVER":
            result = (cardLen == 16) && (first4digs == "6011");
            break;
         case "DINERS":
            var validNums = "068";
            result = (cardLen == 14) && (firstdig == "3") && (validNums.indexOf(seconddig)>=0);
            break;
         }
      return result;
   }
}

And here is the sample code that instantiates this bad boy:

var ccValidator = new CreditCardValidation();
var valid = ccValidator.isValidCreditCardNumber('visa', '4111111111111111');

trace('card is valid: ' + valid);

Pretty straightforward. I have hardcoded the values in the method call above but in the real world this would be puuled from a textfield or a combo box for the card type.

As a side note, if you ever want to get around the validation for Visa simply enter ‘4111111111111111′ (1 “4″, and 15 “1’s”). Unfortunatly, this number will not get you past the real creditcard validation that occurs on the server or at the payment gateway.

Jul

15

Actionscript 3.0 Rain Effect Source Code

By scott

I only posted the AS 3.0 Make it Rain effect yesterday and I have been bombarded with people asking me to release the source. It’s not the tightest code yet but here it is anyway:

package {
   import flash.display.MovieClip;
   import flash.display.BitmapData;
   import flash.display.Bitmap;
   import flash.geom.Rectangle;
   import flash.geom.Point;
   import flash.geom.Matrix;
   import flash.filters.ConvolutionFilter;
   import flash.geom.ColorTransform;
   import flash.filters.DisplacementMapFilter;
   import flash.utils.Timer;
   import flash.events.TimerEvent;

   class RainMaker {
      private var __scope:MovieClip;
      private var __clip:MovieClip;
      private var __sourceBitmap:BitmapData;
      private var __bitmap1:BitmapData;
      private var __bitmap2:BitmapData;
      private var __buffer:BitmapData;
      private var __outputImage:BitmapData;
      private var __bounds:Rectangle;
      private var __origin:Point;
      private var __matrix:Matrix;
      private var __matrix2:Matrix;
      private var __wave:ConvolutionFilter;
      private var __damp:ColorTransform;
      private var __water:DisplacementMapFilter;
      private var __surface:BitmapData;
      private var __fillcolour:int = 128;
      private var __xPos:int;
      private var __yPos:int;
      private var __lastx:int;
      private var __lasty:int;

      //Movieclips
      private var cloneClip_mc:MovieClip;

      function RainMaker(scope:MovieClip, clip:MovieClip) {
         __scope = scope;
         __clip = clip;
         draw();
      }

      private function draw():void {
         cloneClip_mc = new MovieClip();
         __scope.addChild(cloneClip_mc);
         var bitmapImage:BitmapData = new BitmapData(__clip.width, __clip.height, false, 0);
         bitmapImage.draw(__clip);
         __surface = bitmapImage;
         __clip.visible = false;
         __bitmap1 = new BitmapData(__clip.width, __clip.height, false, __fillcolour);
         __bitmap2 = new BitmapData(__clip.width, __clip.height, false, __fillcolour);
         __sourceBitmap = new BitmapData(__clip.width, __clip.height, false, __fillcolour);
         __buffer = new BitmapData(__clip.width, __clip.height, false, __fillcolour);
         __outputImage = new BitmapData(__clip.width, __clip.height, true, __fillcolour);
         __bounds = new Rectangle(0, 0, __clip.width, __clip.height);
         __origin = new Point();
         __matrix = new Matrix();
         __matrix2 = new Matrix();
         __matrix2.a = __matrix2.d = 2;
         __wave = new ConvolutionFilter(3, 3, [1, 1, 1, 1, 1, 1, 1, 1, 1], 9, 0);
         __damp = new ColorTransform(0, 0, 0.99609374, 1, 0, 0, 2, 0);
         __water = new DisplacementMapFilter(__bitmap2, __origin, 4, 4, 32, 32, "ignore");

         var bm:Bitmap=new Bitmap(__outputImage);
         cloneClip_mc.addChild(bm);
         var __waterTimer:Timer = new Timer(10);
         __waterTimer.addEventListener('timer', turnOnWater);
         __waterTimer.start();
         var myTimer:Timer = new Timer(150);
         myTimer.addEventListener('timer', waterEffect);
         myTimer.start();
      }

      private function fitsArea():Boolean {
         var curX = __xPos;
         var curY = __yPos;

         if ((curX < __clip.width) && (curY < __clip.height)) {
            return true;
         } else {
            return false;
          }
      }

      private function waterEffect(ev:Object):void {
         if (isNaN(__xPos)) {
            __xPos = 0;
            __yPos = 0;
         } else {
            if (fitsArea()) {
               __xPos = Math.random()*__clip.width;
               __yPos = Math.random()*__clip.height;
            } else {
               __xPos = 0;
               __yPos = 0;
            }
         }

         __lastx = __xPos;
         __lasty = __yPos;
      }

      private function turnOnWater(ev:Object):void {
            var curX = __xPos / 2;
            var curY = __yPos / 2;

            if ((__lasty != curY) || (__lastx != curX)) {
               __sourceBitmap.setPixel(curX + 1, curY, 16777215);
               __sourceBitmap.setPixel(curX - 1, curY, 16777215);
               __sourceBitmap.setPixel(curX, curY + 1, 16777215);
               __sourceBitmap.setPixel(curX, curY - 1, 16777215);
               __sourceBitmap.setPixel(curX, curY, 16777215);
            }

            __lastx = curX;
            __lasty = curY;
            __bitmap1.applyFilter(__sourceBitmap, __bounds, __origin, __wave);
            __bitmap1.draw(__bitmap1, __matrix, null, "add");
            __bitmap1.draw(__buffer, __matrix, null, "difference");
            __bitmap1.draw(__bitmap1, __matrix, __damp);
            __bitmap2.draw(__bitmap1, __matrix2, null, null, null, true);
            __outputImage.applyFilter(__surface, new Rectangle(0, 0, __clip.width,
            __clip.height), __origin, __water);
            __buffer = __sourceBitmap;
            __sourceBitmap = __bitmap1.clone();
      }
   }
}

Jul

14

Make it rain with the BitmapData Class

By scott

You can have a lot of useless fun with the BitmapData class. In the example below I recreated a popular rain on water effect over a picture using the BitmapData class, the ConvolutionFilter class, the DisplacementMapFilter class, the ColorTransform class, and a handy dandy little interval which uses the new timer classes in AS 3.0.

Since most of my work is for corporate clients I don’t get a lot of time to experiment with the creative capabilities in Actionscript. I am very tired tonight but stay tuned for the tutorial so you can use this effect on your site. If only I could get a swimming pool client, all the content could sit behind this effect. Wouldn’t that be annoying ;)

In order to see this demo you must have Flash Player 9 installed. If you don’t, go here and install it.

Jul

10

I heart E4X, the Actionscript 3.0 new XML Object

By scott

I finally got around to really playing with Actionscript 3 and two words come to mind LOVE IT. Especially the new XML Class which is based on E4X, or true integration of XML in Actionscript. Gordon Smith said it best “Compared with the current XML support in the Flash Player, E4X allows you to write less code and execute it faster because more processing can be done at the native speed of C++.”

After playing with it and doing a few benchmark tests I noticed it wasn’t any faster than the XML object of AS 2.0. Maybe the E4X support isn’t fully optimized in the Flash 9 beta, really this shouldn’t matter because Flash Player 9 is final. But really, who cares, we’re talking milliseconds here, it’s like trying to figure out if you or your friend blink faster. The real bonus of E4X is its simplicity and query like abilities. No longer will we have to depend on an XPath implementation in Actionscript to query xml data. XPath was very slow and very processor intensive with it’s multitude of nested loops. I have seen XPath take down many of browser in my day.

Enough reminiscing, lets get back to E4X. XML nodes can now be drilled down using dot notation, just like a regular object in actionscript. There is also an attribute identifier operator (@). Take a look at the following simple example:

var myXML:XML =
<items>
     <item id="RedButtons" width="300">
          <label>My Red Label</label>
     </item>
     <item id="BlueButtons" width="200">
          <label>My Blue Label</label>
     </item>
     <item id="GreenButtons" width="250">
          <label>My Green Label</label>
     </item>
     <item id="RedButtons" width="400">
          <label>My Other Red Label</label>
     </item>
</items>;

//an XMLList is a subset of XML data
var myList:XMLList = myXML.item.(@id == "RedButtons");

//the new way of writing for each loops
var count:int = 0;
for each (p in myList) {
     trace(' - ' + myList[count].@width);
     count++;
}

//or a standard for loop
var len:Number = myList.length();
for (var i:int = 0; i<len; i++) {
     trace(myList[i].label + ' - ' + myList[i].@width);
}

So, what’s the first thing you noticed. That’s right, no more firstChild.childNodes, etc. Thank goodness! Finally an XML object is treated like a regular object.

As you can see the new XML class is called XML, the AS 1/2 XML class is now called XMLDocument. If you attempt to use legacy code and you compile to AS 3.0 make sure you update the class name first.

In the real world you would probably load in dynamic XML, but you will notice I simply typed in a simple snippet of XML and the compiler didn’t puke. Handy! So my first query which returns an XMLList (another new class) which contains only the item nodes with an id attribute that equals RedButtons. Very similar to XPath. But much quicker because it is native to the player now. Using my XMLList, that I so creativly named myList, I perform two seperate loops. A for each loop and a standard for loop. A couple things here you’ll notice, the syntax on the for each loop has changed slightly. Previously you simply wrote:

for (p in myList) {
     trace(p + ':'  + myList[p]);
}

With AS 3.0 you now have to add in the word each.

In my standard loop you will notice I have typed my index (i) as an int. It is good to use an int when you don’t need a floating point number such as a loop index. You would think this would increase performance, but really it doesn’t. You will also notice my len var equals myList.length(). length is a method so make sure you remember the brackets.

Inside both loops all I am doing is tracing out data. The only thing special here is how I access the attributes using the @ operator.

Well there you have it. Very simple example I know. I’m not sure which I am more excited about, this or the Regular Expression support in AS 3.0. Either way, I can’t wait for my clients to start letting me use it!

Jul

9

My blog, now with comments…finally.

By scott

In my quest to make things more difficult for myself and roll my own blog using php, sql, and my flash CMS, I have finally finished the comments functionality. Not bad for a Flash Developer ;) Let me know what you think.