Supporting TypeScript in Lineman.js

Lineman is a great tool for building client-side applications. Out of the box, it supports CoffeeScript, LESS, SASS, and (of course) JavaScript and CSS. But because Lineman is built on Grunt, it is easy to add any grunt task to the Lineman toolchain. Today, let’s see how we would add TypeScript support to a Lineman project.

Here’s a rundown of the necessary steps:

  1. Add the typescript grunt task to your project
  2. Configure Lineman to load the grunt-typescript task and provide configuration for the task itself
  3. Add a TypeScript file to be compiled
  4. Add the TypeScript task to Lineman’s build/run processes
  5. Add typescript-generated javascript as a source for the concat task

Let’s start with a fresh Lineman project.

$ lineman new ts-test
$ cd ts-test

Add Dependency

Add the grunt-typescript NPM module as a dependency in your project by adding it to package.json.

"dependencies": {
  "lineman": "~0.7.1",
  "grunt-typescript": "0.1.6"
},

Running npm install should leave you with grunt-typescript and lineman under node_modules. At this point, the grunt-typescript task is available to Lineman, but isn’t being loaded, so Grunt isn’t aware of it. Verify this by running lineman grunt typescript. You should get:

Warning: Task "typescript" not found. Use --force to continue.
Aborted due to warnings.

Load the Task

Configure Lineman to load the grunt-typescript NPM module by adding it to the loadNpmTasks array in config/application.js. Lineman will invoke Grunt’s loadNpmTasks method on each task in this array. We must also provide the task with its associated config block. This should be the same configuration block that would normally be added to Gruntfile.js.

loadNpmTasks: ['grunt-typescript'],

typescript: {
  compile: {
    src: 'app/js/**/*.ts',
    dest: 'generated/js/app.ts.js'
  }
}

Now running lineman grunt typescript should give you:

Running "configure" task

Running "typescript:compile" (typescript) task
js: 0 files, map: 0 files, declaration: 0 files

Done, without errors.

Add some TypeScript

Create a new file app/js/greeter.ts. This will be the TypeScript source that is compiled into JavaScript. If you remember our typescript config block specifies the source as ‘app/js/**/*.ts’, so any filename with a ‘.ts’ extension (within any subdirectory) will be compiled into the same JavaScript file. (For more info, refer to grunt-typescript‘s configuration.)

function Greeter(greeting: string) {
  this.greeting = greeting;
}

Now running lineman grunt typescript should give you:

Running "configure" task

Running "typescript:compile" (typescript) task
File ts/generated/js/app.ts.js created.
js: 1 file, map: 0 files, declaration: 0 files

Done, without errors.

Now we’ve got the task working independently from the Lineman build process. For any arbitrary grunt task, this would be sufficient; simply run lineman grunt . However, if we run lineman build, the typescript task will not be run. Let’s add it to the Lineman build process.

Compile during Lineman build

You can add grunt tasks to either the beginning or the end of the Lineman build process using prependTasks or appendTasks, respectively. Since we want the generated JavaScript that the TypeScript compiler emits to also be concatenated and minified with the rest of the app, we need the typescript task to run before the other Lineman tasks. We also want this task to run in both dev and dist phases, so we’ll add the typescript task to prependTasks:common array in config/application.js.

prependTasks: {
  common: ['typescript']
},

Now we’ve got the task running as part of the Lineman build process. However, the typescript-generated javascript is not being concatenated/minified with the rest of our application. We need to add the typescript-generated javascript to the concat task’s source list.

Concatenate/Minify

There are a number of different ways to do this. One of which, is to simply modify the concat-task configuration block in config/application.js.

