If you have ever wanted to use YUI in your next PhoneGap (Cordova) application you are in luck, it is so easy, it’s almost trivial. This will be the first in what will probably be a series of posts about using YUI with PhoneGap (Cordova).

This tutorial assumes that you already know the basics of YUI but, at least for this first instalment, you shouldn’t have any problem following along if you don’t. I am also not going to cover the installation and setup as it is covered in detail in the Getting Started Guide. If you have never built a PhoneGap app I highly recommend completing their Hello World application first. I will be progressing through using an HTC Incredible S with Android 2.3.3 but I will be sure to point out what is device or OS specific. The application that we are going to make is going to build upon the aforementioned Hello World application adding in a button to change the text color.

Most of the confusion with using YUI and PhoneGap (Cordova) is the combo loading - How do you use the Loader in a mobile app? The simple answer is that to get started we aren’t going to be. To accomplish our task we require a certain set of YUI modules. To simplify this tutorial we are going to use the YUI Configurator. In the All Module list, click on the Node and Event Rollups. These rollups include more code than necessary but it makes the setup easier.

The configurator will generate two script tags, be sure to select both urls individually Then copy the URL portions of the link generated by the configurator and paste it into the address bar of your browser. If you are having trouble with the configurator I have included the link to the two required files: File 1 and File 2

Copy the code which is in your browser window and save the code into your assets/www directory in yui-file-1.js and yui-file-2.js respectively, the order these files are loaded is important

Now we are going to get YUI loaded and set up in our index.html file with three additional script tags.

<script type="text/javascript" src="cordova-1.8.0.js"></script>
<script type="text/javascript" src="yui-file-1.js"></script>
<script type="text/javascript" src="yui-file-2.js"></script>
<script type="text/javascript">YUI_CONFIG = { bootstrap: false };</script>

First we load in the Cordova script followed by the two files generated by the YUI configurator plus one more to configure the YUI instance to follow. By setting bootstrap to false we are preventing YUI from trying to load the Loader. Additional details can be found in the bootstrap api documentation.

We are ready for our typical YUI().use statement. Add another script tag just above the closing body tag and instead of loading in specific modules like you normally would, we are going to load in all modules that have been loaded into the page using the * symbol.

<script type="text/javascript">
YUI().use('*', function(Y) {

	var button = Y.one('button'),
		header = Y.one('h1');

	button.on('touchstart', function(e) {
		header.setStyle('color', '#00B2EE');
	});

});
</script>

We use the Y.one() method to select our dom elements as usual and then attach an event listener to the button using the Node.on() method. But instead of using the typical click event, we substitute it for the touchstart event. Just for fun you can change the touchstart back to click and you will see that it works but there is a noticeable delay in the action - this will happen with any click event handler with PhoneGap (Cordova). we then take the element we assigned to the header property and set it’s color with the Node.setStyle() method.

And that’s it! You can now load this onto your device using the same method described in the PhoneGap (Cordova) example and you have made your very first YUI PhoneGap (Cordova) application. I hope you enjoyed this brief tutorial and if you have any questions feel free to comment below, mention me on twitter or join #yui on irc.freenode.net

<!DOCTYPE HTML>
<html>
<head>
<title>My First Cordova YUI App</title>
<script type="text/javascript" src="cordova-1.8.0.js"></script>
<script type="text/javascript" src="yui-file-1.js"></script>
<script type="text/javascript" src="yui-file-2.js"></script>
<script type="text/javascript">YUI_CONFIG = { bootstrap: false };</script>
</head>
<body>

<h1>YUI PhoneGap (Cordova) Example</h1>
<p>Click this button for a surprise</p>
<button>Click Me</button>

<script type="text/javascript">
YUI().use('*', function(Y) {

	var button = Y.one('button'),
		header = Y.one('h1');

	button.on('touchstart', function(e) {
		header.setStyle('color', '#00B2EE');
	});

});
</script>
</body>
</html>

The often requested Popup Calendar or as some may call it, Date Picker, is now finally available in the Yahoo CDN hosted YUI Gallery!

The YUI Popup Calendar component extends YUI’s Calendar Widget as well as a few other Widget modules you likely have already loaded on the page giving you a very lightweight (1.6KB minified) way to add date picker like functionality to your web page or application.

