Jon Williams (jon)

Website: http://shovemedia.com


 

Posts by Jon Williams (jon):


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 4

I'm structuring the project folder like this:

/portfolio
  /deploy
    /css
    /img
      /icons
        /link.png
        /video.png
        /pic.png
    /js
      /framework
      /lib
        /jquery.js 

    /background.png
    /index.html

    /contact
      /thumb.png
      /bio_contact.html
    /projects
      /project1
        /thumb.png
        /video.html
        /description.html
      /project2
        /thumb.png
        /video.html
        /description.html

  /src
    /project1
      /video

  /notes

If your information architecture is tight, you'll likely see echoes of your file hierarchy in your data hierarchy and echoes from both in your code's object hierarchy. Guess that means we're going to need some code then.

Stay Tuned:

In the next installment, we're going to spend a little time playing with jQuery-based animation so that we're comfortable with it before trying to integrate the effect into the site itself.

HTML5 Portfolio Project – Part 3

Don Your Project Manager Hat

A week with no updates?! ... And things are about to get more busy, not less.

Plus, upon reviewing the project plan, I noticed an omission: there's no specific line item for the mask transition. It seemed so obvious, I neglected to capture it in the schedule. I'm adding 3 hours to account for it (new total: 44 hrs).

Ok, To Work Then

Last time, I talked a bit about the data model. Since then, I've thought of some improvements. Just because the Flash version worked a certain way doesn't mean I shouldn't re-evaluate my assumptions.

First, the home screen ought to be treated as a node. It has a url and graphical content, just like the portfolio pieces. It's button-ness behaves a little differently, and it's sized larger, but we should be able to abstract away the differences. In theory, it would be nice to allow unlimited node nesting. This would allow projects to have sub-projects (for example) and enable a lot more organizational flexibility, extending the useful lifetime of the site. We'll see if we can squeeze it in. (Google: YAGNI)

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

Don't get lost in the braces. Deeper nesting is a necessary evil, I think. Otherwise, I'm going to end up with redundant references to "link" all the flattened elements.

I've also changed some of my naming conventions. Each nodes stores its child nodes in an Array called "sections".

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.

Automatic Value-Object Mediation with JS Signals

Just a little something I cooked up tonight after a few beers with Lee:

ModelMediator = function (model) {
  this.__onChanged__ = new Dictionary();

  for (var key in model)
  {
    this.__defineSetter__(key, function (arg) { this.set(model, key, arg) });
    this.__defineGetter__(key, function () { this.get(model, key) });
    this.__onChanged__.set(key, new signals.Signal());
  }
}

ModelMediator.prototype.get = function (model, key)
{
  return model[key]
}

ModelMediator.prototype.set = function (model, key, value)
{
  model[key] = value;
  this.__onChanged__.get(key).dispatch({model:model, key:key, value:value});
}

Dictionary class here

Downloading and understanding JS Signals is left as an exercise for the reader.

Dictionary Class (JavaScript / JS)

I sorely miss the Dictionary class from AS3, but wasn't excited by what my Googles led to and figured I'd learn more by rolling my own.

JavaScript Arrays can be used as a hash, but only with String or Numeric keys. This class lets you use any Object. A function, an Array, a class ... anything. It could be faster, among other possible improvements, but this implementation has been coming in handy all over.

Dictionary = function ()
{
  this.keys = [];
  this.values = [];
}

Dictionary.prototype.get = function (key)
{
  var index = this.keys.indexOf(key);
  if (index != -1)
  {
    return this.values[index];
  }
}

Dictionary.prototype.set = function (key, value)
{
  var index = this.keys.indexOf(key);
  if (index == -1)
  {
    this.keys.push(key);
    index = this.keys.length - 1;
  }

  this.values[index] = value;
}

Dictionary.prototype.remove = function (key)
{
  var index = this.keys.indexOf(key);
  if (index == -1)
  {
      return undefined;
  }

  var value = this.values[index];

  delete(this.values[index]);
  delete(this.keys[index]);

  return value;
}

