JavaScript on Modern Web

JavaScriptly


Archive for September, 2008


Simple Web Chat with Meteor Comet Server

Comet is not really brand-new (Ajax) term today; however, with most of people how it works remains somewhat mysterious.

I have a little hand-on experience with Comet when creating a hobby game project with DWR Reverse Ajax sometime ago. It (DWR) was simple to start and really worked but required a Java web server (Tomcat, Jetty…) that I found rather expensive (in term of resources) for such small application. I want a small, dedicated and reliable server for Comet apps while don’t like to be deeply sunk in technical terms like “Bayeux” or “Continuation”.

After reviewing the Comet Maturity Guide from Comet Daily I decided to give Meteor Comet server a try for several reasons, especially because it’s built on Perl that can be easily deployed in any server with Perl installed (i.e. almost all Linux servers, no?) and it should be lightweight (up to my experience with Perl). Here is other info about Meteor server:

  1. Server daemon will run on any platform for which Perl is available.
  2. In live use typically 1,000 clients per node receiving 2 msgs (~400 bytes) per sec each. Tested up to 5,000 clients per node receiving 1 msg/sec each.
  3. Nodes exist independently, supports broadcasting for message distribution. Cluster of three Meteor nodes runs FT Alphaville.
  4. Transports are completely configurable within simple constraints.
  5. Server supports client ‘catch-up’, allowing clients to regulate quality of service themselves.
  6. Stable, in production use.
  7. GPL v2. Free.

Install & Setup Meteor Server

You can find very good how-to guide for installing Meteor server from Meteorserver.org; actually it’s quite simple to follow. Just download, extract to default location (/usr/local/meteor) and configure daemon service as guided. One notice is on Fedora/Cent OS the start function in init scripts must be changed from:

sudo –u root ./meteord >/var/log/meteord 2>&1 &

to:

./meteord >/var/log/meteord 2>&1 &

After installed and run, the server will listen on 2 ports: 4670 and 4671 as default - the first port is “Subscriber port” and the second is “Controller port”. However, you typically want to serve your website on port 80 to avoid cross-site scripting restrictions, therefore, Meteor must also serve on the same port. Yes, the installation guide points out three possible ways to do that:

1. Set SubscriberPort to 80 and SubscriberIP to a specific IP, say X.X.X.1, in the Meteor config file. This will bind Meteor to a particular IP address on port 80 and leave Apache to bind to port 80 on a different IP. To make this work you will need to run Meteor as root, and configure Apache (or whatever web-server you’re using) to bind to port 80 on a different IP, say X.X.X.2.

2. Configure a firewall to redirect traffic on port 80 of one specific IP, to the Meteor Subscriber port (typically 4670). Then both Meteor and Apache can run in their default configurations, and Meteor can be run as a non-privileged user. So, if you’re using iptables you need the following commands:

1
2
3
iptables -t nat -I PREROUTING -p tcp --dport 80 --destination X.X.X.1 -j REDIRECT --to-port 4670
iptables -A INPUT -p tcp --dport 4670 -j ACCEPT
/etc/init.d/iptables save

or if you’ve got a hardware firewall, set up an equivilent set of rules on that.

3. Run Meteor on a different physical server to the one running Apache. Then each server need only have one IP, and Apache can be run in its default configuration. Meteor must be set to use port 80 and be run as root.

Well, in brief, you’ll need either another dedicated (virtual) server or to obtain another IP. But if you don’t have another server and don’t want to buy extra IP just for testing, I can tell you the forth way to get Meteor server work in the same machine and the same IP. The answer is to configure “Reverse Proxy” to pass all requests from specific domain to Meteor. Fortunately, it’s very simple to do that with Apache’s mod proxy. Here are what I did.

1. Make sure Apache to load “mod proxy” by commenting out the following line in httpd.conf file.

1
2
3
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so

2. Add “reverse proxy” for the sub-domain (like data.yourdomain.com) that points to Meteor

1
2
3
4
5
6
7
8
9
10
NameVirtualHost *:80
# other virtual hosts
#...
<VirtualHost *:80>
    	ServerAdmin admin@localhost
    	ServerName data.yourdomain.com
 
 	    	ProxyPass  /  http://127.0.0.1:4670/
    	    	ProxyPassReverse  /  http://127.0.0.1:4670/
</VirtualHost>

Restart Apache and you’ll see the magic work!

The Chat Application

