Yes, I'm still alive, don't worry. The most important component to this entire project is the Ajax code used to retrieve chat messages from the database. Initially when you join a chat room, the entire chat history for that room will be repopulated into your window. When loading that chat, we just have an Ajax request grab the entire log from a JSON feed. That JSON feed can get pretty long depending on how busy the room is, so we store the entire log in the response body of the page.
However when the user is already in the chat, and we're updating the current chat, only a few messages are being pulled at a time, and not the entire updated log. There's a neat trick with Prototype that we can use to send smaller bits of message logs. For smaller tidbits of JSON (<8kb) it's possible to place the JSON object into a "X-Json" header, instead of the usual response body that everyone's used to.
You can download a quick python script I wrote up demonstrating this feature
here.
Here's the headers to a page taking advantage of the
X-Json feature.
Response Headers
Content-Type text/x-json
X-JSON {"messages": {"message": [{"text": "hey, this is pretty neat" ... snip
Transfer-Encoding chunked
Date Mon, 02 Jul 2007 13:55:29 GMT
Server CherryPy/3.0.1
The response body is empty.
The code is too long to post in full, but I'll annotate the import prototype/javascript bits. I tried to take advantage of all of the prototype features I could, I'm new to programming in prototype, but some of this code demonstrates some pretty nifty stuff that you can do with prototype.
new Ajax.Request('/json_feed', {
method: 'get',
onSuccess: function(transport, json) {
json.messages.message.each(function(message) {
$('messages').innerHTML += "[" + message.time + "] " + message.user;
$('messages').innerHTML += ": " + message.text + "
";
console.log(message.text);
});
},
onFailure: function() {
console.error("there was an error loading the messages...");
}
});
That's it! That's all the javascript we need to grab a JSON object, parse it, and display it on a page. Of course, we need to write callbacks and whatnot if we want the javascript to continually check for updates to the JSON feed.
The key to using the
X-Json header is in the
onSuccess portion of the code.
onSuccess: function(transport, json) { ....
By adding the second parameter (
json), Prototype will look in the X-Json header to see if there's any data there that it can use, if there isn't it will just return
null. Adding the X-Json header, and changing the Content-type in webpy is very simple. To do this we just change the
web.header dictionary.
web.header('Content-type', 'text/x-json')
web.header('X-Json', simplejson.dumps(message_list))
I also had a bit of fun fooling around with the
each method that is used to iterate through lists. You can check the out relevant code here.
json.messages.message.each(function(message) { ....
While this example is very simplistic, there are plenty of more useful and complicated cases you can use it in. What the each method allows you to do is iterate through lists in a much more elegant fashion than using your basic
for(int k = 0; k < node.length; k++) rigamarole.
I'm going to take an example from Andrew Dupont's
Prototype/Scriptaculous Crash Course, where you can apply a function to every single item in a list.
function makeRed(node) {
node.style.color = "red";
}
nodes.each(makeRed);
With this kind of functionality I could have made a function to auto-magically add each of the messages into the page, as opposed to writing the code inside of the each loop.
function addMessages(message) {
$('messages').innerHTML += "[" + message.time + "] " + message.user;
$('messages').innerHTML += ": " + message.text + "
";
}
json.messages.message.each(addMessage);
For more information about using the
X-Json header check out prototype's official documentation
here, and for more about using prototype's
each function you can look at the api as well.