Removing Trace and Logging Statements for Production

I'm saving these recipes here so I can bookmark, and I'm sure someone else will find them useful.

I decided to convert calls to trace or my logging framework to use conditional compilation so that they won't be included when I do a production build, but are included when I do a debug build.

The Flex compiler supports this by allowing you to set a variable at compile-time, eg:

-define=CONFIG::debug,true

In your code, simply wrap the code to be conditionally compiled in curly braces, prepended with this variable, eg:

CONFIG::Debug
{
	trace( 'Debug' );
}

I spent some time writing a regular expression that finds trace statements, and wraps them in this block. In plain English, start at the beginning of the line, match zero or more whitespace, the word "trace", zero or more whitespace, an open parenthesis, the rest of the line.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1CONFIG::debug { $2 }

FDT's internal parser doesn't like the conditional compilation syntax, so we need to change our replacement text slightly to wrap that part in markers instructing it to ignore them.

Search for: ^([ \t]*)(trace[ \t]*\(.*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

The same recipe for any call to logger, eg: logger.debug, logger.info, logger.warn, etc.

Search for: ^([ \t]*)(logger\..*)$
Replace with: $1/*FDT_IGNORE*/ CONFIG::debug /*FDT_IGNORE*/ { $2 }

Masking TLF (Text Layout Framework)

Just a quick note to point out a bug (?) I found today while working with the ActionScript 3 Text Layout Framework.

I wanted to add a mask to the Sprite container I'd passed to my TLF ContainerController. Whatever it is that happened wasn't correct: there was masking occurring, but the shape I was using as a mask was (incorrectly) visible on the stage. Rolling over my buttons caused redraw of stage regions, and my TLF text in those areas would go from invisible to incorrectly masked.

After trying several things, I pulled an old-school trick out of my bag: nest the Sprite I was passing as a container to the ContainerController in another holder Sprite, and apply the mask to the holder instead. From there, everything worked as expected.

Note: I further tracked this down to be caused by the ContainerController being created smaller than the text stuffed inside. Only then do I have problems with masking. YMMV.

(See the original post)

I messed around with this quite a bit but was never able to get things to work exactly as I'd hoped. I even got as far as using the XmlTask Ant extensions to modify the launch file on the fly. Unfortunately, I couldn't figure out how to get Eclipse to reload the modified configuration without completely restarting, so that path resulted more or less in a dead end.

What I did figure out is how to make your launch configurations shareable via SVN, so that only one team member (yours truly) has to deal with builds and launch configurations. Everyone else can update from SVN and run ANT tasks without being familiar with all these grisly details.

The last post mentioned (with no small amount of grumbling) that you have to change the JRE setting to "Same JRE as Workspace" on every single build target you create (or rename). This is incredibly annoying. If you go to the same dialog: Run >> External Tools >> External Tools Configurations...

Check out the Common Tab. Here you can pull the launch file configuration into the project and out of the workspace by selecting "Shared file" instead of "Local file". This lets you share launch configurations with the rest of the team.

If you wish, you can create a template for new build targets. If you get the file naming convention right, you should be able to get away with defining new build targets without ever needing to visit the External Tools Configurations dialog. I've got a mixed OS X / Windows Team, so I made some modifications to Eclipse's default launch file output. In short, I removed explicit references to my OS X default JVM.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntLaunchConfigurationType">
<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/PROJECT_NAME/build.xml"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="PROJECT_NAME"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_ANT_TARGETS" value="BUILD_TARGET_NAME"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/PROJECT_NAME/build.xml}"/>
<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>
</launchConfiguration>

Note the spots where you should substitute your own PROJECT_NAME and BUILD_TARGET_NAME.

I tried setting the DEFAULT_VM_INSTALL to true, and it worked, but Eclipse insisted on mucking with it as soon as I visited the External Tools Configurations dialog again.

I'm storing my launch files in .settings and setting the filename to:
PROJECT_NAME BUILD_FILE_NAME.xml [BUILD_TARGET_NAME].launch

Your mileage will almost certainly vary.