Additional details, examples, and more can be found by following the links below. I hope that you find this module helpful and I’ll continue to develop on it so please feel free to contact me with bug reports and feature requests.

This multi-part series will take you through everything from how to get a basic Node.js static file server going, to different way’s to deliver the content to the user, common pitfalls, caching, and when to use Node.js to serve files vs other solutions.

I’m going to assume you already have Node.js up running on your computer and that you know javascript. This code was developed on v0.6.1-pre and I will make an effort to update it and the version number as things change with Node.js.

First thing your going to do is create a new file which will house our server code.

touch server.js

There are a few native Node.js modules that we’ll need and a few more will be added as this series continues.

var http = require('http'),
    path = require('path'),
    fs = require('fs'),
    util = require('util');

We are pulling in the following modules: - HTTP: Low level API that handles the parsing of streams and handling of the HTTP messages. - Path: A string manipulation library, also allows you to check the file system for the existence of a file. - File System: Provides simple wrappers around standard POSIX functions for file IO. - Utilities: Extra utility features.

Create the server

Within the scope of this part of the tutorial we only need to access the HTTP module once for its createServer() method. We create a HTTP server which calls the _handler callback when a request event is made and start listening on a specified port.

http.createServer(_handler).listen(3000);
console.log('Server running at http://127.0.0.1:3000/');

Now set up the request handler which will house all of the code that handles each and every request that comes into your server. We’ll also define some variables which we’ll use later.

function _handler(req, res) {
    var root = "..",
        url = "",
        contentType = "text/plain",
        filePath = "";

        //All following code goes here
}

Because this is strictly a file server we only want to support the GET method and reject any other requests. To do this we need to reference the request object passed to our callback, an instance of http.ServerRequest stored in req

if (req.method !== 'GET') { //If the request method doesn't equal 'GET'
    res.writeHead(405); //Write the HTTP status to the response head
    res.end('Unsupported request method', 'utf8'); //End and send the response
    return;
}

On most web servers when you send a request to the server without a file you will get some type of default file usually the index.html. Because this is a file server, if they aren’t requesting a file, they are going to get an error. After we determine that they are requesting a file we do simple string concatenation to build the file path and then pass it to the path.exists() method. This method will test the file path for existence and then pass that information to the callback, serveRequestedFile in this case.

if ('.' + req.url !== './') {
    filePath = root + req.url;
    path.exists(filePath, serveRequestedFile);		
} else {
    res.writeHead(400);
    res.end('A file must be requested', 'utf8');
    return;
}

Stream your files

Now that we have set up the http server, verified that they are using the correct method, and that they are requesting a file, we need to serve that file to them. To do this we will use our previously mentioned serveRequestedFile() function which will receive the status of our file inquiry which we need to test before proceeding.

function serveRequestedFile(file) {
    if (file === false) {
        res.writeHead(404);
        res.end();
        return;
    }

    //Following code will go here

}

Because we aren’t implementing any caching mechanism yet we don’t want to read the whole file into memory before serving. To get around that we use the File System’s fs.createReadStrem() method. This method returns a Readable Stream which emits a number of events. We will set up an error event handler in the event there is a server error and the file cannot be served to the user.

var stream = fs.createReadStream(filePath);

stream.on('error', function(error) {
    res.writeHead(500);
    res.end();
    return;
});

Before we send the data to the user we should set the files mime type. For simplicity sake I have included the mime object within the function itself. On a true file server you would likely import a much larger mime list.

var mimeTypes = {
    '.js' : 'text/javascript',
    '.css' : 'text/css',
    '.gif' : 'image/gif'
}

contentType = mimeTypes[path.extname(filePath)];

We have now verified that the file exists, created a stream instance for the file in question, set up basic error handling, and found the files mime type. The only thing left is to actually send the file to the user. We first set the content type and the status in he header then use Utilities util.pump() method to push the previously initiated stream to the user. The pump method reads the data from the readable stream stream and sends it to the writable stream res. If there is an error in this process or the stream is closed the third arugment, the callback, is called.

res.setHeader('Content-Type', contentType);
res.writeHead(200);

util.pump(stream, res, function(error) {
    //Only called when the res is closed or an error occurs
    res.end();
    return;
});

Start the server and start serving files!

node server.js

I hope you enjoyed the first part of this series and were able to get your own file server up and running. I have included a working example of the above code if you want to see it in its entirety. If you need further help feel free to comment below, mention me on twitter, or join #Node.js on irc.freenode.net.

This information is accurate for YUI 3.5.0pr2 - there are cross browser issues with the following method on previous versions.

From time to time you may find yourself loading modules that aren’t available. Whether it be from a user supplied modules list or an unreliable source, the YUI instance confg gives you an easy way to check if there are any errors while loading your modules.

A common YUI config and use statement would look like the following.

YUI({
  modules: { // Define your custom module meta data
    'custom-module' : {
      fullpath: 'path/to/module.js'
    }
  }
}).use('node', 'custom-module', function(Y) {
  // Your code here
});

If for some reason the loader was unable to fetch a required module and you wanted to act on that event, the loader provides you with an onFailure method that is called with an object that has details about the modules it failed to fetch.

To use this method you would amend the above code to include that method.

YUI({
  modules: { // Define your custom module meta data
    'custom-module' : {
      fullpath: 'path/to/module.js'
    }
  },
  onFailure: function(error) {
  	// Error handling code
  }
}).use('node', 'custom-module', function(Y) {
  // Your code here
});

The error object is structured.

{
  data: Array[], // Contains a list of the requested modules
  msg: "Failed to load path/to/module", // A string error message
  success: false // A bool representation of the success of the requests
}

It’s that simple, by setting up your onFailure callback you can easily handle any errors that may arise by an improperly structured config or use statement.

Warning

If there is a failure in your initial module request the onFailure callback will be called before the Y instance is set-up. This means that no YUI methods will be available and calls to them will cause your application to fail. In this case the this property will point to YUI.

More Reading

In the following example the onFailure callback will be called if at any time there is a failure while trying to load modules. You will need to keep this in mind if your doing any lazy loading of modules so that you can appropriately handle the errors.

YUI({
  modules: { // Define your custom module meta data
    'good-module' : {
      fullpath: 'correct/path/to/good-module.js'
    },
    'bad-module' : {
      fullpath: 'path/to/bad-module'
    }
  },
  onFailure: function(error) {
  	// I'm going to be called when the request for bad-module is made
  }
}).use('node', 'good-module', function(Y) {

  Y.use('bad-module', function() {
  	// More code here
  });

});

The error.msg property string will contain comma delimited messages about which values failed.

YUI({
  modules: { // Define your custom module meta data
    'wrong-module' : {
      fullpath: 'path/to/wrong-module'
    },
    'bad-module' : {
      fullpath: 'path/to/bad-module'
    }
  },
  onFailure: function(error) {
  	console.log(error);
  }
}).use('node', 'wrong-module', 'bad-module', function(Y) {
  // Code here
});
{
  data: Array[22],
  msg: "Failed to load path/to/wrong-module,Failed to load path/to/bad-module",
  success: false
}

This method only works if the module has been defined. You will not receive an error for the crazy-module.

YUI({
  modules: { // Define your custom module meta data
    'wrong-module' : {
      fullpath: 'path/to/wrong-module'
    }
  },
  onFailure: function(error) {
  	console.log(error);
  }
}).use('node', 'wrong-module', 'crazy-module', function(Y) {
  // Code here
});

You will however get a string error logged to the console in addition to the onFailure if the undefined module comes before the defined custom modules.

YUI({
  modules: { // Define your custom module meta data
    'wrong-module' : {
      fullpath: 'path/to/wrong-module'
    }
  },
  onFailure: function(error) {
  	console.log(error);
  }
}).use('node', 'crazy-module', 'wrong-module', function(Y) {
  // Code here
});
"yui: NOT loaded: crazy-module"

After a year of trying to get the time to roll my own blog I cut my losses and set up with tumblr. The plan is to write a quality article every week on web development but you will probably see an odd one here and there about other things that I find interesting.

I hope you enjoy the things to come and I look forward to your comments.

-Jeff Pihach