I often start with a Comet solution by building an online chat so that I can test how fast and reliable the solution is across different browsers. Also, it’s interesting to see messages coming in real-time manner.

Of course, there is no rocket science here; it looks simple like the demo below.

Web Chat on Meteor Comet Server

Web Chat on Meteor Comet Server

Essentially, you’ll have to include Meteor’s scripts from sub-domain, data.javascriptly.com in this demo. This script file comes from Meteor server we configured as above and enclose all functions to pull messages in (near) real time.

<script type="text/javascript" src="http://data.javascriptly.com/meteor.js" charset="utf-8"></script>

Then, setup Comet connection on page load:

1
2
3
4
5
6
7
8
9
10
11
// Set this to something unique to this client
Meteor.hostid = "1234567";
// Our Meteor server is on the data. subdomain
Meteor.host = "data." + location.hostname;
// Call the addMsg() function when data arrives
Meteor.registerEventCallback("process", addMsg);
// Join the chat channel and get last 10 events, then stream
Meteor.joinChannel("chat", 10);
Meteor.mode = 'stream';
// Start streaming!
Meteor.connect();

Every time a message arrives, the addMsg() function will be executed to add it to chat console, simple enough!

Now, when you click “Send” button the message you’ve typed in will be sent to Apache/PHP server (not the Meteor server) using an Ajax POST.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sendMsg(){
	var msg = $('#message').val();
	if(msg.length == 0) 
		return;
 
	var user = $('#user').val();
	if(user.length == 0) user = "Guest";
 
	$.post('chat.php', {
		user: toCharRef(user), 
		message: toCharRef(msg)
	});
 
	$('#message').val("");
	$('#message').focus();
}

On receipt Apache/PHP will parse the message and write it to Meteor’s Controller which is listening on port 4671 like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
$ch = "chat";
$op = false;
// Open a controller channel to Meteor server
echo "Connecting to Meteor\n";
if (!($op = fsockopen("127.0.0.1", 4671, $errno, $errstr, 5))) {
	echo "Meteor not responding\n";
}
else{
	socket_set_blocking($op,false);
	echo "Connected\n";
	$haswritten = false;
	$buf = "";
	$out = "ADDMESSAGE ".$ch." <strong>".$user." ".$time."</strong> ".$message."\n";
	...
}

Meteor server, then, delivers added message to all clients subscribing to “chat” channel. That’s all.

The app works seamlessly on IE6/7/8, Firefox 3, Safari 3.1 while on Opera 9.2 and Google Chrome Meteor uses “simple forever IFRAME” technique for streaming which makes hourglass pointer display all the time. Hope Andrew Betts and team will find some workaround to avoid this issue in next version?

Chat message supports Unicode (the feature many developers ignore) so you can chat in any language you like: German, French, Chinese, Japanese and English of course. Please feel free to leave your comments/questions here.

Share and Enjoy:
  • description
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Mixx
  • SphereIt
  • TwitThis
  • NewsVine
  • Google
  • Technorati
  • Facebook

Quick & Useful JQuery Plugins

Here are some quick and useful JQuery plugins from Jason Frame’s “JQuery Grab Bag“.

Auto-Grow TextArea

The technique is borrowed from Facebook that uses an off-screen <div> to calculate the required dimensions of the textarea to reveal all texts inside instead of to display vertical scrollbar. Run the following code line to enable auto-grow behavior to all textareas on page.

$('textarea').autogrow();

Online Demo

Input Hint

It’s not always necessary to attach label to every text field in web form, instead you can use hint to tell user what to type in the fields. This plugin will help keeping away from tedious codes to add hints to input boxes.

First, add hint attribute to input fields.

<input type="text" hint="Your first name" name="firstname" />
 
<input type="text" hint="Your last name" name="lastname" />

Then, simply get the functionality work on the fields.

$('*[@hint]').inputHint();

Online Demo

Text Resizing from Cookie

This is not a JQuery’s plugin but still depends on JQuery to allow user to select appropriate text size on reading. Of course, some modern browsers feature zoom functionality but not equally good. So, users still want to resize text on fly.

We’ll need to define some styles with different font sizes for body tag.

body.small { font-size: 10px; }
body.medium { font-size: 12px; }
body.large { font-size: 14px; }

And some links for size selection accordantly.

<div id='sizer'>
      <a href='#' class='small'>small</a>
      <a href='#' class='medium'>medium</a>
      <a href='#' class='large'>large</a>
