Follow Me!

Posted on Feb 14, 2011

Writing an AIR Directory-Cleanup Script

Lately I had to write a little script that would automatically spot out and delete all empty folders from inside a given path.

This can be useful in some applications to get deprecated data folders that are not being used anymore!

I wrote this little snippet of code that recursively identifies the deepest nested folder to delete it if it’s empty, going back to the given root folder!

import flash.filesystem.File;

function cleanDirectory(dir:File):void
{
	var listing:Array;
	var temp:File;

	// fetching all listed Files
	listing = dir.getDirectoryListing();

	if (listing.length > 0)
	{
		for each (temp in listing)
		{
			// identifying directories
			if (temp.isDirectory) cleanDirectory(temp);
		}
	}

	// we need this to delete directories
	dir = new File(dir.nativePath);

	// after our recursive call - is the directory empty?
	listing = dir.getDirectoryListing();

	// if yes, delete the directory
	if (listing.length == 0) dir.deleteDirectory();
}

cleanDirectory(File.applicationDirectory); // app installation folder
cleanDirectory(File.applicationStorageDirectory); // app user storage

 
I hope some of you find this little snippet useful.
The usage of this cleanup-script can vary from application to application. How would you use it?

Posted on Jan 9, 2011

Embedding Fonts in AS3 / Flash Player 10

Embedding Fonts in AS3 is pretty easy.

With the Flex-Framework you can use a simple Embed to include an external ttf or otf font.

import flash.text.Font;

[Embed(source='MyFont.ttf',
       fontName="FontNormal",
       mimeType="application/x-font-truetype")]
var FontNormal:Class;
Font.registerFont(FontNormal);

 
You can even specify a specific character-range in the Embed-Tag:

[Embed(source='MyFont.ttf',
       fontName="FontNormal",
       mimeType="application/x-font-truetype",
       unicodeRange='U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E')]

 
To get all Fonts that are embedded you can use a simple loop like this:

import flash.text.Font;
var embeddedFonts:Array = Font.enumerateFonts();
for each (var embeddedFont:Font in embeddedFonts)
{
  trace(embeddedFont.fontName);
  trace(embeddedFont.fontType);
  trace(embeddedFont.fontStyle);
}

 
You can use the embedded Font as follows:

import flash.text.TextField;
var field:TextField = new TextField();
field.embedFonts = true;
field.setTextFormat(new TextFormat("FontNormal"));
field.text = "Hello World";

 
So far so good. This stuff all used to be working in Flash Player versions lower than 10.
tracing out the properties of all your embedded fonts you might notice a property telling “embedded CFF”.

CFF (Compact Font Format) has been introduced with Flash Player 10 and can only be used with the new Text Engine. Thus you have to explicitly disable it in the Embed-Tag. To do so simply add embedAsCFF=”false”

[Embed(source='MyFont.ttf',
       fontName="FontNormal",
       mimeType="application/x-font-truetype",
       embedAsCFF="false")]

 
This way you can use all your embedded Fonts on normal AS3 TextFields!

Happy Coding!

Posted on Jan 8, 2011

Global Functions in AS3

Writing global functions in AS3 is much easier than you may think.
It’s so easy that a lot of people forget to think about it…

simply create an AS3-File, give it the same name as your function should have and put it on your root project directory meaning your file is set to no exact package!

Example

filename: myTrace.as

package {
  public function myTrace():void {
    trace("my trace");
  }
}

 
 
This way you are able to even create kind of a jQuery-style Tweening engine:

package
{
	import com.firsara.animation.Tween;

	public function $(obj:*, ease:* = null):Tween
	{
		return Tween.append(obj, ease);
	}
}

 
 
Still I encourage you to put every single global function in an appropriate file to stay with the internal AS3 definition logic that tells you that every definition – being it a class, a global Variable or a simple function – in a seperate file!

Posted on Jan 7, 2011

How to write an AIR Zip file unpacker using the nochump library

I’ve recently had to write an unzipper for updating local files of an AIR-app!
Searching the internet I’ve found some very powerful unzipper libraries.

First I’ve tried it with the AS3 FZip library.
Although it worked pretty well in the beginning and no errors occured, I got the problem that this library wasn’t able to unzip correctly meaning that I got a lot of broken files!

So I tried it with the well known nochump-library which turned out to be a very good solution to my problem!

First off, get the nochump zip-libraries, as you will need it for this Unpacker Class: nochump zip library

The core functionality of this class is, that you are easily able to load a zip file from the internet and unpack it to your local filesystem.
 

usage

To use this class simply create a new instance and call the unpack function!

import com.firsara.utils.Unpacker;

unpacker = new Unpacker("output_directory");
unpacker.unpack("http://www.example.com/path_to_your_zip_file.zip");

unpacker.addEventListener(ProgressEvent.PROGRESS, showProgress);
unpacker.addEventListener(Unpacker.UNPACK_FILE, showUnpack);
unpacker.addEventListener(Event.COMPLETE, unpackComplete);

As you can see above you can also watch for three events.
The First one shows you the current download progress of your zip file.

You can access the progress through

unpacker.percentage

The second events gets dispatched when a file is being unzipped.
Even though this process does not take long it can still be interesting to display the current File that gets unzipped. To get the current File you can output

