I wanted to share some quick notes on how to get JavaScript code assist "working" under the latest version of Aptana Studio.

Note: As far as I can tell, this feature is very buggy, and as such, YMMV.

There are three features that I find particularly desirable:

  • Jump-to-definition: A Command-Click opens the file to the location where the highlighted function (etc) is defined. Probably the most important / useful.
  • Code-completion: a drop-down of possible entries appears as you type / upon pressing Control-Space.
  • Code-hints: Class constructor and method signatures appear as you type / upon mouse hover, displaying helpful information about method signatures & parameters.

Sounds pretty awesome, huh? Too bad it doesn't work as advertised. Let's talk about what's busted.

  • The SDocGen tool only seems to catch classes that are defined globally, so if you're organizing your functions (as you should) into requirejs define calls (for instance), your comments won't get picked up.
  • Using a @return tag inside a constructor function will seem to work ok, but it tramples the class's method definitions, so I avoid it in that context. The same @return tag inside method functions doesn't seem to have this issue.
  • The opening documentation tag is "/**" (Note: that's 2 asterisks). The closing documentation tag is "*/". Nothing else seems to work.
  • I've seen code hints (eg for google maps) that seemed to work using the object-literal syntax. I never got this to work. Lucky for me, I don't define my classes that way.

That first bullet is nearly impossible to work around, and I eventually quit trying. Instead, I hacked around it by appending the following structure to the bottom of my file:

if (false)
{
  MyClass = function (xyz)
  {
    /**
      Documentation here
      @param {String} xyz This text appears as a hint while you enter the xyz parameter
    */
  }

  MyClass.prototype.method = function ()
  {
    /**
      Method documentation here
      @return {Number}
    */
  }
}

Unfortunately, that means repeating yourself a bit, but everything seems to work. The "if (false)" pattern keeps this code from executing at runtime, but SDocGen will pick up your comments for documentation. When you Command-click (I'm on OS X) a class or method name in your code, it will jump to the first occurrence in the definition file, ie the correct one. Provided you're defining one class per file, you shouldn't have any collisions.

I discovered that a limited set of HTML tags are supported. I've had success with UL/LI, STRONG, EMPHASIS, BR, TABLE/TR/TD. I was even able to get a simple style-sheet to work, but that was nearly useless. The only thing that worked reliably was colors, and it was necessary to omit the '#' from my hexadecimal colors. HTML did not work for method-parameter pop-up help. I'll probably avoid this feature for now.

SDocGen is available on github; maybe someone can fix it?

Good luck!

HTML5 Portfolio Project – Part 2

Jump to Part 1 here.

Today, we're going to define the data format for our portfolio. Back in 2005, XML was the last data format we were ever going to need, so that's what I used. It must have been second-to-last, because today, everyone seems to prefer JSON. I'm more than happy to commiserate about the pros and cons over beers sometime, but I've moved on. We're doing this in JSON. Afterall, the portfolio is going to be driven by JavaScript; I doubt much justification is warranted.

Don Your Information Architect Hat

A portfolio is a list of projects. Each project has a bunch of properties like a title and a URL. Each project will also have some media (like pictures and/or video) and a brief, text-based description.

Here's what I came up with originally:

  • client - eg. Microsoft
  • title - eg. Micro-site
  • description - eg. "Lorem ipsum..."
  • date - eg. January 2011
  • role - eg. developer
  • link - eg. http://microsoft.com/someMicroSite
  • thumbnail - eg. thumbnails/3d-templates.jpg
  • media - eg. microsite/image1.jpg, microsite/image2.jpg, microsite/image3.jpg, microsite/video.mov

All fairly straight-forward, I hope.

I did over-simplify the items in the media section. In order to make this work, we need to know a little more than just what URL has the media.

  • title - eg. Image 1
  • icon type - eg. pic / video
  • content - eg. microsite/image1.jpg

Turning all of this into JSON should be easy. First, double-check to make sure our names make sense and are consistent. ... Then, we need to decide whether it makes sense to encode items with more than one value as an Array or as an Object (hash). There are two spots in this project where this comes into play. One should be obvious. Can you spot the second?

  1. The list of projects
  2. The list of media belonging to each project

Above all else, it pays to be consistent. The two items above are similar enough that, whether we choose to store them in an Array or an Object, we'll probably want to use the same approach for both.

Array is Best

You'll want to use an Array when you have a list of things and their order matters. We want to display projects in a specific order, so let's use an Array. That was easy! Wait...

No, Object is Best

You want to use an Object when you have a list of things and the order doesn't necessarily matter, and each item can be identified by some sort of unique key. An Array (a type of Object afterall) often does this automatically. It uses numeric keys.

Come to think of it, each of our projects will be accessible via a unique URL. And, hey, the media items will have URLs as well. But wait, we didn't show "url" anywhere in our list of properties. Where should it go? If I use an Object to store my list items, I could use the URL fragment as the key, or I could go back to the Array (preserving the order of my list) and add a "url" property to each item...

Just Pick One Already

Hopefully you're starting to see that there's no one right way to do this. I want to be able to control the order of my items, but I also need to access each item by a unique key (its URL). Here's a sample of what I think my data will look like (so far).

[
  {
    "url"         : "contact",
    "client"      : "Email:<br />jon@shovemedia.com",
    "title"       : "contact info",
    "description" : "content/contact.html",
    "date"        : "01/01/2011",
    "role"        : "developer",
    "link"        : "mailto:jon@shovemedia.com?subject=New Business",
    "thumbnail"   : "thumbnails/email.jpg",
    "pics"        : []
  },
  {
    "url"         : "3D",
    "client"      : "Photobiz",
    "title"       : "3D Templates",
    "description" : "content/3d.html",
    "date"        : "01/01/2011",
    "role"        : "developer",
    "link"        : "http://photobiz.com",
    "thumbnail"   : "thumbnails/3d-templates.jpg",
    "pics"        : [
            {
              "url"     : "video",
              "title"   : "watch!",
              "icon"    : "video",
              "content" : "papervision/videoPlayer.html"
            },
            {
              "url"     : "image1",
              "title"   : "Image 1",
              "icon"    : "pic",
              "content" : "papervision/image1.jpg"
            }
          ]
  }
]

Most of this is identical to the XML format I've been using. I've introduced one small change for the "description" field. Rather than trying to embed HTML within a JSON document (and spending a tutorial or three to explain it), I'm going to place that in an external document and load it on request. That's "lazy initialization" for the initiated.

Stay Tuned:

In the next episode, we'll decide how best to organize everything into folders at the file system level. If you take a close look at the JSON above, you'll should be able to spot a hint or two in some of the example values that contain file paths.

HTML5 Portfolio Project – Part 1

Introduction

I built a Flash-based portfolio back in 2005. It was itself a nice example of the kind of work that kept new projects coming: back-button & bookmarking support, raw content visible to Google & non-Flash browsers, Google Analytics tracking, easily updatable via an XML file, etc. All at a time when most people believed it couldn't be done. For better or worse, it's functional enough that it still drives my portfolio today.

In the seven years since, browsers have done a great deal to catch up. Canvas, web fonts, and CSS enhancements have expanded the possibilities so that I'm confident I can rebuild the whole thing in HTML5. I still get compliments on the minimalist design, and I think I'd get even more if it worked on iPads and iPhones. ;)

This move is long overdue, and I've decided, not only does it make sense to open source the final template, but there's value in letting you follow my process. I will make mistakes, but I'll probably learn something from them, and in turn, so might you.

The Plan

We won't be doing any coding today. Instead, I'll start where all projects ought to start: with a plan. The numbers in parenthesis represent the estimated number of hours required.

  • (1) Project Plan
  • (1) Define JSON format
  • (1) Assemble project file structure
  • (4) Scale transition
  • (4) Navigation
  • (4) Canvas-based Tooltip
  • (6) Text in circle
  • (3) Media Player (video)
  • (2) Background gradient effect
  • (4) Preloader animation
  • (1) CSS / media queries
  • (1) Google Analytics integration
  • (1) Fonts (Typekit?)
  • (8) Bug Fixes

Total: 41 hours

These estimates don't include the time required to write up the accompanying blog-posts or answer your questions. That's probably another 15-20 hours. In addition, I usually multiply my initial estimate by two to account for the Murphy-factor. Also, I've got three other projects right now, and this isn't the most important, so it's going to take a lot longer than a week. Let's schedule launch for June 1st, that sounds nice, doesn't it?

Stay Tuned:

In the next episode, we'll define a JSON format for each project in our portfolio and start thinking about how those will get transformed in to richer JavaScript structures and HTML elements.

Smarty base href Modifier

It appears that Safari 5 will not respect the base href tag when used in a page served via https when the base href indicates a URL with the http protocol. There is probably more to this issue, but I ran across it while working on an Authorize.net integration using their SIM API. After a successful transaction, Authorize.net requests a URL of the developer's choosing, and proxies the response through their server to the user's browser. This allows developers to do callback processing of the successful transaction on their server, and display a custom "thank you" page. Brilliant. The problem is, if you have any included CSS, JavaScript, or images in the page, you either need to give them all fully qualified URLs, or use the base tag to specify a root URL to use for all relative link attributes in the page (href, src, action, etc.). I've done this same type of integration many times, going back about six years, and have never had a problem using the base href in this scenario. Now, Safari 5 will completely ignore the base href and display a big mess of un-styled markup when you reach the end of the Authorize.net transaction flow.

I do a lot of work with the Smarty template engine, and the site I'm working on now is large, complex, and has a fairly advanced layout with lots of CSS and jQuery goodies. Everything is so abstracted, it's just not practical to construct a single template for my Authorize.net relay pages, of which there are several. It's also not practical to change the templates to use fully qualified URLs for everything. So what I did was write a simple Smarty modifier that prepends a specified URL to all of the relative src, href, and action attributes within the supplied string. It also fixes background: url() inline CSS styles. On my relay templates, all I have to do is wrap the contents of the template in a {capture} function, assign the output to a variable, and run it through my modifier. Absolute paths for all. Here's the modifier code, hopefully it will save someone a few grey hairs.

<?php
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */

/**
 * Smarty base_href modifier plugin
 *
 * Type:     modifier<br>
 * Name:     base_href<br>
 * Purpose:  apply a base URL to all relative
 *				src, href, and background:url() attributes in a string
 * @author   Rob Ruchte <rob care of thirdpartylabs com>
 * @param string
 * @param string
 * @return string
 */
function smarty_modifier_base_href($string, $base_href)
{
	$base_href = (substr($base_href, -1)!='/') ? $base_href.'/':$base_href;

	$patterns = array();
	$patterns[] = '/href=\"[^#]\/?([^\"]*)\"/i';
	$patterns[] = '/src=\"\/?([^\"]*)\"/i';
	$patterns[] = '/action=\"\/?([^\"]*)\"/i';
	$patterns[] = '/background: ?url\(\'?\/?([^\'|\)]*)\'?\)/i';
	$patterns[] = '/background-image: ?url\(\'?\/?([^\'|\)]*)\'?\)/i';

	$replacements = array();
	$replacements[] = 'href="'.$base_href.'$1"';
	$replacements[] = 'src="'.$base_href.'$1"';
	$replacements[] = 'action="'.$base_href.'$1"';
	$replacements[] = 'background: url(\''.$base_href.'$1\')';
	$replacements[] = 'background-image: url(\''.$base_href.'$1\')';

    return preg_replace($patterns, $replacements, $string);
}

/* vim: set expandtab: */

?>

Usage:

{capture assign=output}
{include file="include/header.tpl" title="Event Ticket Registration"}
	<div class="span-3" id="leftbar">
		[...]
	</div>
	<div class="span-11 last" id="content">
		<div style="background: url(/images/feature.jpg);">
			[...]
		</div>
		<div class="span-11 last" id="copy">
		<div id="breadcrumbs">
			{foreach from=$breadcrumbs item=currNav name=breadcrumbs}
				{if !$smarty.foreach.breadcrumbs.last}<a href="{$currNav.path}">{/if}
				{$currNav.title}
				{if !$smarty.foreach.breadcrumbs.last}</a>&nbsp;&raquo;&nbsp;{/if}
			{/foreach}
		</div>
        <h2>Registration: {$event->getTitle()|escape}</h2>
	    {$purchaseSuccess}
        </div>
    </div>
{include file="include/footer.tpl"}
{/capture}
{$output|base_href:$smarty.const.BASE_URL}

,

Serving a Flash Socket Policy File From Processing

Last night I spent way too long trying to get AS3 to communicate with a simple socket server I wrote in Processing. I've done this kind of thing before and seemed to recall that it was pretty simple. But in the meantime, Adobe, in an effort to be more secure, has changed the Flash player to require a "socket policy file". The socket policy file is very similar to the familiar crossdomain.xml file that defines security permissions for HTTP access. Unfortunately, the socket policy file must either be sent on demand from the sockets that the player is attempting to access, or from port 843 on the host that the player is attempting to connect to. If all you want to do is run a quick and dirty socket server and have Flash clients connect to it, this is all a huge PITA.

Since ports below 1024 require root permissions in order for processes to use them on OS X, and I didn't want to run some other server process just to serve policy files, I needed to kludge a way to send the policy file from my nice, elegant socket server every time a client connected and requested a policy file. The example below is a simplified version, as it only listens on one port - the project I was working on was basically a proxy from one SWF to another, so I had two socket servers that needed to listen for the requests for a policy file and respond. In this example, the server listens for connections on port 5208, and simply echoes incoming data from the client to System.out, and has some visual feedback in the window. When the incoming message contains the string "policy-file-request" (the entire message from Flash will be terminated with a null char), we simply spit back the XML for a wide-open socket policy, followed by a null char (This is required, or the Flash player will not accept the policy file. This is what tied me in knots last night. RTFM!)

import processing.net.*;

int bgLevel = 0;
int port = 5208;
Server server;

String flashDomainPolicy = "<?xml version=\"1.0\"?>"
                              +"<cross-domain-policy xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd\">"
                              +"<allow-access-from domain=\"*\" to-ports=\"*\" secure=\"false\" />"
                              +"<site-control permitted-cross-domain-policies=\"all\" />"
                              +"</cross-domain-policy>";

void setup ()
{
  size(200, 200);
  server = new Server(this, port);
  background(bgLevel);
}

void draw ()
{
  Client client = server.available();

  if (client !=null)
  {
    String message = trim(client.readString());

    if (message != null)
    {
      if (match(message,"policy-file-request") != null)
      {
        sendFlashPolicy(server);
      }
      else
      {
        server.write(message);
        System.out.println(message);

        // Change the background color to indicate message activity
        bgLevel = 255;
      }
    }
  }

  // Fade to black
  if (bgLevel>1)
  {
    bgLevel-=2;
    background(bgLevel);
  }
}

void sendFlashPolicy(Server socketServer)
{
    socketServer.write(flashDomainPolicy+char(0));
    System.out.println("Sending Flash policy file");
}

You can test this without a Flash client by firing up the sketch and telnetting to the port. Any strings you send will be echoed to System.out, unless you send the string "policy-file-request" in your message, which will result in the server sending the policy XML to you. You should be able to connect a Flash client to this server and start communicating right away.

, , , ,

Recently, Blogger began appending a tracking gif to the content of each entry in their Atom feeds. The URL used in the image src uses https, most likely to avoid warnings when it's rendered in a https context. For some reason, when rendering the feed content containing the tracking image, the Flash player can crash, taking the browser with it on certain platform/browser combinations. We found the problem in FireFox 3.0 on OSX, but only on PPC Macs. Go figure.

In our case, we are proxying the Atom feed through a PHP script so we can display the feed contents to user agents without the Flash player. This made it fairly easy to iterate through the entries, and with a simple bit of regex, strip out the offending markup from the contents.

Blogger is wrapping the image tag in a div with a very specific CSS class, which makes our job easy:

foreach($feed->entries as $currEntry)
{
    
$currEntry->content ereg_replace('<div class=\"blogger-post-footer\">.*</div>'''$currEntry->content);
}

Depending on what you're using to parse the feed, you may or may not need to be concerned about decoding and encoding html entities during this process.

, ,

Automatic AMF class mapping in Zend (Part 2)

see Part 1

Polishing off automatic mapping of objects to the correct class from PHP (Zend) back to ActionScript turned out to be easier than I thought.

In addition to the more commonly used $_explicitType class property, Zend also supports using a function called getASClassName. You can either paste this into each of your remote class stubs, or modify the getClassName method in Serializer to do it automatically.

public function getASClassName() 
{
 return ">" . str_replace("_", ".", get_class($this)); 
}

Clearly you'll need to avoid underscores appearing package and class names. That may be true regardless if you're planning to take advantage of Zend's automatic class loading.

On the Flash side, if you're not using the Flex compiler, you can use this to automatically register class aliases to the correct string.

function autoRegisterClassAlias (localClass:Class):void
{
registerClassAlias(">" + flash.utils.describeType(localClass).@name.split("::").join("."), localClass)
}

I've made it compatible with the Flex version. If the greater-than character is the cause of annoyance and confusion, feel free to remove it, but you'll need to do so on the PHP classes as well.

I'm sure this won't be our last post on this topic. If you've got lots of experience with AMF and Zend (I honestly don't) please post your thoughts in the comments.

