Monday, November 17, 2008

HTML5 Cross Document Messaging

So I got to play with HTML5 Cross Document Messaging this weekend and built this quick demo -

[ demo ] [ source code ]

The demo demonstrates how easy it is for two iframe of different origins to talk to each other. In real time, each iframe is passing its own mouse coordinates from "onmousemove" event to each other.Right now, this feature is only supported in Firefox 3, IE8, Opera 9 and Safari Nightlies.

The basic semantic is to use postMessage() to send data to a window object and the receiving window should register for the "onmessage" event to receive data.
    window.document.onmousemove = function(e) {
      var x = (window.Event) ? e.pageX : window.event.clientX;
      var y = (window.Event) ? e.pageY : window.event.clientY;
      
      // this send data to the second iframe of the current page
      window.parent.frames[1].postMessage('x = ' + x + ' y = ' + y, '*');      
    };

    var onmessage = function(e) {
      var data = e.data;
      var origin = e.origin;
      document.getElementById('display').innerHTML = data;
    };

    if (typeof window.addEventListener != 'undefined') {
      window.addEventListener('message', onmessage, false);
    } else if (typeof window.attachEvent != 'undefined') {
      window.attachEvent('onmessage', onmessage);
    }
One thing to beware of is that for some reason it would not receive data properly if you use the traditional event listener setup for "onmessage" event. You have to use addEventListener() -
  window.onmessage = function() {
    // did not work in FF3
  };
This is a really neat feature and I can see it being extremely useful for platform builder or widget container to share common data easily and efficiently.

Tuesday, November 11, 2008

Using Gears Geolocation API + YouTube Geo Search

I just put together a sample to demonstrate combining both Gears Geolocation API and the new YouTube geo search.

I have also posted this on the official YouTube API blog.

[ Go to demo ]   [ Source code ]

Monday, November 3, 2008

console.log() everywhere

I love using console.log() from Firebug. I can't imagine debugging JavaScript without it. Althought Chrome, Firefox(with Firebug) and Safari all support console.log(), IE and some other browsers do not.

Another problem is that I am too lazy to remove or comment all the console.log() in the production code (actually they are quite useful to leave around for future maintenance).

So I have written this quick snippet that basically injects the console.log() into the window object on unsupported browsers. The "virtual" window.console will appear as a removable div (click to remove) on the top rightmost corner. And the snippet would selectively turn on and off console.log() based on whether the flag ?__debug__ is appended to the end of the URL.
/*
 * Selectively turn on or off (using http://url/?__debug__ to turn on) 
 * console.log for debug.  Also create a virtual console.log() if it doesn't 
 * exist already (IE)
 */
if (/__debug__$/i.test(document.location.href)) {
  if (!window.console) {
    window.console = {};
    window.console.log = function(message) {
      
      var divId = 'v_window_console';
      var consoleDiv = document.getElementById(divId);
      
      if (!consoleDiv) {
        // create the virtual window console        
        var consoleDiv = document.createElement('div');
        consoleDiv.id = divId;
        consoleDiv.style.position = 'absolute';
        consoleDiv.style.top = '0';
        consoleDiv.style.right = '0';

        consoleDiv.onclick = function() {
          consoleDiv.innerHTML = '';
        };

        document.body.appendChild(consoleDiv);
      } 

      messageDiv = document.createElement('div');
      messageDiv.innerHTML = message;
      
      consoleDiv.appendChild(messageDiv);
    }
  }       
} else {
  // non-debug mode, an empty function
  window.console = window.console || {};
  window.console.log = function(message) {};
}

You can also link to this file which contains this snippet and all other utilities function that I collect.

Serving minified JavaScript files with Google App Engine

JavaScript minification is the process that removes source code comments and newlines in order to create a compacted version of the file. This is especially useful when your web app is loading very large JavaScript files, minification can boost performance by cutting a big portion of the download time.

Since I host a lot of JavaScript using Google App Engine, I have made a script that can automate minification whenever JavaScript is requested. The goal is to make this process as transparent as possible. I am using Douglas Crockford JSMin in my python script.

So you just need to place all your JavaScript under a "js/" directory. When you request a file like this http://achau.appspot.com/js/cmc-core.js, this will serve original file without minification. But when you request the file with this URL http://achau.appspot.com/js_min/cmc-core.js, the minified version will be served.

To further enhance performance, I used the App Engine memcache feature so that file request will be served out of App Engine cache. This works great for stable files in production. But since files served out of memcache can be outdated (in javascript.py - default cache expires every 15 minutes), I have added extra access points to retrieve the latest copies directly from disk instead of cache. Use "js_/myfile.js" to access your original JavaScript files without hitting the cache and likewise use "js_min_/myfile.js" to access the minified JavaScript without hitting the cache.

This script can actually minfiy CSS files as well - http://achau.appspot.com/js_min/codemirror/css/jscolors.css

How to install:

1) Upload these two python scripts to your App Engine account: javascript.py and jsmin.py

2) Add this entry to your App Engine app.yaml file -
- url: /js.*
  script: javascript.py
Now your App Engine account is ready to serve minified JavaScript :)

Summary:

After the installation, you now have four different URLs to access your JavaScript files.