Archive

javascript

Every now and then I think of some interesting programming project that would be fun to do. A lot of times I actually start working on these side projects. Unfortunately, I typically only stick with the project until I get to the finishing touches where it’s 90% done. Of course, it’s the last 10% of the project that always takes 90% of the time and by then I’ve stopped learning cool new stuff and all that’s left are annoying bugs. So before I start on my next project (after having lost interest in the last one), I’m going to write out my idea so that maybe someone else with more spare time than me can build it.

Every few weeks for the last year, someone around me mentions node.js as this cool new thing that everyone has to try out. In case you haven’t heard, node.js takes the V8 javascript engine and lets you run server side javascript. The especially exciting part is that all IO, including such common things as reading a file from disk and talking to a database, are asynchronous. It uses an event loop instead of a threading model to achieve really good concurrency, which apparently is important for things like really scalable network applications.

I’d love to have an excuse to play around with node.js but haven’t thought of anything that compelling until now. I usually like building web applications but unfortunately building traditional web applications in node would be missing the point (or painfully difficult). All the database drivers for traditional dbs like mysql and postgresql are blocking so you don’t get any of the benefits of node’s asynchronous IO model. If you are not doing anything cool with concurrency, you might as well stick with what you know and use python or ruby.

Ok, so what are cool concurrency related problems to solve? You could always write a chat server, but everyone and their cousin is writing a chat server in node these days. You could write a static file server or a web proxy, but that’s kind of boring and everyone already uses nginx. But what about taking static resource servers to the next level?

Imagine a static file server and/or web proxy that was geared towards serving resources like javascript and css used in larger web applications. Some features you don’t typically expect from a static file server but would be totally awesome and useful:

  • automatic javascript minification – http://github.com/mishoo/UglifyJS looks promising
  • automatic css minification – you might have to write this yourself, not too hard
  • automatic image optimization – use pngcrush or something similar
  • proxy to another server instead of the file system to get the unminified files
  • rewrite static resource urls in html files on the fly to point to the minified sources
  • concatenate multiple css and js files into single files based on usage as detected in html
  • hooks for adding your own customizations – it’s just javascript… no need to dust off those c books
  • track usage statistics and performance metrics in some key-value db

This wouldn’t replace nginx or CDNs or anything for really big scale stuff, but I think it could be a useful development tool. You would run this static resource server along side your single-threaded application server during development. You could even run it in production as the primary source backing a CDN. It would be easy to use with any sort of application server written in any other language. Being able to customize the server with javascript would make it easy to integrate any static resource packaging system you might divise for your application server. The best part though is that you get to write it in node!

Last week Google submitted their own entry into the JavaScript framework race by open sourcing Closure, the library that powers google docs, maps, mail and more.

I couldn’t resist spending some time playing with the closure tools, especially the dojo-like module dependency resolver (a.k.a goog.provide and goog.require) and the closure compiler. It was a bit tricky at first because the example “applications” from the tutorial are pretty contrived and there is no information on using closure with existing JavaScript libraries.