Automatic AMF class mapping in Zend (Part 1)

First off, much thanks to Lee Coltrane for figuring this out. He's a bit busy at the moment, so I'm writing this up without the benefit of his input which I'm sure will be extremely valuable once our schedules mesh up.

A few caveats right off the bat. 1) We're still figuring out our best practices. 2) This is Flex only (see below)  3) We haven't (yet) written any code to automatically populate the $_explicitType variable going back to Flash from PHP.

If you're reading this, you've more than likely come across a Flex metatag like the one below:

[RemoteClass(alias="com.domain.FooBar")]

Personally, I found it annoying to have to populate the alias manually. The compiler knows the package name and the class name. Don't make me type it again! And, in fact, the compiler will accept the metatag without it:

[RemoteClass]

The only problem is getting Zend (in our case) to map it to the correct remote class. The Adobe engineers saw fit to prepend a greater-than character to the outgoing classname when using this syntax. So, if you omit the alias, ">com.domain.FooBar" will be sent instead.

To get ZendAmf to map this automatically, you'll need to make a slight modification to the Deserializer.php class. Mine is located in Zend/Amf/Parse/Amf3/Deserializer.php (Note: I haven't even looked at the Amf0 class yet, but my understanding is that custom class mapping isn't supported in AMF0) Insert the following code around line 315 in the readObject method after it has tried to determine the className but before it has assigned a return object.