unpacker.currentFile

in your event handler!

That’s quite all you need to know…
The complete class looks like this:
 

Unpacker class

package com.firsara.utils
{
	import nochump.util.zip.*;
	import flash.utils.setTimeout;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.EventDispatcher;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.URLRequest;
	import flash.net.URLStream;
	import flash.net.URLLoader;

	public class Unpacker extends EventDispatcher
	{
		public static const UNPACK_FILE:String = "unpack";

		private var _directory:String;
		private var _currentFile:String;
		private var _percentage:Number;
		private var _stream:URLStream;
		private var _zipFile:ZipFile;

		public function get percentage():Number  { return _percentage; }
		public function get directory():String   { return _directory; }
		public function get currentFile():String { return _currentFile; }

		public function unpack(url:String):void
		{
			if (url.indexOf('http://') < 0) url = 'http://' + url;

		 _stream = new URLStream();
		 _stream.load(new URLRequest(url));
		 _stream.addEventListener(ProgressEvent.PROGRESS, showProgress);
		 _stream.addEventListener(Event.COMPLETE, extract);
		}

		public function Unpacker(dir:String):void
		{
			_directory = dir;
			_percentage = 0;

			if ((_directory != "") &&
					(_directory.charAt(_directory.length - 1) != "/"))
			{
				_directory += "/";
			}

			_directory.replace(/\\/gi, "/");
		}

		private function showProgress(event:ProgressEvent):void
		{
		 _percentage = Number(event.bytesLoaded / event.bytesTotal);
		 dispatchEvent(event);
		}
		private function dispatchComplete():void
		{
			dispatchEvent(new Event(Event.COMPLETE));
		}

		private function extract(e:Event):void
		{
		 _stream.removeEventListener(ProgressEvent.PROGRESS, showProgress);
		 _stream.removeEventListener(Event.COMPLETE, extract);

			_zipFile = new ZipFile(_stream);
			extractFile(0);
		}

		private function extractFile(id:int):void
		{
			var applicationDirectory:File = File.applicationDirectory;
			var filePath:String;
			var zipEntry:ZipEntry;

			zipEntry = ZipEntry(_zipFile.entries[id]);
			filePath = applicationDirectory.nativePath.toString();
			filePath += ("/" + _directory + zipEntry.name);

			_currentFile = zipEntry.name;

			var storage:File = new File(filePath);
			if (zipEntry.name.charAt(zipEntry.name.length - 1) == '/')
			{
				storage.createDirectory();
			}
			else
			{
				var entry:FileStream = new FileStream();
				entry.open(storage, FileMode.WRITE);
				entry.writeBytes(_zipFile.getInput(zipEntry));
				entry.close();
			}

			dispatchEvent(new ProgressEvent(UNPACK_FILE));

			if (id >= _zipFile.entries.length - 1) dispatchComplete();
			else setTimeout(extractFile, 50, (id + 1));
		}

	}
}

 

Example

Here’s an example of how the class can be used in order to display Progress:

package
{
	import com.firsara.utils.Unpacker;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.display.Sprite;
	import flash.text.TextField;

	public class Main extends Sprite
	{
		private const ZIP_PATH:String = "http://nochump.com/blog/wp-content/uploads/2008/11/nochump-ziplib-105-src.zip";
		private const OUTPUT_DIR:String = "_nochump_src/";
		private var unpacker:Unpacker;
		private var output:TextField;

		public function Main()
		{
			if (stage) construct();
			else this.addEventListener(Event.ADDED_TO_STAGE, construct);
		}

		private function construct(event:Event = null):void
		{
			if (event) this.removeEventListener(Event.ADDED_TO_STAGE, construct);

			output = new TextField();
			output.width = this.stage.stageWidth - 100;
			output.height = this.stage.stageHeight - 100;
			output.x = output.y = 50;
			addChild(output);

			unpacker = new Unpacker(OUTPUT_DIR);
			unpacker.addEventListener(ProgressEvent.PROGRESS, showProgress);
			unpacker.addEventListener(Unpacker.UNPACK_FILE, showUnpack);
			unpacker.addEventListener(Event.COMPLETE, unpackComplete);
			unpacker.unpack(ZIP_PATH);
		}

		private function showProgress(event:ProgressEvent):void
		{
			var p:String = String(Math.round(unpacker.percentage * 100));
			if (p.length == 1) p = "0" + p;
			output.text = "Unpacking " + p + "%";
		}

		private function showUnpack(event:ProgressEvent):void
		{
			output.text = "Unpacking file \"" + unpacker.currentFile + "\"";
		}

		private function unpackComplete(event:Event):void
		{
			output.text = "Unpacking in \"" + unpacker.directory + "\" done ";
			unpacker.removeEventListener(ProgressEvent.PROGRESS, showProgress);
			unpacker.removeEventListener(Unpacker.UNPACK_FILE, showUnpack);
			unpacker.removeEventListener(Event.COMPLETE, unpackComplete);
		}

	}

}

 

Download

Download the full class including a simple example of how it can be used: UnpackerSample.zip

Note that this class will only work in AIR as it uses “File” and “FileStream”!
If you are able to use this class in your projects please give me credits!

What do you think about this class?

Posted on Dec 30, 2010