Anyway, I thought I would share how I hooked up Closure to an existing JavaScript library (the one that powers http://gvr.carduner.net).

Step 1 – get the Closure Library.

Closure is actually a set of three tools: a JavaScript Library, a JavaScript compiler, and a templating language. The dependency resolution tools are part of the library, so you should check that out first. At the moment, the closure library can be checked out from the project subversion repository:

svn checkout http://closure-library.googlecode.com/svn/trunk closure

If you are just interested in using the dependency resolution tools and not the entire framework, you just need two files: base.js and calcdeps.py. The full checkout can take a while as they’ve included all the generated api documentation, which you can also read online.

Step 2 – Instrument your JS library code

If you already have your JavaScript code split up into “modules” that live in different files, adding the dependency code is pretty easy. In my case, I wanted to use Closure with gvr.carduner.net, which already has a set of interdependent JavaScript files like so:

gvr.core.js		gvr.renderer.js		gvr.web.client.js	jquery.history.js
gvr.js			gvr.robot.js		gvr.web.tests.js	launcher.js
gvr.lang.js		gvr.runner.js		gvr.world.js
gvr.lang.parser.js	gvr.tests.js		gvr.world.parser.js

Open up each JavaScript file, and add declarations to the top about what each file provides and requires. For example, gvr.runner.js depends on gvr.robot.js and gvr.core.js. In turn it provides the gvr.runner namespace. So at the top of gvr.runner.js I added the following:

// gvr.runner.js
goog.require("gvr.core");
goog.require("gvr.robot");
goog.provide("gvr.runner");

Then of course I had to add the provide statements to gvr.core.js and gvr.robot.js, as in:

// gvr.core.js
goog.provide("gvr.core");

// gvr.robot.js
goog.require("gvr.core");
goog.provide("gvr.robot");

The goog.provide statements will actually create the object namespace you pass in, so the following would be valid:

goog.provide("foo.bar.baz");
foo.bar.baz.blah = "blah";
foo.somethingElse = "somethingElse";

In fact, if you try to create the namespaces later, the compiler will throw warnings/errors at you. So if you have any code that looks like the following, you should remove it.

var foo = foo || {};  // DELETE THESE LINES
foo.bar = foo.bar || {};  // AS THEY CONFLICT WITH goog.provide("foo.bar.baz");
foo.bar.baz = foo.bar.baz || {};

Once you have added all the right goog.require and goog.provide statements to your code, you’ll need to generate a dependency graph using the calcdeps.py script.

Step 3 – Build the dependency graph with calcdeps.py

The dependency resolution system uses a pre-generated dependency graph to link namespaces like foo.bar.baz to their corresponding JavaScript files and the files they require. This is all stored in a file called deps.js. The one for the closure library can be found in trunk/closure/goog/deps.js. Here are the first few lines to give you an idea of what this looks like:

// This file has been auto-generated by GenJsDeps, please do not edit.
goog.addDependency('array/array.js', ['goog.array'], []);
goog.addDependency('asserts/asserts.js', ['goog.asserts'], []);
goog.addDependency('async/conditionaldelay.js', ['goog.async.ConditionalDelay'], ['goog.Disposable', 'goog.async.Delay']);
goog.addDependency('async/delay.js', ['goog.Delay', 'goog.async.Delay'], ['goog.Disposable', 'goog.Timer']);
// ... this goes on for quite a while ...

In order for closure’s dependency mechanism to know about your libraries, you need to create a deps.js file of your own. This can be done with the calcdeps.py script you should have downloaded by now. If you checked out the entire closure library source, the calcdeps.py script can be found in trunk/closure/bin/calcdeps.py.

The calcdeps.py script must be run from the directory where your files will be served, as it stores the relative file paths in the generated deps.js file, which is in turn used to build urls to all your JavaScript files. For example, my application’s directory structure looks like this:

gvr-online/
  closure/ # this is the trunk checkout of closure
    closure/
      bin/
        calcdeps.py # I'll use this script to generate my own deps.js
      goog/ # this is the closure library source, including deps.js and base.js
  app/
    src/
      ui/  # This directory is exposed to the web as http://localhost:8080/ui/
        lib/ # this is where my javascript library lives
        closure/
          goog/ # this is a symlink to the closure/closure/goog/ directory at the top level

The gvr-online/app/src/ui/ directory is what gets exposed through the web, so the calcdeps.py script should be run from that directory. Here is the command I used to run it:

cd app/src/ui/ && python calcdeps.py -p lib -o deps > deps.js

The -p lib option tells calcdeps.py to search in app/src/ui/lib/ for js files with goog.provide and goog.require statements. The -o deps option tells calcdeps.py to generate a dependency graph file, which gets saved to app/src/ui/deps.js. If you are using the rest of the closure library, and not just the dependency stuff, you will need to add an extra -p closure argument.

With that done, we can try this out in a browser.

Step 4 – Instrument your HTML

Next you’ll need to add the closure hook to your html files. In my project, there is just one html file, index.html. If you just include the base.js file like the closure tutorial suggests, you will not be able to goog.require your own library modules. You have to tell base.js where to find your library, and where to find the deps.js file. I added the following to the <head> section of index.html:

    <script type="text/javascript">
      var CLOSURE_NO_DEPS = true;
      var CLOSURE_BASE_PATH = "/ui/";  //this is the directory where I ran calcdeps.py
    </script>
    <script type="text/javascript" src="/ui/closure/goog/base.js"></script>
    <script type="text/javascript" src="/ui/deps.js"></script>

The CLOSURE_NO_DEPS option tells base.js that it shouldn't load closure's deps.js file and that we will handle the dependency graph loading ourselves. The CLOSURE_BASE_PATH setting is a prefix that should be added to the paths specified in the deps.js file. Next we load base.js which defines the goog.require and goog.provide functions. And finally, we load the deps.js file that was generated in the last step.

With these files loaded, you can now goog.require any of your modules. For example, at the bottom of my index.html file, I can have this:

<script type="text/javascript">
goog.require("gvr.web.client");
</script>
<script type="text/javascript">
client = gvr.web.client.newClient();
client.getUser(function(user){ alert("Hi "+user.nickname); });
</script>

Closure - pun intended

So far, I think closure's dependency resolutions tools are my favorite. It's relatively simple (you only need two files really) and doesn't require you to structure your code in any particular directory hierarchy (unlike Dojo last I checked). My only wish at this point is for calcdeps.py and base.js to have a mechanism for registering third party libraries like jQuery without adding goog.provide() to the top of their files. You could add other libraries to the end of the generated deps.js, but that isn't very maintainable and won't work with the closure compiler (I think?). I haven't yet gotten to using the closure compiler with my code, so more on my experience with that later.

Seven years ago I worked on an open source project called Guido van Robot, a programming language and environment for teaching basic programming concepts to beginners. I recently jumped back onto the project to move Guido van Robot to the web. Originally written in python with a gtk front-end, I’ve rewritten Guido van Robot in JavaScript with an html front-end!

GvR-Online!

GvR-Online!

I have dubbed the result GvR Online! GvR Online is comprised of a core Javascript library that runs GvR programs, a jQuery powered editing/simulation environment, and a lightweight python web service with a RESTful json api for data storage that runs on Google App Engine.

The code base has just gotten cleaned up enough for other people to start hacking on it and I’m on the lookout for contributors. You might be wondering why GvR Online is a project worth working on, so let me tell you.

The desktop version of GvR has been used all across the world, in and outside of classrooms, to successfully introduce people to computer programming (it even runs on the OLPC XO laptop). I want the online version to be just as successful and more. With the online version, schools with locked down computer labs won’t need to install any additional software to run Guido van Robot in their classrooms. Students and teachers will be able to work on their GvR programs from any computer with an internet connection. Sharing GvR programs with others – programmers or not – will be easier than ever before. Beyond GvR Online itself, I hope to provide a reproducible example of how the web can be harnessed to create easy to use and massively distributable online learning tools. I can imagine an ecosystem of micro web applications that provide interactive learning environments for topics far beyond programming.

But if purely philosophical or philanthropic reasons are not enough to get you to work on GvR, then maybe the technology pitch will do it.

The technology that powers GvR is the future of the open web. GvR lives in the cloud. The code base lives in Mercurial, a distributed revision control system. The all new HTML 5 canvas tag is being used to render the GvR world. The primary programming language is JavaScript, the most popular programming language. The entire application runs off a RESTful web service that can be used to integrate with any other application. Imagine the possibilities of integration with projects like Bespin. I mean, this is cool stuff right?

So what are you waiting for?

Check out the site at http://gvr.carduner.net.

Download the code from http://bitbucket.org/pcardune/gvr-online/.

Read the api documentation at http://gvr.carduner.net/ui/docs/index.html.

And start hacking!

I’ve written another mini JavaScript game for fun.  This one is called Practice Pong.  There is only one player, and you just bounce a ball against a bunch of walls, but you get points and it gets harder to keep up with the ball after each hit.  The goal is obviously to keep the ball within bounds and rack up as many points as you can.  When you lose, there is a quick link to post your score on Twitter.  My highest so far is 55 points.

It was written for Firefox 3 as it uses no images.  The red ball is in fact just a div with some Firefox-only css (-moz-border-radius is my best friend).  Here is an excerpt from the help to get you started:

The goal of the game is to keep the ball inside the box using the paddle. The paddle will follow your mouse movements from inside the playing area. To start the game, just click anywhere inside the box.

There are a few improvements I’d like to make.  Some are not so interesting technically but are important for the game to feel complete; things like proper pausing (it is kind of screwed up right now) and restarting the game (for now you can just refresh the page).  On the flip side are some much more interesting technical bits like adding interactive sounds.  I’ve got half a mind to encode all the sounds directly into JavaScript using data urls. It’s one of those things that sounds so wrong, yet so right.

As usual, for posterity’s sake, here is a screenshot.  To try the game out for yourself, go to http://carduner.net/pong

picture-4

Namespaces are one honking great idea — let’s do more of those!
- Tim Peters, The Zen of Python

Almost every modern programming language has the concept of namespaces.  JavaScript is not among them.  Namespaces allow the developer to separate large blocks of code from each other using a well defined and language enforced naming convention.  When it comes to organizing large code bases in an intuitive hierarchical manner (as you might organize the file system on your computer), namespaces are indispensable. As JavaScript starts being used for more advanced applications (think client side form validation versus Google docs) the traditional ways of writing JavaScript begin to fall apart. Although namespaces are not a built-in language feature of JavaScript, it is not that hard to replicate similar functionality that can make your code much more manageable. In this short blog post I will try to describe a few of the ways to do namespacing along with their pros and cons.

One Example – Google

It’s hard to find a small example where namespaces are really useful. That’s because namespaces are primarily helpful for large projects. Google is a great example of a large project and it is no surprise that they use namespacing. Google has several different JavaScript frameworks for services such as search, maps, and rss feeds.  From one perspective, all this code belongs together because it is all made by Google and it is all made for accessing Google’s web services using JavaScript.  On the other hand, they have very little if anything to do with each other.  The API for search is completely different than the API for displaying a map or reading an RSS feed.  Namespaces allow you to group code at multiple levels of organization so that code can belong to both the very general “Google” code base, along with the more specific “search” or “maps” code base.  When every piece of code is associated with a namespace, you rarely wonder about what project it is for, where it is used, or what it does.  All that information is implied by the namespace (although sometimes rather tersely).

What are some ways you can duplicate namespaces in JavaScript?

Simple Naming Conventions

The simplest model that some people consider satisfactory for namespacing is to just use a naming convention.

Suppose we have three functions that are for doing searches, displaying maps, and reading RSS feeds.  We might define them with these names:

function search(query){ ... }
function map(geocode){ ... }
function read(rssUrl){ ... }

From the function names alone, it is really hard to tell what they do when we are considering the entire scope of all of Google’s web services.  It’s even worse if you consider that these functions could be used along side your own internal functions that have similar names but which do very different things.  It would be better to change these function names to:

function GSearch_search(query){ ... }
function GMaps_map(geocode){ ... }
function GRSS_read(rssUrl){ ... }

Now we can look at the function names and see that the G means its part of Google’s web service APIs, and in particular GSearch, GMaps, and GRSS correspond to the search, maps, and RSS feed APIs respectively.  Keep in mind that each one of the APIs will have lots of different functions related to their services.  For example,

function GMaps_map(geocode){ ... }
function GMaps_getGeocode(address){ ... }
function GMaps_plotMarker(geocode, description, icon){ ... }
...

The naming convention succeeds at communicating which functions belong together, but that is about it.  It becomes very tedious to always write out GMaps_ for every function call.  It also isn’t hard to imagine another company named “Geozone” coming out with their own maps service where they decided to prefix everything with G for Geozone. And we definitely don’t want to write out GeozoneMaps_ all the time.

Although a naming convention helps us out, we can do better.

Nested Objects

The next level up from using a simple naming convention is to use JavaScript’s simple object model to group functions together.  It would look something like this:

var google = {
  maps: {
    getGeocode: function(address){ ... }
    plotMarker: function(geocode, description, icon){ ... }
    ...
  },
  search: {
    search: function(query){ ... }
    findSimilar: function(searchResult){ ... }
    ...
  },
  rss: {
    read: function(rssUrl){ ... }
    subscribe: function(rssUrl, rssReader){ ... }
    ...
  }
};

Depending on how much experience you’ve had with JavaScript, you may be surprised that this is correct syntax.  At first this seems like a lot more code than just using the naming convention, but the benefits outweigh the downsides.  With this namespacing pattern you would call the functions like this:

google.maps.getGeocode("1600 Pennsylvania Ave. Washington DC");

Now we have the luxury of making longer and more descriptive namespace names (google is much better than just G) without worrying about having to always write out the full namespace for the function.  As soon as we get tired of writing google.maps.* everywhere, we can just reference that entire google.maps.* object structure with a shorthand variable like this:

var m = google.maps;
m.getGeocode("1600 Pennsylvania Ave. Washington DC");

Realistically speaking, it is probably not a good idea to stick entire namespace structures into a one character variable name – but it serves as a good example of just how far you can go.

Now the Google maps API is actually way more extensive than what I have outlined here.  Every few months, Google comes out with new amazing controls for their maps from simple zooming to alternate map types, miniature navigation, right on down to their street view interface.  All the code that handles these map controls should live under the same namespace.  You might end up with namespaces that are rather deep:

google.maps.control.streetview.showStreetView("some address");

Now it is really nice to be able to map google.maps.control.streetview to short variable name like streets so you get:

streets.showStreetView("some address");
streets.hideStreetView():
...

This is a lot better than writing GMapsControlStreets_showStreetView() constantly. But there are still problems with using a big fat object declaration to do namespaces.  The big nasty factor is that you have to define the entire google.* namespace – maps, search, and RSS – in a single JavaScript file.  That is definitely insane especially if you the web developer just want to use Google maps and don’t want to force clients to download all the code for search and RSS too just so you can have pretty namespaces.  And just imagine what would happen when Google adds new services to the JavaScript library?

In order to keep developers from going crazy working in one gigantic JavaScript file, different areas of code need to be broken out into different files.  As a simple case, we might have one file for each Google web service: maps, search, and RSS (in reality I’m sure they have tens if not hundreds).  We would have maps.js, search.js, and rss.js.

To still get our namespace magic we might consider putting in the full namespace object structure in each of the JavaScript files.  This solves the problem for someone who just wants to use one of the libraries.  They could have HTML like this:

<script type="text/javascript" src="maps.js"></script>
<script type="text/javascript">
  google.maps.geocode.getGeocode("1600 Pennsylvania Avenue");
</script>

Unfortunately, if you try to use two libraries on the same page, the namespaces end up colliding and whichever one is loaded last takes precedence.

<script type="text/javascript" src="search.js"></script>
<script type="text/javascript" src="maps.js"></script>
<script type="text/javascript">
  google.maps.geocode.getGeocode("1600 Pennsylvania Avenue");

  // the below won't work because the namespace structure in maps.js
  // redefined the "google" variable from search.js clobbering all the
  // search functions.
  //google.search.findSimilar("http://someurl.com")
</script>

Ideally we need a way to “merge” code from multiple files into the same namespace.  As it turns out, this isn’t actually that difficult.

Safely Constructing Nested Objects

At the top of each JavaScript file, instead of just defining the entire namespace object structure in one fell swoop, we can add to the namespace in stages, at each point making sure not to clobber any existing namespace with the same name.  So if we had one JavaScript file, control.js for everything mapped to the google.maps.control namespace, it would look like this:

// setup google.maps namespace safely
var google = google || {};
google.maps = google.maps || {};
// define google.maps.control namespace.
google.maps.control = {
  streetview: {
    showStreetView: function(address){ ... },
    hideStreetView: function(){ ... },
    ...
  },
  zoom: {
    zoomToLevel: function(level){ ... },
    zoomToFit: function(markers){ ... },
    ...
  }
  ...
};

Now suppose all of the JavaScript files are setup this way.  And we are loading several different ones like so:

<script type="text/javascript" src="google.search.js"></script>
<script type="text/javascript" src="google.maps.geocode.js"></script>
<script type="text/javascript" src="google.maps.control.js"></script>

Let’s walk through how the code in these scripts gets executed.

  1. First search.js is loaded.  It has in it the following line:
    var google = google || {};
    

    Looking at the google || {} part, since this is the first script to be loaded, there are no existing global variables defined.  Thus the variable named google will evaluate to undefined.  Normally this would raise a runtime error and the JavaScript would stop processing except that it is part of a boolean statement.  in JavaScript, undefined is equivalent to false in boolean expressions, so the next thing that will be evaluated is the {} section.  Since {} is equivalent to true in JavaScript, the statement google || {} will evaluate to {}. Remember that or blocks return the first object that evaluates to true, in this case the {}.  Now we’ve defined the variable google as a new empty object.  The google.search.js file would then go on to attach additional namespaces to the google object. So that once the file is fully loaded we would be able to call

    google.search.findSimilar("http://someurl.com");
    
  2. Next, the google.maps.geocode.js file gets loaded.  This has the same statement:
    var google = google || {};
    

    But as we saw in step 1, the google.search.js file already set the google variable to {} and added additional properties to the google object.  That means the statement google || {} evaluates to whatever the value of the existing google variable is, since it is equivalent to true in the boolean statement.  So in this particular case, the code is effectively equivalent to

    var google = google;
    

    which means we are not overriding the entire google namespace defined in google.search.js.

  3. Next we have a line that looks like:
    google.maps = google.maps || {};
    

    This works exactly the same way except that this time, since the google.search.js file did not specify the maps namespace, the expression google.maps || {} evaluates to {} and so we initialize the google.maps namespace.

  4. By the time google.maps.control.js has loaded, the google.maps namespace already exists and so we the expression above becomes equivalent to
    google.maps = google.maps;
    

    and we have managed to preserver the google.maps.geocode namespace defined in google.maps.geocode.js.

  5. Finally, we initialize the google.maps.control namespace in its entirety with the line:
    google.maps.control = {
      ...
    };
    

    because we know it is not defined in any other JavaScript file. At the end of this process, once all the JavaScript files are loaded, we have an object structure that looks like:

    var google = {
      search: {
        ...
      },
      maps: {
        geocode: {
          ...
        },
        control: {
          ...
        }
        ...
      }
    };
    

Namespacing in JavaScript Frameworks

All of this may sound like a lot of work, and the truth is that it is.  However, more and more frameworks are starting to use this method for namespacing and have provided helper functions to make it a lot easier.  Here are some examples:

  • ExtJS:
    Ext.ns("google.maps.control");
    google.maps.control.myFunc = function(){ ... };
    
  • YUI:
    YAHOO.namespace("google.maps.control");
    google.maps.control.myFunc = function(){ ... };
    

Namespacing on the Server

As some additional background on namespacing, here is how some other “modern” programming languages handle it.

  • PythonIn Python, namespaces are implicitly constructed from the file system path.  If we were writing these google namespaces in python, we would have a directory tree like so:
    google/
      __init__.py
      search.py
      maps/
        __init__.py
        control.py
        geocode.py
      ...

    Then if the google directory were on the python path, you could “import” these sections of the code from python like so:

    import google.maps.control
    google.maps.control.showStreetView()
    

    or alternatively like so:

    from google.maps import control
    control.showStreetView()
    

    You can see that we were able to replicate this functionality in JavaScript by reassigning a namespace section to a short variable name.

  • JavaIn Java, namespaces are explicitly defined in every .java file.  The control.java file would look like this:
    package google.maps.control;
    
    public class StreetView {
      public static void showStreetView(){ ... }
    }
    

    Although the package is defined explicitly in every .java file, the .java files themselves are typically placed in a directory structure just like python.  You would then access the namespace from another file like so:

    import google.maps.control.StreetView;
    
    public class Foo {
      public static void main(String[] args){
        StreetView.showStreetView();
      }
    }
    

Conclusions

From the Python and Java examples of namespacing, it is pretty clear that namespacing has become a fundamental part of modern programming languages – especially languages designed for building complex and maintainable software (even ActionScript has namespacing these days). Although JavaScript was not originally intended for building complex and powerful software, the demands of the market and the constraints of the browser have forced JavaScript in that direction. If you are a front-end developer that’s new to advanced JavaScript development, I urge you to take the leap now and start using namespaces. Although namespacing might be a conceptual high jump to take at first, you will be much happier in the long run with well organized code. If you are a back-end developer that fears JavaScript due to its lack of formalized patterns, fear not! There are ways to replicate the high level patterns you know and love and the entire web development community will benefit from your involvement in front-end technologies.

If there is anything I didn’t explain well enough, please let me know and I will try to augment that section with more descriptive text.

One of the first projects we were given in my high school computer science class with Jeff Elkner was to write a command-line English to Pig Latin translator in python.  I used this as an excuse to learn GUI development and ended up writing a version with TKinter (python’s Tcl/Tk binding).  I wish I still had the code for that lying around somewhere so I could put up a screen shot.  Needless to say, it was the most hideous GUI application you’d ever seen with tricked out fonts and garish colors.

Over the last eight years I’ve advanced a little in my programming technique.  As my first foray into what I hope is a series of mini web applications I decided to reimplement the Pig Latin Translator in JavaScript.  Here are a few features:

  • A classic console-inspired look and feel
  • Translation to and from Pig Latin
  • Automatic detection of the input language
  • A help screen for the technologically disinclined

Be sure to try it out at http://www.carduner.net/piglatin. Below is a screen shot (always for posterity’s sake).

Pig Latin Translator

Follow

Get every new post delivered to your Inbox.

Join 65 other followers