</div>

Then call cookieResize() function on load. Done.

cookieResize('#sizer a', 'medium');

Online Demo

Download example codes

Share and Enjoy:
  • description
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Mixx
  • SphereIt
  • TwitThis
  • NewsVine
  • Google
  • Technorati
  • Facebook

Unobtrusive Draggable Tabbed Navigation

There are many examples on creating tabbed navigation with (or without) help of JavaScript frameworks like Prototype, MooTools or JQuery. However, I find that it’s much easier to create draggable tabbed navigation using Chain.js and its great extension: Interaction.js.

Riziq, creator of Chain.js & Interaction.js, already showed an example on how to utilize the libs to build tab interface in a few JS code lines. However, for its own purpose, the example is not SEO-friendly — disabling JavaScript in your browser will result in empty content and search engines will see nothing on your page consequently. Now say, you want to create a tabbed navigation for your blog to show/hide “Latest Posts”, “Latest Comments” etc. and you want the links can be seen no matter if JavaScript is enabled or not in reader’s browser — something looks like this:

Tabbed navigation for my blog

Blog's tabbed navigation

Step 1: HTML & CSS

Just create a template in HTML and full links to latest posts, comments and most popular articles:

<div id="wrapper">
	<ul id="tabs">
		<li class="tab"><a href="#" class="title">Tab</a></li>
	</ul>
 
	<div class="content" id="latest_posts">
		<h2>Latest Posts</h2>
		<p></p>
	</div>
 
	<div class="content" id="latest_comments">
		<h2>Latest Comments</h2>
		<p></p>
	</div>
 
	<div class="content" id="most_pop">
		<h2>Most Popular</h2>
		<p></p>
	<div>
</div>

And here is the style sheet to get tabbed look:

#wrapper * {
    margin: 0;
    padding: 0;
}
 
#wrapper {
    background: #000;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 0.8em;
    padding-bottom: 1px;
    padding: 15px;
    width: 400px;
}
 
#wrapper ul {
    float: left;
    list-style: none;
}
 
#wrapper li {
    display: block;
    float: left;
    margin: 0 6px 0 0;
}
 
#wrapper li a{
	display: block;
    float: left;
    padding: 9px 15px;
    text-decoration: none;
}
 
#wrapper div {
    background: #fff;
    clear: left;
    height: 200px;
}
 
#wrapper .content{
	padding: 10px 15px;
}
 
#wrapper .content p{
    line-height: 1.6em;
    padding-top: 0.5em;
}
 
#wrapper .content a{
	color: #666;
}
 
.tab a {
    background: url("off_tab.png") top left repeat-x;
    color: #f2f2f2;
}
 
.tab.selected a{
    background: url("active_tab.png") top left repeat-x;
    color: #000;
}

At this point, the page looks fairly good and search engines (Yahoo, Google…) will be surely happy with it.

Step 2 - Creating Tabs and Link Them to Contents

It’s very simple to create tabs with Chain.js. The following scripts will do the job (for those who are not familiar with Chain.js, please read my articles here and here).

1
2
3
4
5
6
7
8
$('#tabs')
	.items([
		{id: 'latest_posts', title:'Latest Posts'},
		{id: 'latest_comments', title:'Latest Comments'},
		{id: 'most_pop', title:'Most Popular'}
	])
	.selectable({required:true}) /* force the first tab selected */
	.chain();

Now, we need to link tabs with related contents in div tags specified by id field. On tab clicked, all other contents should be hidden but linked one; this function is to accomplish that.

1
2
3
4
5
6
function showContent(id){
	$('.content').each(function(i, el){
		$(el).hide(); // hide all tabs
	});
	$('#' + id).show(); // show selected tab
}

By customizing default chain() method, we can inject onclick event to each tab.