// save off the current config object instead of directly exposing it via module.exports
var config = require(process.env['LINEMAN_MAIN']).config.extend('application', {
... // this remains untouched
}

// add the typescript-generated js as a concat source
config.concat.js.src.push('generated/js/app.ts.js');

// export the modified config
module.exports = config;

Now when we run lineman build our Greeter function is compiled from TypeScript into JavaScript, concatenated along with the rest of the application JavaScript (‘generated/js/app.js’) and minified (‘dist/js/app.min.js’)!

Fetch and Iterate

Tonight I stumbled across a blog post from the future! Or something like that. On 4/17 at 11:30PM I read a blog post that was published on 4/18 at 1:54. Time zones = time travel. Anyway…

It was about finding a cleaner, more succinct, syntax for a standard pattern:
fetch_data, process_data, rinse and repeat until no more data!

Quick, go read the original post. Don’t worry, it’s short.

{elevator music}

Okay, so how do we improve this? Well, in some languages (like JavaScript, Ruby, PHP, et. al), an assignment expression actually returns a value. And since the expression returns a value, the expression itself can be used as the looping condition, like so:

while(data = fetch()){
  process(data);
}​

Notice the single equals sign? We are doing assignment here, not an equality check. We invoke fetch() and the result is returned and assigned to data. Now the value of data itself is used as the while condition. So the data is continually fetched and processed until fetch() returns a falsy value. (In JavaScript, that would be one of false, undefined, null, 0, "", or NaN)

This is a very common pattern in PHP, especially when fetching/querying data from a database. It’s a much less common pattern in JavaScript, but still quite valid. As is with most useful things, this approach can be misused. It can easily be confused for a typo (missing an equals sign?) so it should be used with care. For instance, ensuring that your fetch() function and data variable are named clearly should make it less likely to be misunderstood. When used appropriately, it can certainly increase the clarity of your code.

IE6, MooTools, and MultiBox: A Tale of Woe

On a recent project, I traveled to the depths of hell and came out alive. Here’s how it went down…

The initial task wasn’t all that unique or difficult. We were to add a few videos (most hosted on YouTube, with a couple self-hosted) that would open in a lightbox-style popup when the thumbnails were clicked. MooTools was already being used on the project, so we selected MultiBox by phatfusion as the pop-up of choice. I won’t go into the reason(s) for selecting MultiBox. The more I work with it, the less I like it; but that’s for another post. So, we have MooTools as the JavaScript framework and MultiBox as the plug-in.

Base Functionality

Keeping progressive enhancement techniques in mind, we set the destination URL of the thumbnails to be the video page on YouTube. This gives crawlers the ability to find the true video destination, as well as user’s without JavaScript the ability to find the video. Now, I realize that YouTube doesn’t even work without JavaScript, but that’s their problem. (On a side-note, it’s really sad that they use JavaScript to inject the Flash video player, when the SWFObject static publishing method would work just fine).

So the first problem we run into is that, by default, the MultiBox media type of youtube expects the link URL to point directly to the video and not to the video page on YouTube (one reason to ditch MultiBox). Note the difference in URLs below. The video identifier is the same in both (eCzEkiIucUk) but rather than passing it as a parameter to the ‘watch’ page, we can pass it as the slug to /v.

Video page on YouTube: http://www.youtube.com/watch?v=eCzEkiIucUk
Actual video on YouTube: http://www.youtube.com/v/eCzEkiIucUk

To deal with this, I wrote a quick script that executes ondomready. It parses the v parameter value out of the query-string and rewrites the URL to point directly to the video. This way, when MultiBox opens and inspects the URL to load, the video itself is opened in the MultiBox pop-up rather than the entire YouTube page.

Roadblock #1 : MultiBox ‘types’

At this point, everything is working as expected until the client requests that we provide a customized fallback message for users without Flash. This is a perfectly reasonable request. Ideally the fallback content would be a transcript of the video and perhaps some still images from the video. Maybe even a link to the raw H.264 video. Of course, in this case we are requested to simply provide a ‘Please install Flash’ message. Unfortunately, MultiBox doesn’t have a way to provide custom fallback content for its youtube type. (Yet another reason to ditch MultiBox.)

Rather than using the youtube type and its out-of-the-box functionality, we’ll switch over to the element type. This type allows you to target an element in the dom and the targeted element (and children) is cloned into the MultiBox pop-up. For this to work, we’ll provide our own object element, complete with cross-browser nesting of objects (see SWFObject static publishing) and our own custom no-flash fallback content. Each video thumbnail will now have the actual video markup next to it in the source, hidden via CSS. The thumbnail is marked up to link to the video page on YouTube (for JS-off users). On page load, the links are rewritten to point to the fragment identifier of the video object in the page (e.g. href="#video1"). This way, when MultiBox is activated using the ‘element’ type, it will grab the object element with id="video1" and clone its contents into the pop-up.

Roadblock #2: MooTools ‘clone()’

With the exception of IE6, everything works as expected across browsers both with and without JavaScript. However, it fails in IE6 (of course). Upon inspection of the cloned object elements after they are inserted into the pop-ups, we notice that they no longer contain the param children they’re supposed to. The original object elements still contain their param children, but the clones do not. Internally, MultiBox uses the MooTools clone() method on the base Element class so I put together a quick test page to verify that MooTools’ clone() method works properly in IE6 on object elements. It failed the test, and I filed a bug against MooTools.

The workaround for the MooTools clone() bug is to use the old-school innerHTML property ourselves rather than allowing MooTools to do proper DOM manipulation in the background. So I open up MultiBox and find the routine whereby it uses clone() and add a quick hack. I check if the browser is IE6 and if so, use innerHTML to copy the object element (and all its children) into the pop-up.

Roadblock #3: IE6 ‘flashvars’

So now we have the object being correctly cloned into the pop-up when a user clicks the video thumbnail. An then I get another ticket. All the YouTube videos are playing, but the self-hosted video is not. Why is the video not playing? This particular video is played using a SWF player which takes the FLV file name to play via the flashvars param. I notice that the FLV file isn’t being requested in IE6. But we just fixed the param bug and the children are all there! Or are they really? After some more debugging and googling, I find that IE6 resets the value of the flashvars param when its innerHTML is modified.

You’re not surprised, are you?

After some more searching, I find a great blog post that describes the solution. Rather than passing the different variables to the flash player using the flashvars param, we’ll just add them to the SWF query-string. So this:

http://player.swf

becomes this:

http://player.swf?path=video1.flv

Since we’re using SWFObject’s static publishing method, there are 2 object elements. One intended for IE, and another one, intended for everyone else. The proper object (for Firefox, Chrome, et. al.) is nested within the IE object and hidden via Conditional Comments. Since this whole flashvars mess is only a problem for IE6, we only need to modify the outer object element.

Roadblock #4: MultiBox’s flvplayer

Just when you thought we were out of the woods, we have one last issue. Now that we have the video objects with proper fallbacks, and the elements being cloned properly, and the config file information passed correctly, we still don’t have a functioning video in IE. Since we modified the outer IE object element, thanks to the previous issue, we’ve broken the video in all versions of IE. What’s the problem this time? It turns out that the flvplayer.swf that comes packaged with the phatfusion MultiBox doesn’t stream the video properly when the FLV file name is passed via query-string. Solution? Ditch phatfusion’s flvplayer and use another! (I recommend any of the JW players if your situation fits the non-commercial bill)

When the bugs align…

So at the end of this ordeal, I had three more reasons to avoid phatfusion’s MultiBox widget, I had filed yet another bug against MooTools, and I had conquered yet another bug in IE6. And it all piled up because of a tiny bug in a particular flvplayer; that was caused by the solution to a specific bug in IE6; which was only a problem because of a bug in a particular version of MooTools; which was only a problem because of a poorly designed API in MultiBox; which was encountered because the client wanted to display a download button for Flash; mostly for users who aren’t even able to install Flash if they wanted to. Don’t you just love web development?

Google Analytics Tagging with HTML5 data-* Attributes

Imagine if you will, a page with a significant amount of dynamic page elements. For instance, a slide-out panel containing a number of ‘panes’ containing topical information on various ‘factors’. Let’s assume that once this slide-out is open, we wish the user to be able to jump from factor to factor (similar to a slideshow) using buttons along the bottom of each factor. Let’s go even deeper and say that within each factor, we have a collection of close-up images. Again, the user should be able to navigate the close-ups via next/previous buttons similar to a slideshow. In addition to the next/previous buttons, along the top edge of the ‘close-ups’ section are progress indicator dots that highlight according to which close-up is active (and link directly to individual close-ups).  Given that this functionality should work without JavaScript, all of these UI controls are marked up as links. And we have quite a few of them—to wit, x*(x-1) factor links (for x factors) plus x*y (direct links to close ups per factor) plus x*(2y) (next/prev links for each y close-up for each factor). So, let’s say we have 3 factors and 3 close-ups per factor. We now have 33 links! And now the task is to tag each of these links with unique labels that will be sent back to Google Analytics upon each click event. I’ve broken down a brief subset of the analytics tags for each of the three types of links below.

Tag Templates

Tagging template for the factor navigation along the bottom of each [x] factor:

/page_name/factor_[x]/factor_1_icon
/page_name/factor_[x]/factor_2_icon
/page_name/factor_[x]/factor_3_icon

Tagging template for the prev/next buttons on each [y] close-up on each [x] factor:

/page_name/factor_[x]/closeup_[y]/next_arrow
/page_name/factor_[x]/closeup_[y]/prev_arrow

Tagging template for the close-up direct links (also used as progress indicator) on each each [x] factor:

/page_name/factor_[x]/closeup_dot_1
/page_name/factor_[x]/closeup_dot_2
/page_name/factor_[x]/closeup_dot_3

Approach

As with anything, I try to keep my code DRY. Considering that these tags will likely end up as magic strings in some form or another, I’d like to reduce the maintenance overhead of these tags as new factors or close-ups are added or removed. The tags themselves convey the hierarchy of the structure in which they are contained. So let’s map the tagging hierarchy onto the structural hierarchy. This tagging information could be embedded in the id or class attributes of an element, though I think that would be coupling two separate needs (JS behavior/CSS styling + Analytics) onto the same data. This information could also conceivably go into the title attribute, though this attribute is meant for human (read: end-user) consumption. Nothing seems to fit, so let’s try out an HTML5 data-* attribute: data-ga.

First, the /page_name segment should map to the page:

<body data-ga="/page_name">

Each ‘factor_[x]’ segment should map to its own factor pane:

<ul class="factors">
  <li data-ga="/factor_1" />
  <li data-ga="/factor_2" />
  <li data-ga="/factor_3" />
</ul>

Each ‘closeup_[y]’ segment should mapt to its own close-up pane:

<ul class="closeups">
  <li data-ga="/closeup_1" />
  <li data-ga="/closeup_2" />
  <li data-ga="/closeup_3" />
</ul>

 

And each link or button gets its own respective value. Keep in mind, multiple data-ga values will be ‘scoped’ by their ancestors’ data-ga values:

<a href="#factor-1" data-ga="/factor_1_icon" />
<a href="#factor-1-closeup-1" data-ga="/closeup_dot_1" />
<a href="#factor-1-closeup-2" data-ga="/next_arrow" />
<a href="#factor-1-closeup-3" data-ga="/prev_arrow" />

Now, whenever a link is clicked, we simply concatenate the data-ga values from each ancestor! The method below should have the context of this as an anchor element with a data-ga attribute. Generally, it would be in the click handler of any element matching the selector: "a[data-ga]". Once ga is concatenated, it can be used as the tag for a Google Analytics API call (_trackPageview or _trackEvent).

$("a[data-ga]").click(function(event){
  var ga = $(this).parents('[data-ga]').andSelf()
           .map(function(){return $(this).attr('data-ga');});
  ga = $.makeArray(ga).join('');
  _pageTracker._trackPageview(ga);
});

Demo

//jsfiddle.net/eY4cq/1/embed/

A few additional notes

Performance

It would be much better to calculate the ga-tag for every link on the page during page load and cache the result in the data store of the element. As written, the ancestor traversal is executed on every click, and would result in the same return value each time. However, as a special case when I was first implementing this, there were some widgets that altered the hierarchy of the page, thus it was necessary to only perform the concatenation at event-time rather than load-time.

As an additional performance boost, I added a class of ga-scope to each ancestor element that contained a data-ga attribute. This allowed me to use a class selector in the .parents() filter. This will only yield a performance boost in browsers that support a native implementation for querying by class name, thus allowing the selector engine (MooTools or jQuery) to avoid stopping to inspect every single ancestor on its way up the tree.

MooTools

I originally implemented this with MooTools. During the implementation I discovered a bug in the MooTools selector parser where the attribute selector doesn’t properly find attributes with hyphens. So, I created a custom pseudo-selector as a workaround:

Selectors.Pseudo.data_ga = function(){
  return Boolean($(this).get('data-ga'));
};
$$('a:data_ga').addEvent('click', function(event){
  var ga_code = this.getParents('.ga-scope')
                .get('data-ga').reverse().join('')
                + this.get('data-ga');
});

JavaScript Best Practices

@ikeif recently tweeted a request for some JavaScript best practices. Rather than simply reply to him, I thought I’d post them here and beat him to the blog-post-punch. I’m not going to expand much on any of these, although any discussion that arises will likely spawn its own post. These are in no particular order and are really nothing more than a brain-dump. I’ve numbered them for easy reference in the comments.

  1. Avoid global variables. When you must use a ‘global’, use your own namespace.
  2. Avoid cluttering the global namespace with functions. Assign your ‘global’ functions to a single namespace (see above).
  3. Discover the JavaScript framework/library that speaks to you and stick with it. Don’t load jQuery and Prototype on the same project. Yes, I know you can run many libraries in noConflict mode now, but think of the additional overhead you are placing on your users. All for some snazzy plugin? Port it!
  4. Use JSLint. I assume you validate your HTML? Use JSLint to validate your JavaScript. If your code is JSLint safe, you can avoid a few browser idiosyncrasies (hasOwnProperty() anyone?). As a bonus, JS-Lint safe code is also JSMin safe so you can minify your scripts without worrying if the minification will affect functionality.
  5. A side effect of using the JSLint validator in Aptana, my IDE of choice for front-end development, is my use of JSLint’s special `/*global */` comment. JSLint will flag any global variables unless they are explicitly listed as dependencies in this comment. This means at the top of all of my scripts, one can easily spot any required dependencies (specific MooTools modules for instance).
  6. Use feature detection not browser sniffing.
  7. Write unobtrusive scripts instead of inline event handlers.
  8. Keep your styles in your CSS! Although most libraries make it easy to manipulate element styles, it’s much better to keep your styling where it belongs- in your CSS. Mixing the two violates separation of concerns and makes your code less maintainable. Instead, add and remove classes as necessary in your scripts.
  9. Make sure your UI elements support proper interaction when JS is disabled. If a link opens a lightbox, set the href to point to the lightbox content so no-JS users can still access the content. Links with `href=”#”` kill kittens.
  10. Any UI elements that *only* support JavaScript interaction (and think carefully about this) should be created by JavaScript. Don’t litter your HTML will dummy elements that are only there for JS events. Your script should create and inject them.
  11. If at all possible, don’t modify libraries or plugins directly. This makes future upgrades a nightmare. It is much better to extend the plugin/library without modifying the original.
  12. Your .js files should be served with HTTP `Content-Type: application/javascript`. BUT the `type` attribute in your HTML `script` element must be `text/javascript` or else Internet Explorer will crap itself.
  13. Language=”JavaScript” was deprecated, like, a zillion years ago. Stop using it.
  14. Don’t pre-optimize your code by using fancy looping structures. It is much better to have readable code. Once your app is running, then you can go back and profile it to eliminate bottlenecks. There is no point in pre-optimizing your scripts when the overhead of your background image is 10x slower.
  15. Don’t return false from an event listener when all you really want is event.preventDefault(). Maybe someone else wants to listen for that click event, too, mmmkay?
  16. Stop using document.write

Okay, that’s my list. I may add more later. Disagree with any of these? What best practices or anti-patterns do you have?

jQuery.Firebug: A call for feedback.

As a result of some of the discussion following from my post on my new jQuery plugin, jQuery.Firebug I’m soliciting feedback for its desired behavior. Example:

$('.setA').log();
$('.setB').log("some", "information");
$('.setC').log("title attribute is: ", ".attr('title')");

Some explanation. The log method follows the same rules as the Firebug console.log method. It can take 0 or more arguments that are concatenated into a space-separated string when finally printed to the console. For some jQuery-specific behavior, I have added a little wrinkle as shown with the log statement following SetC. If an argument to the log method:

  1. is a string
  2. begins with a period (dot)
  3. is a valid jQuery method

then the jQuery method specified is executed on the jQuery selection and the result is printed to the console. In the example above, if the title attribute on the first element of SetC is 'example title' then the final log message would be "title attribute is: example title".

Further, my the plugin will feature an additional option (off by default) that will explicitly print each element in the jQuery selection wrapped in a console.group. In the example above, say SetC contains 2 elements. If the option were turned on, the output would be similar to the output of the following:

console.log("title attribute is: example title");
console.group($(".setC"));
console.log($(".setC").get(0));
console.log($(".setC").get(1));
console.groupEnd();

So, back to the problem at hand. My issue, is when and where to print the jQuery selection itself. The different options are:

  1. only print the jQuery selection when there are no arguments to the log method
  2. only print the jQuery selection when there are no arguments to the log method but also print the jQuery selection in place of any string argument equalling "this" (similar to my jQuery method replacement demonstrated above with .attr("title"))
  3. always prepend the jQuery selection to the arguments (so the jQuery selection is printed before the rest of the arguments)
  4. always append the jQuery selection to the arguments (so the jQuery selection is printed after the rest of the arguments)

I’m leaning towards either #3 or #4 but am open to feedback. Please comment with your suggestions. Keep in mind that all four above choices will still result in just one log message per log() call. Turning on the ‘explicit‘ option is the only thing that will result in more console messages than log() calls. Also, keep in mind that printing the jQuery selection itself to the console will allow deep inspection. For instance, clicking on the jQuery selection in Firebug shows what elements are selected, etc.

Announcing jQuery.Firebug

I have been sitting on my latest jQuery plugin for some time now. Although I realize that the code is not yet of production quality and there are certainly bugs and features that remain to be addressed, I’ve decided that I should at least release this plugin to the wild. At the very least, I would love some feedback on it and possibly new features to be added. “So let’s see it!” you ask?

jQuery.Firebug is a jQuery plugin that simply exposes the Firebug Console API to the jQuery object. That’s about it. Under the covers, it bridges some functionality between Firebug and Firebug Lite and has a host of other small feature. But all in all, it simply adds the Console API methods directly to the jQuery object.

The goal of this plugin is to allow inspection of your jQuery selections while in the middle of a chain. For those of you who have ever had a jQuery chain like:

$(".elements").parents("div")
.find(".new").show().end()
.find(".old").hide();

and you load up the page and it doesn’t work. How do you begin debugging? You open up Firebug but are unable to easily ‘step through’ the jQuery chain. Inevitably, you have to break up each selector, assign it to a temporary variable solely to call console.log(temp) on your selection. Enter jQuery.Firebug:

$(".elements").log()
.parents("div").log()
.find(".new").log()
.show().end().log()
.find(".old").log()
.hide();

Each log method returns the same selection that was passed to it, so you can simply continue your chain as if it weren’t even there. Every Firebug method (as of Firebug 1.2) is supported so you can call debug(), assert(), info(), dir(), profile(), etc.

There are a few additional features that I will address later as the code begins to settle down. For now, the source and documentation can be found in Subversion at svn.jasonkarns.com/jquery/firebug.  There is much work to be done on the plugin as well as on the documentation. Until then, let me hear any feedback you may have.