// Allow and map automatic aliases via RemoteClass metatag & flex
if (strpos ($className, '>') === 0)
{
  $className = substr ($className, 1);
}
//translation: If the classname begins with '>' use the rest of the string instead.

This addition will deal with incoming class mapping. If you want to remove the alias parameter from all of your ActionScript RemoteClass metatags, you'll need to format your $_explicitType entries like this:

$_explicitType = ">com.domain.FooBar";

...which is what we'll look at in Part 2! 

NOTE: I said at the very beginning this is Flex only. Compiling with the Flash IDE does not support the RemoteClass metaTag, so you'll need to use registerRemoteClass as you've probably read about elsewhere. Perhaps in part 2 (or 3?) I'll write up an ActionScript class that can register classes against the correct package names automatically. Until then, I'll give you a hint: flash.utils.describeType.

Smarty File Size Modifier

Here's a simple Smarty modifier that will format an integer that represents the number of bytes in a file as a human readable string.

Usage:
{$fileSizeInBytes|file_size}

Example:
{assign var=fileSizeInBytes value=10485760}
{$fileSizeInBytes|file_size}

{assign var=fileSizeInBytes value= 768000}
{$fileSizeInBytes|file_size}

{assign var=fileSizeInBytes value=303}
{$fileSizeInBytes|file_size}

Output:
10 MB
750 Kb
303 bytes

<?php
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */

/**
 * Smarty file_size modifier plugin
 *
 * Type:     modifier<br>
 * Name:     file_size<br>
 * Purpose:  format file size represented in bytes into a human readable string<br>
 * Input:<br>
 *         - bytes: input bytes integer
 * @author   Rob Ruchte <rob at thirdpartylabs dot com>

 * @param integer
 * @return string
 */
function smarty_modifier_file_size($bytes=0)
{
    $mb = 1024*1024;

    if ($bytes > $mb)
    {
        $output = sprintf ("%01.2f",$bytes/$mb) . " MB";
    }
    elseif ( $bytes >= 1024 )
    {
        $output = sprintf ("%01.0f",$bytes/1024) . " Kb";
    }
    else
    {
        $output = $bytes . " bytes";
    }

    return $output;
}

/* vim: set expandtab: */

?>

,

SEO Presentation for NCSU Web Developer Group

Jon and I delivered a presentation about SEO to the NCSU Web Developer Group this afternoon. If you were in attendance, thanks very much for coming to see us, we hope you found it informative. We talked about a lot of useful online resources during the presentation, all of the links are in the slide deck that we've posted for your convenience. You can download a PDF of the slides here, or view them on SlideShare

Please feel free to post comments and questions here.