8
9
10
11
12
13
.chain(function(){
	var self = this;
	this.click(function(){
		showContent(self.item().id);
	});
}

All codes are as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$(document).ready(function(){
	$('#tabs')
		.items([
			{id: 'latest_posts', title:'Latest Posts'},
			{id: 'latest_comments', title:'Latest Comments'},
			{id: 'most_pop', title:'Most Popular'}
		])
		.selectable({required:true}) /* force the first tab selected */
		.chain(function(){
			var self = this;
			this.click(	function(){
				showContent(self.item().id);
			});
		});
 
	showContent('latest_posts');
});
 
function showContent(id){
	$('.content').each(function(i, el){
		$(el).hide(); // hide all tabs
	});
	$('#' + id).show(); // show selected tab
}

Step 3 - Enable Drag-n-Drop

Of course, in practice, there is almost no need to make tabs draggable for a blog’s tabbed navigation. But if required, it can be fulfilled by just adding 2 more functions, draggable() and  sortable(), before the chain() method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$('#tabs')
	.items([
		{id: 'latest_posts', title:'Latest Posts'},
		{id: 'latest_comments', title:'Latest Comments'},
		{id: 'most_pop', title:'Most Popular'}
	])
	.selectable({required:true}) /* force the first tab selected */
	.draggable({axis:'x'})  /* enable drag-n-drop */
	.sortable({align:'horizontal'})
	.chain(function(){
		var self = this;
		this.click(	function(){
			showContent(self.item().id);
		});
	});

So now we have a tabbed navigation with advanced functionality implemented in a few code lines which are completely separated from HTML. And like other examples here, you can see demo and download source codes from links below:

Share and Enjoy:
  • description
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Mixx
  • SphereIt
  • TwitThis
  • NewsVine
  • Google
  • Technorati
  • Facebook

LightningDOM to Balance DOM vs innerHTML

Using DOM API versus innerHTML to create/change content on webpage has been in discussion for years. The pain (slow DOM manipulation speed) left by Internet Explorer causes a big concern over DOM API for serious use in practice because the browser is still holding big slice of market share today (about 80%).

We can guess that the browser war (re)started by Firefox and stirred up by Google Chrome lately will result in better IE versions, yet that bright day won’t come any time soon.

Recently, I had reviews on PURE and Chain.js as client-side template engines. Though both libraries take pure, unobtrusive JavaScript approach for implementation, PURE seems more eager about speed when using innerHTML to create elements while Chain.js depends on DOM API for more flexible and direct manipulation.

I cannot say what approach is better. PURE may have some advantage on page with huge number of DOM elements to be created/changed but I think most of projects will be happy with Chain.js for its power. It grows a question: any chance that we can achieve both flexibility and speed in these libs? Probably, LightningDOM donated by Peter Rust will be a right answer.

In the post, Peter places LightningDOM as “a layer between the raw HTML and your templating/data-mapping code, without the slowness of the DOM.” So how it works? Just see the example.

1
2
3
4
var strMarkup = document.getElementById('destination').innerHTML;
var dom = new LightningDOM(strMarkup);
dom.firstChild().innerText("I Love speed!");
document.getElementById('destination').innerHTML = dom.outerHTML();

Not too difficult to understand, right? You give all HTML markups to LightningDOM, do anything you want with familiar getters/setters like you can do with DOM API then assign the result back to real DOM’s innerHTML.

Peter tells more about internal implementation to get both speed and lightness in the library.

The LightningDOM parses the given HTML using Regexes. I have two different parse algorithms (but both using the same regex). IE is much faster at parsing the whole markup by doing a single regex replace to convert the markup into JSON and then to eval the JSON. FF (and I’m guessing Chrome & Opera) is much faster at looping through the markup with a global regex.

All the tags are parsed into a two-dimensional array (well, technically an array of arrays), which represents all the information in the tags. Here’s a pretty look at the internal data structure representing a chunk of HMTL (note that we use a simpler object model inspired by ElementTree to representing the inner-text as a “tail” of the previous tag, rather than a nested “Text Node”).

Array of Arrays (_aMarkup):

Of course, the decision to utilize LightningDOM for PURE or Chain.js depends on leaders of projects but it does not prevent you from taking advantage of the lib if you see true value from it. Personally, I think it’s a good solution though I’ll have more tests/benchmarks to evaluate its stability across browsers.

Share and Enjoy:
  • description
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Mixx
  • SphereIt
  • TwitThis
  • NewsVine
  • Google
  • Technorati
  • Facebook

Chain.js v0.2 – Updated Examples

In previous post, I had a chance to introduce Chain.js with 3 examples and found a few issues due to both bugs in version 0.1 and my unawareness of implemented features. Now version 0.2 of Chain.js has just come out, I’d like to revise the examples to show better implementation we can do with the library in practice.

Basically, we don’t need to call chain() function every time we add or replace items instead just execute it once on DOM ready event, and we can utilize anchor property to define where items to be rendered within table tag.

Example 1: All codes to get it works are here.

1
2
3
4
5
6
7
8
9
10
11
12
13
function render(){
	$.getJSON('json.php', 'lang=' + $('#langList').val(), function(books){
		$('#table_book').items('replace', books);
	});
}
 
$(document).ready(function(){
	$('#table_book').chain({
		anchor: 'tbody' /* defines, where items will be rendered*/
	});
	$('#langList').change(render); 	// handle change event of drop-down list
	setTimeout(render, 100); 		// call render function on load
});

Example 2: Almost the same codes as example 1 plus add, remove and sort functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function render(){
	$.getJSON('json.php', 'lang=' + $('#langList').val(), function(books){
		$('#table_book').items('replace', books);
	});
}
 
function addData(){
	var data = {title: $('#input_title').val(),
		publisher: $('#input_publisher').val(),
		price: $('#input_price').val()};
 
	$('#table_book').items('add', data);
}
 
function sortBy(col){
	$('#table_book').items('sort', col, {toggle:true});
}
 
$(document).ready(function(){
	$('#table_book').chain({
		anchor: 'tbody', /* defines, where items will be rendered*/
		bind: function(){ /* bind event */
			this.dblclick(function(){
				$(this).item('remove');
			});
		}
	});
 
	$('#langList').change(render); 	// handle change event of drop-down list
	setTimeout(render, 100); 		// call render function on load
});

Example 3: Custom data binding looks much easier now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function render(){
	$.getJSON('json.php', 'lang=' + $('#langList').val(), function(books){
		$('#table_book').items('replace', books);
	});
}
 
$(document).ready(function(){
	/* Custom data binding */
	$('#table_book').chain({
		anchor : 'tbody', /* defines, where items will be rendered*/
		'.price' : {
			style: 'text-align: right',
			content: '${price.toFixed(2)}'
		},
		'.order .order-link': {
			style: 'color: red;',
			href: '{orderLink}',
			target: '_blank',
			content: 'Order Now!'
		}
	});
 
	$('#langList').change(render); 	// handle change event of drop-down list
	setTimeout(render, 100); 		// call render function on load
});

Thanks Rizqi Ahmad for kind support to complete these examples.

Download updated example codes

Share and Enjoy:
  • description
  • Digg
  • del.icio.us
  • StumbleUpon
  • Reddit
  • Mixx
  • SphereIt
  • TwitThis
  • NewsVine
  • Google
  • Technorati
  • Facebook

Zend Framework 1.6 Comes with Dojo and FirePHP Integrated

Probably the biggest change in Zend Framework (ZF) 1.6 is about Ajax support with integration of famous Dojo JavaScript toolkit. ZF 1.6 comes with several new helpers, elements and components that help to display/interact with Dojo widgets as announced on Zend’s official site.

  • A dojo() placeholder view helper to facilitate Dojo integration in your views, including setting up the required script and style tags, dojo.require statements, and more. In essence, this supports and enhances Dojo’s modularity at the application level.
  • Zend_View helpers and Zend_Form elements and decorators that utilize Dijit, Dojo’s layout and widget platform. This simplifies creation of Zend_Form elements that can be rendered as Dijits. For instance, highly interactive widgets such as calendar choosers, time selectors, and combo-boxes are provided.
  • Zend_Dojo_Data, a component for creating dojo.data-compatible response payloads. dojo.data defines a standard storage interface; services providing data in this format can then be consumed by a variety of Dojo facilities to provide highly flexible and dynamic content for your user interfaces.
  • A JSON-RPC server component: Zend_Json_Server. JSON-RPC is a lightweight remote procedure call protocol, utilizing JSON for its serialization format; it is useful for sites that require a high volume of interaction between the user interface and server-side data stores, as it allows exposing your server-side APIs in a format directly accessible via your client. Dojo has native JSON-RPC capabilities, and Zend Framework provides a JSON-RPC implementation that is both compatible with Dojo and the published specifications.

In order to help developer track JavaScript bugs, ZF 1.6 also provides native support for FirePHP, a Firebug extension for PHP-Ajax development. Using ZF 1.6 now you can log to Firebug Console via FirePHP instantly, no need to download the FirePHPCore library.

Two primary components are available — Zend_Log_Writer_Firebug and Zend_Db_Profiler_Firebug - providing functionality to log (any info you want) and profile database queries to Firebug console. Read more how to utilize FirePHP in ZF 1.6 here.

Share and Enjoy: