Yes! Score one for the good guys. The buttons on the wiimote apparently gives out key events in Javascript, so it likely does the same for Flash. Read more on it here.

Update: Apparently Flash doesn’t get the key events, although Javascript does. Still, this is the beta version of the Internet Channel, it’s likely an oversight.

Update 2: There’s a clever solution out now from Mario to send key events to flash in a rather interesting way.

User Agents and You

December 18th, 2006

I tried to visit a website today that blocked me for using a browser it hadn’t heard of. Why do web designers who are aware enough to know how to access a browser’s user agent only seem to use it to block working browsers from their site? Even Google does it! Let’s set the record straight just in case somebody needs it.

  • GOOD: Noticing a certain older browser doesn’t work with your modern website, so you redirect those who use it so they can upgrade.
  • BAD: Thinking that all browsers except for the few you’re aware of are probably incompatible, so it’s better to block them until they switch to something you are aware of. It could be a security problem if they access your site with an incompatible browser so detecting the user agent is a perfect way to prevent this.
  • GOOD: Relying mostly on CSS and XHTML to ensure that your website will be easy to view in all kinds of browsers (text browsers, older cell-phone browsers for example).
  • BAD: Since your site is table-heavy you change the content based on the user agent (or even possibly the language) to ensure that it can be viewed everywhere. Unless your site is frequently updated it will eventually become outdated and improperly handle newer browsers.
  • GOOD: A specific browser has a bug, so you redirect it when it’s that specific version. That or alter your site so it continues to work while keeping it compatible with other browsers.
  • BAD: A specific browser has a bug, so you block that browser entirely. That or you block all versions starting with above the one with the bug since it will always have that bug.
  • GOOD: Assuming that the user agent can be faked, you develop future-proof ways to detect browser features.
  • BAD: Since browsers are required by law to deliver proper user agents, you use them to detect what features are available.

Basically, don’t change your content based on what browser is accessing it unless that specific version has a problem. Assume future versions will not. Don’t use the user agent in Javascript to detect what features are available. The key word is future-proof, just deliver standardized content and hope for the best. If you really want to do any of the bad things on that list, then at least allow people to bypass it somehow.

Shell Scripting

December 13th, 2006

This is the first shell scripting guide I’ve seen that doesn’t suck. It’s worth it to every programmer to learn shell scripting, bash files, unix/linux command line, blah blah blah whatever. It’s all the same. After overcoming your initial disgust and/or horror you’ll come to realize something, it’s actually pretty useful. The Linux programmer types are evil, to be sure, but they really know how to get the job done when you want to be hardcore about it. Just in case you don’t know, Windows users can use Cygwin to bring the Unix command line experience to them.

The most important thing to know is that the language used in the command line is the exact same thing as a shell script. That’s all a shell script really is, line after line of commands as if somebody typed them one after the other. I enjoy it so much now that I run my website entirely through a command line (frontends are for suckers) and it’s actually much easier in the long run once you get over the learning curve.

Fast Array-Searching Algorithm

December 4th, 2006

A while back I tried to solve a simple problem, first pick a completely random integer, then what’s the fastest way to guess it? That is to say, using the fewest amount of chances. You only have one advantage, when you guess, you are told if the number you guessed is <= or > the number you a searching for.

The answer is pretty simple. Let’s say the number is 481, all you need to start with is a really big number that is greater than what you’re looking for, let’s say 1000. Cut it in half, is it still greater than the number? Yes, so now you know that the number is >= 0 and < 500. Cut it in half again, and you’ll find it’s <= the number you’re searching for. Now you know that the number is >= 250 and < 500. Continue with this like so…

250 >= x < 500
Guess: 325 (it's <=)
325 >= x < 500
Guess: 412 (it's <=)
412 >= x < 500
Guess: 456 (it's <=)
456 >= x < 500
Guess: 489 (it's >)
478 >= x < 489
Guess: 483 (it's >)
478 >= x < 483
Guess: 480 (it's <=)
480 >= x < 483
Guess: 481 (it's <=)
481 >= x < 483
Guess: 482 (it's >)
481 >= x < 482

At the end, you’ll notice there’s only one number that’s >= 481 and < 482, the number you’re looking for.

In case you didn’t notice several numbers above were rounded. This always bothered me as it seemed untrustworthy, inheriting possible problems from floating point number math. Then I realized this issue could be overstepped and made much faster through the use of binary math. With a binary math technique you’d only work with multiples of two, so there are never rounding issues. First you need to find the highest multiple of two that is above the number you are searching for, in this case, we’re trying to find 1683.

Guess: 00000000 00000001 (1) (true)
Guess: 00000000 00000010 (2) (true)
Guess: 00000000 00000100 (4) (true)
Guess: 00000000 00001000 (8) (true)
Guess: 00000000 00010000 (16) (true)
Guess: 00000000 00100000 (32) (true)
Guess: 00000000 01000000 (64) (true)
Guess: 00000000 10000000 (128) (true)
Guess: 00000001 00000000 (256) (true)
Guess: 00000010 00000000 (512) (true)
Guess: 00000100 00000000 (1024) (true)
Guess: 00001000 00000000 (2048) (false)
Answer: 2048

Now we know the first multiple of two that is higher than the number is 2048. Now finding the number between the current minimum/maximum is much simpler through the use of binary math.

00000100 00000000 => 1024 (start)
00000110 00000000 => (1024 | 512) == 1536 (true)
00000111 00000000 => (1536 | 256) == 1792 (false)
00000110 10000000 => (1536 | 128) == 1664 (true)
00000110 11000000 => (1664 | 64) == 1728 (false)
00000110 10100000 => (1664 | 32) == 1696 (false)
00000110 10010000 => (1664 | 16) == 1680 (true)
00000110 10011000 => (1680 | 8) == 1688 (false)
00000110 10010100 => (1680 | 4) == 1684 (false)
00000110 10010010 => (1680 | 2) == 1682 (true)
00000110 10010011 => (1682 | 1) == 1683 (true)
Answer: 1683

You’ll notice every time the result is true, the bit that is currently being tested stays in the result, otherwise it returns to zero.

Next is a simple example of how to use this technique. The goal of this code is to insert a number into an Array in numerical order. That is to say, each number in the Array should be greater than (or equal to) the numbers that precede it. Also keep in mind even if there is only one entry (length == 1) there are two possible places to put the new number, before and after the lone entry.

function addEntry(entry:Number):void {
var cBit:uint = 1;
while (cBit < arr.length) {
cBit <<= 1;
}
var pos:uint = 0;

do {
if ((pos | cBit) <= arr.length) {
if (entry >= arr[(pos | cBit)-1]) {
pos
|= cBit;
}
}

} while ((cBit >>>= 1) !== 0);
arr.splice(pos,0,entry);
}

That’s the simplified version, get a more optimized and complete example here. The great thing about this method is that it can be used to keep (or find) just about anything sorted. I’d also like to note that while I did actually think this up on my own, I’m definitely not the first. Not by a long shot. The real name for it appears to be Binary Search Algorithim.

Website Patches

October 8th, 2006

Back in my Javascript days I realized bookmarks have no cross-site security issues. That is to say, if you click on a bookmark that is a javascript: URL, you can insert your own code into the site. I added a bunch of “features” to my browsing experience through special bookmarks, most of which are now standard in browsers these days (my favorite was the one that automatically reloaded the page every 30 seconds). I called them website patches.

Here’s a new website patch for you (bookmark it, it doesn’t work on this blog) from Digg. Try it on sites with lots of images, like Fark.

I present to you a short but sweet example of how to create and prepare zip files automatically for FZip with Eclipse using Ant. Have Python installed.

<zip destfile="bin/files.zip" compress="true" keepcompression="true" update="true">
<zipfileset dir="files/images/" prefix="images" />
<zipfileset dir="files/text/" prefix="text />
</zip>
<exec executable="C:/Python24/python.exe" failonerror="true">
<arg file="tools/fzip-prepare.py" />
<arg file="bin/files.zip" />
</exec>

Change the executable to wherever Python is located on your computer. It should work on Linux or a Mac as well.

For reasons unknown to me there is no option to remove unused files from a zip using ant’s built-in zip task, instead it only updates/adds files. In other words if you remove a file from the source folder, it will stay in the updated zip file anyway. A “clean” target comes in handy when you want to make sure there’s no extra files hanging around.

<target name="clean" description="Cleanup files">
<delete file="bin/files.zip"/>
</target>

A while back I lamented the lack of attention towards decompression in Flash on Flashcoders. Claus Wahlers agreed.

FZip – Open-source Actionscript 3 zip library.

I developed a tool that added a small bit of information (an Adler32 checksum, as standard zip files only have a CRC32 checksum) into a zip file, and using some code that Claus developed for the new version of Deng, we successfully made a working unzip tool in Flash. Claus has more on how to use it.

The tool itself requires Python, which may seem like an annoyance, until you realize it’s not Windows-only and not that big of a hassle. It can easily be combined with a those ant buildfiles. I’ll try to cover more on this in the future, but for now, download and enjoy.

Backup

August 28th, 2006

I don’t trust computers to not suddenly explode and destroy everything I’ve ever created, and I’ve discovered what I believe to be the easiest and most foolproof way to backup my code, or other things for that matter. Email it to yourself automatically.

I use Subversion to manage my personal code library on a remote server, and since my goal is to always have everything important exist in two separate places, I have a compressed dump of the entire repository emailed to myself weekly. I delete the older versions, but since my email account has plenty of free space it’s not very urgent. This all seems straightforward, but I had to do a lot of research to find the right program to use, which I am now sharing with you. All you need to do is set up a cron job like this…


0 6 * * 0 (svnadmin dump ~/svn/repo/ | gzip --best > ~/svn/repo-backup.svndump.gz) 2>&1 | mutt -s 'repo-backup.svndump.gz '$(date +%m/%d/%Y) -a ~/svn/repo-backup.svndump.gz youremail@email.com

Obviously, replace the path to the repository and the email address to correct values, and put the backup file somewhere too. This particular job is scheduled to run at 6am on Sunday morning. The repository (~/svn/repo) is dumped and compressed (using gzip, at the highest level of compression) to the file "~/svn/repo-backup.svndump.gz". I use mutt to send the email with the file attached. The subject is the name of the file and the date the email was sent (to avoid grouping backup emails together). I also want messages that svnadmin outputs (which contain information on what was dumped, and possible error messages) to be the body of the email, but svnadmin sends the messages through stderr while mutt receives the body of the email through stdout. To fix this stderr is redirected as stdout after stdout has been used to compress the dumpfile. Isn’t Unix great?

Anyway, if you use Gmail, besides a lot of space to manage these backups, you can also make the process easier using plus-addressing. Let’s say your email is "bob@gmail.com", then set the email address in the example to "bob+backups@gmail.com". Set up a label called "Backups" in Gmail and a filter which automatically labels emails as "Backups" when they have been sent to "bob+backups@gmail.com". You can keep them out of your Inbox if you archive them with the filter as well.

Some other important things…

  • Keep in mind there are size limits for attachments in emails, but this isn’t much of a problem as long as your repository stays fairly small (and full of text files).
  • You don’t need to send them to one address, check the manual page for mutt for more information.
  • Using bzip2 rather than gzip would be a better idea, as bzip2 has recovery features if something goes wrong, but currently Gmail has a major bug when viewing emails with bzip2-compressed files attached. These emails are improperly handled and won’t open through Gmail.
  • I prefer to use a svnadmin dump since I won’t be making any commits at exactly that moment, but more active repositories should probably dump a copy of the repository using svnadmin hotcopy instead as a safety precaution.

The future of computers

August 25th, 2006

Amazon is onto something. It’s like a VPS on steroids. I’m not joking when I say that this is the future of the Internet hosting, or more accurately, computer processing in general.

Zlib experiments

August 11th, 2006

I’m getting tired, but I have the following for you all, a working sample of this whole compression thing that I’ve become interested in lately. You’ll need Python for this. You should also know how to compile and debug an AS3 class. Save the following code sample as “zlib.py” and put it along with the file you wish to compress somewhere. The file you want to compress should be a text file for this example, although any file will work.

[Python]
#!/usr/bin/env python

import zlib
import os, sys

# Make windows output as binary
if sys.platform == “win32”:
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

if len(sys.argv) < 2:
sys.exit(“No filename given”)
else:
f = open(sys.argv[1], “rb”)

deflate = zlib.compressobj(9)

while 1:
buf = f.read(1024);
if buf:
sys.stdout.write(deflate.compress(buf))
else:
break;
sys.stdout.write(deflate.flush(zlib.Z_FINISH));
[/Python]

Open the directory where you put the files in the command line.

  • If you’re using Windows then type “zlib.py filename > compressed.zlib” at the command line. Replace “filename” with the name of the file you want to compress.
  • If you’re using Mac OS X (or another Unix OS) first make sure you can execute the file, then type “./zlib.py filename > compressed.zlib” at the command line. Replace “filename” with the name of the file you want to compress.

Save the following Actionscript 3.0 file as “Testify.as” and compile it.

[Actionscript]
package {
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.events.Event;
import flash.events.IOErrorEvent;

public class Testify {
static private var url:String = “compressed.zlib”;

private var urlLoader:URLLoader;

public function Testify() {
trace(“Loading “+url);
urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Eve​nt.COMPLETE,onComplete);
urlLoader.addEventListener(IOE​rrorEvent.IO_ERROR,onError);
urlLoader.load(new URLRequest(wadUrl));
}
private function onComplete(e:Event):void {
trace(“Loaded”);
var data:ByteArray = urlLoader.data;
urlLoader.data = null;
urlLoader.close();
urlLoader = null;

data.uncompress();

data.position = 0;
trace(data.readUTFBytes(data.b​ytesAvailable));
}
private function onError(e:Event):void {
trace(“Error”);
}
}
}
[/Actionscript]

Alright, now make sure the “compressed.zlib” is in the same folder as your SWF file, and use either the Flex debugger (flc.exe) or (if you’re using Eclipse) debug the class as an Actionscript project. Your output should be the original file.