svndiff2html: Convert SVN Diffs to More Readable HTML

Recently I wanted an easy way to generate diffs from svn that I could easily read -- i.e. with some sort of markup. TextMate automatically displays diffs in an attractive manner, and in a manner that makes them much easier to decipher visually. It also allows you to export any code file as html, which is also nice; however, I didn't want to keep having to open all my diffs in TextMate just for the sake of formatting them. I also wanted to be able to generate them quickly and be able to distribute them as standalone files that could ostensibly be viewed in a web browser.

I did a little research and found a script over at http://www.linuxjournal.com/content/convert-diff-output-colorized-html which was fairly close to what I wanted to do, so I started with that and modified its processing algorithm and css styling to match my TextMate output. After getting that working with static svn diff output files, I added the ability to simply navigate to the desired directory in the command line and invoke the script to output the html formatted diff.

At that point, I decided that there were 2 key comparisons that were important to me generally -- svn diff -r HEAD and svn diff -r PREV:HEAD -- which output the diff for the local file to the repository version, and the diff for the head as compared to the previous revision. In other words, one compares the stuff on your machine to the repository, and the other can compare what you just committed to what was there before. I added those, and a help command, and that become svndiff2html. I hope this helps someone save some time. Code follows after the break...

Read the rest of this post »

Pass flashvars from an embedded swf to a loaded swf.

This is somewhat trivial though not entirely intuitive -- passing flashvars from an embedded flash movie to a flash movie that the swf is loading. I was working on some code recently that accomplished this task using one solution that I have seen most often -- by passing the flashvars as query string parameters. In other words:

SwfA.swf is embedded on the page and has the flash var "userName" with the value "Voytek". SwfB.swf is loaded by SwfA.swf, and must utilize the userName variable. Often this will be accomplished by loading SwfB using the URL "SwfB.swf?userName=Voytek". This works just fine for most cases. There are, however some cases where this can be problematic. For example, suppose the site this is to be deployed on is a high traffic site and a caching system is employed to cache the swf files. Depending on the caching server, it may be that files are cached according to their *full* URL, which means that if we now need to load the swf with the userName "Milgrim", we have cached the file a second time referenced by the URL "SwfB.swf?userName=Milgrim". Now deploy this on a site with hundreds of thousands of users, and you might as well not even cache the swf files at all.

Fortunately, it came to my attention that the loaded swf SwfB can access the flashVars just as easily as SwfA, it just so happens that that action of getting the flashVars needs to be made asynchronously. As long as the loaded swf SwfB has already been added to the stage, it can access the flashVars via "stage.loaderInfo.parameters" (rather than accessing the flashVars in the embedded SwfA using "root.loaderInfo.parameters"). In order for this to be possible however, SwfB must be loaded and added to the display list of the stage.

As you can see from the code above, SwfA just loads SwfB just like you would normally do. SwfB just listens for its own ADDED_TO_STAGE event and then grabs the flashVars from the stage's loaderInfo. The only catch is that if the UI build or some other immediate process depends on the flashVars, then you need to make sure and wait for the ADDED_TO_STAGE event in order to proceed.

Translate Mac line endings to Unix for grep

Today I was attempting to run a grep search on all the *.as files in the project code I am working with. The code in places is both old and new and has been edited in a variety of editors, from the Flash IDE, to TextMate, to Flash Builder, to who knows how else. The unfortunate result of this history is that many of the files have Mac style line endings (\r) instead of Unix style line endings (\n). When trying to run a grep search on these files, the search returns a bunch of jumbled code salad.

Since I was only really wanting to do the search for now, I ended up using:

tr '\r' '\n' <filename.txt | grep searchstring

This works by using the transform command to convert the mac line endings, and pipe out the result to a grep search that is now happy. For more permanent results, the line endings could be replaced entirely using:

perl -pi -e 's/\r/\n/g' textfile.txt

The "tr" command, however, was a great temporary work around for running the search on the files that I needed to run, since I didn't want to end up having to recommit a bunch of files to SVN just to change the line-endings. The original jumbled search result I was getting was somewhat inexplicable until I started analyzing the reason why it worked for some files and not for others. Hope this helps someone out there.

ExternalInterface.available and Security Errors

Something I suppose I had never really tested thoroughly before having not had to deal too much with random application embeds in the wild is the issue with ExternalInterface.available not quite doing what you might expect. According to Adobe documentation, this property

Indicates whether this player is in a container that offers an external interface. If the external interface is available, this property is true; otherwise, it is false.

This is usually fine, as long as your embed has allowScriptAccess defined to either "always" or "sameDomain" (when the embed lives on the same domain as the swf). In addition, when prescribing embed code for folks to use, one would normally provide the embed code with allowScriptAccess set to "always" if there was a need to use ExternalInterface. Unfortunately, the ExternalInterface.available property will return true in cases where the allowScriptAccess parameter is not set to allow access (such as on Facebook flash embeds, where it is always rewritten to "never") or when an industrious embedder has removed all the seemingly extraneous parameters from the embed.

The solution for this scenario is to always use a try-catch statement to trap any errors that occur when trying to access the ExternalInterface, and deal with the error accordingly. This might look something like:


    if (ExternalInterface.available) {
        try {
            ExternalInterface.addCallback("moo", onMoo);
        } catch (error:SecurityError) {
            trace("Error: " + error.toString());
        }
    } else {
        trace("ExternalInterface is not available for this container.");
    }

This is not super pretty, but this will ensure that the app doesn't generate any unexpected behavior in the wild.

Testing out code markdown for posterous...

Here is the code:

/**
 * Calls a javascript method to launch a URL.
 *
 * @return
 *      Returns <code>true</code> if the code was able to execute.
 */
public static function openWindowWithURL(launchURL:String, w:int = 960, h:int = 593):Boolean {
    var windowName:String;
    try {
        // This tries to open a window via javascript, which could fail in 
        // some cases, such as when the swf is embedded on facebook, or when
        // the embed has specified that scripting is disabled.
        windowName = "newLaunchedWindow" + (new Date()).time;
        var winConfig:String =
            'width=' + w +
            ', height=' + h +
            ', toolbar=yes, location=yes, directories=yes, status=yes' +
            ', menubar=yes, scrollbars=yes, resizable=yes';
        var jscommand:String = "window.open('" + launchURL + "','" +
            windowName + "','" + winConfig + "');";
        var url:URLRequest = new URLRequest("javascript:" + jscommand + " void(0);");
        navigateToURL(url, "_self");
        return true;
    } catch (e:Error) {
        windowName = null;
        trace("Error occurred opening URL via JS.");
        return false;
    }
    return false;
}