Christian Heilmann

You are currently browsing the Christian Heilmann blog archives for May, 2008.

Archive for May, 2008

@media2008 report live on YDN

Friday, May 30th, 2008

It is 1.14am after the first day of @media2008 and I just finished my report over on the YDN blog:

See you there tomorrow morning for day two, I am off to bed.

Providing script configuration in-line and programatically

Friday, May 23rd, 2008

One of the things I’ve been quite consistent and pushy about when writing code is to separate out all the things that should be customizable into an own configuration object.

A normal script I write these days would look like this:

var module = function(){
  // configuration, change things here
  var config = {
    CSS:{
     classes:{
       hover:'hover',
       active:'current',
       jsEnabled:'js'
     },
     ids:{
       container:'maincontainer'
     } 
    },
    timeout:2000,
    userID:'chrisheilmann'
  };
 
  // start of main code 
  function init(){
 
  };
  // ... more methods and other code ...
 
  // make init a public method
  return {
    init:init
  };
}();
module.init();

The benefits should be quite obvious:

  • Implementers don’t need to hunt through the whole script to find what they need to change
  • There’s a clear separation of “change what you need to change here” and “only touch this if you know what you are doing” – allowing more developers to use your code.
  • You show implementers in a single location where you overlap with other development layers, for example by defining IDs of HTML elements you use and CSS class names you apply to generated elements.
  • Having all the strings in one place makes for easier localisation and also speeds up the script (IE6 creates a string object for every string – even in conditions inside loops for example!)

I was quite OK with that until Ara Pehlivanian asked for an option to programatically override the configuration files, much like the YUI allows you to do with the YAHOO.util.Config utility. He is right of course, sometimes you’d want to change the config and re-initiate the script (the other way of course is to write a module with instantiation).

The easiest way to approach that is to make the config object public:

var module = function(){
  // configuration, change things here
  var config = {
    CSS:{
     classes:{
       hover:'hover',
       active:'current',
       jsEnabled:'js'
     },
     ids:{
       container:'maincontainer'
     } 
    },
    timeout:2000,
    userID:'chrisheilmann'
  };
 
  // start of main code 
  function init(){
 
  };
  // ... more methods and other code ...
 
  // make init and config a public method
  return {
    init:init,
    config:config
  };
}();

That way you can override the properties you need before you call init():

module.config.CSS.ids.container = 'header';
module.config.userID = 'alanwhite';  
module.init();

However, Ara thought it more convenient to be able to provide an object as a parameter to init() that overrides certain properties. You can do that by checking for this object, looping through its properties and recursively trying to find and match a property of the config object:

var module = function(){
  // configuration, change things here
  var config = {
    CSS:{
     classes:{
       hover:'hover',
       active:'current',
       jsEnabled:'js'
     },
     ids:{
       container:'maincontainer'
     } 
    },
    timeout:2000,
    userID:'chrisheilmann'
  };
 
  // start of main code 
  function init(){
    // check if the first argument is an object
    var a = arguments;
    if(isObj(a[ 0 ])){
      var cfg = a[ 0 ];
 
      // loop through arguments and alter the configuration
      for(var i in cfg){
        setConfig(config,i,cfg[i]);
      }
    }
  };
 
  function setConfig(o,p,v){
    // loop through all the properties of he object
    for(var i in o){
      // when the value is an object call this function recursively
      if(isObj(o[i])){
        setConfig(o[i],p,v);
 
      // otherwise compare properties and set their value accordingly
      } else {
        if(i === p){o[p] = v;};
      }
    }
  };
 
  // tests if a parameter is an object (and not an array)
  function isObj(o){
    return (typeof o === 'object' && typeof o.splice !== 'function');
  }
  // ... more methods and other code ...
  // make init a public method
  return {
    init:init
  };
}();
module.init({
  container:'header',
  'timeout':1000
});

This works swimmingly when all the configuration properties are unique. It fails though when a property in a nested object has the same name as another one on a higher level. In order to allow for this, we can offer the option to send a string with the path to the property as the property name. Then it gets messy as we need to eval() that string and make sure we return the value in the right format. All in all it could look like this:

var module = function(){
  // configuration, change things here
  var config = {
    CSS:{
     classes:{
       hover:'hover',
       active:'current',
       jsEnabled:'js'
     },
     ids:{
       container:'maincontainer'
     } 
    },
    timeout:2000,
    userID:'chrisheilmann'
  };
 
  // start of main code 
  function init(){
    if(isObj(arguments[ 0 ])){
      var cfg = arguments[ 0 ];
      for(var i in cfg){
        if(i.indexOf('.')!== -1){
          var str = '["' + i.replace(/\./g,'"]["') + '"]';
          var val = getValue(cfg[i]);
          eval('config' + str + '=' + val);
        } else {
          setConfig(config,i,cfg[i]);
        }
      }
    }
  };
  function setConfig(o,p,v){
    for(var i in o){
      if(isObj(o[i])){
        setConfig(o[i],p,v);
      } else {
        if(i === p){o[p] = v;};
      }
    }
  };
  function isObj(o){
    return (typeof o === 'object' && typeof o.splice !== 'function');
  };
  function getValue(v){
    switch(typeof v){
      case 'string':
        return "'" + v + "'";
      break;
      case 'number':
        return v;
      break;
      case 'object':
        if(typeof v.splice === 'function'){
          return '[' + v + ']';
        } else {
          return '{' + v + '}';
        }
      break;
      case NaN:
      break;
    };
  };
 
  // ... more methods and other code ...
  // make init a public method
  return {
    init:init
  };
}();
module.init({
  'container':'header',
  'CSS.classes.active':'now',
  'timeout':1000
});

In order to make that readable, let’s encapsulate all the configuration alteration code in an own module:

var module = function(){
  // configuration, change things here
  var config = {
    CSS:{
     classes:{
       hover:'hover',
       active:'current',
       jsEnabled:'js'
     },
     ids:{
       container:'maincontainer'
     } 
    },
    timeout:2000,
    userID:'chrisheilmann'
  };
 
  // start of main code 
  function init(){
    console.log(config);
  };
 
  // ... more methods and other code ...
 
  // Configuration changes 
  var changeConfig = function(){
    function set(o){
      var reg = /\./g;
      if(isObj(o)){
        for(var i in o){
          if(i.indexOf('.')!== -1){
            var str = '["' + i.replace(reg,'"]["') + '"]';
            var val = getValue(o[i]);
            eval('config' + str + '=' + val);
          } else {
            findProperty(config,i,o[i]);
          }
        }
      }
    };
    function findProperty(o,p,v){
      for(var i in o){
        if(isObj(o[i])){
          findProperty(o[i],p,v);
        } else {
          if(i === p){o[p] = v;};
        }
      }
    };
    function isObj(o){
      return (typeof o === 'object' && typeof o.splice !== 'function');
    };
    function getValue(v){
      switch(typeof v){
        case 'string': return "'"+v+"'"; break;
        case 'number': return v; break;
        case 'object':
          if(typeof v.splice === 'function'){
            return '['+v+']';
          } else {
            return '{'+v+'}';
          }
        break;
        case NaN: break;
      };
    };
    return{set:set};
  }();
  // make init a public method
  return {
    init:init
  };
}();
module.init({
    'container':'header',
    'CSS.classes.active':'now',
    'timeout':1000
});

And that is one way to provide a configuration object and make it possible to change it programatically in the init() method. Can you think of a better one?

An unobtrusive badge for Google Reader’s shared items

Wednesday, May 21st, 2008

I am a user of Google Reader to get through the vast amounts of RSS feeds I subscribed to. I think it is safe to say that reading RSS and twittering has replaced most of my web surfing.

Like most big RSS readers, Google reader also allows you to share great finds you had with people who want to and are in your social neighbourhood. You can either get these finds as a feed or as a little badge (called a clip in Google lingo) to include in your blog or other sites.

The out-of-the-box version of this badge can be customized and results in two JavaScript includes which write out the badge.

That is nice, but I don’t quite care for things that could offer functionality without JavaScript but don’t bother, which is why I checked more closely what the Google badge does.

If you look at the generated script includes you’ll find for example the following URL ( added spaces to avoid breaking my blog :) )

http://www.google.com/ reader/public/javascript/ user/07479231772993841072/ state/com.google/broadcast? n=5&callback=GRC_p%28%7Bc%3A%22green%22%2Ct %3A%22Christian%20Heilmann%27s %20shared%20items%22%2Cs%3A%22false%22%7D%29%3Bnew%20GRC

Clicking this will get you a JSON object with a wrapper function (and for some reason a comment that this is a JavaScript file), which means you can use this for your own purposes.

All you need is your user ID, which you can get this one easily from your shared items homepage that Google Reader offers. In my case this is http://www.google.com/reader/shared/07479231772993841072.

The other interesting parameters of the JSON API are the n parameter defining the amount of items and the callback parameter defining the name of the function call wrapped around the JSON data.

Putting all of this together it was easy to create a badge that uses the following HTML to show off my shared items on Google Reader.


Visitors without JavaScript will still be able to click through to the page of my shared items. Those with JavaScript will get the latest five.

You can see the badge in action and download it for yourself on the demo page (using tutorialbuilder):

How SearchMonkey can lead to a cleaner, more data-rich web

Friday, May 16th, 2008

I normally don’t write about products of my company here, but I want to quickly talk about SearchMonkey, a new service by Yahoo available for developers right now.

I spent some time with journalists from several magazines yesterday explaining the technicalities of SearchMonkey following a presentation about the business and end user benefits explained by a colleague who knows more about that area. This opened my eyes as to what a massive impact SearchMonkey will have if we use it the right way.

What is SearchMonkey and what is the business or user benefit?

On the surface, SearchMonkey means that Yahoo! opens their search result pages to write plugins for them. You can individually style search results and provide “a much richer user experience”. In other words you can actually write “monkeys” that allow people to deep-dive into your site from the search results provided by Yahoo.

Say for example you are netflix or any other DVD rental company and you want people to rent the movie. You could write a monkey that styles search results by imdb.com or other film review sites to show the cover of the DVD, a synopsis and a link to netflix to rent the movie.

The benefits are that the end user does not need to load the IMDB page if she’s happy with the information in the synopsis and you as netflix get a lot of traffic you hadn’t had before.

Splendid, that gets the end users and business people happy, but how does it lead to a cleaner web?

How can you use this to make the web a cleaner place?

Well, this is a strong business case to show to people. Search result pages are boring and people tried to game them with SEO techniques (and abominations) for years – people love to be found. Yahoo! opening their SERPs (Search Engine Result Pages) to the world means first of all you can offer a monkey as a service if you are a developer or small agency.

Monkeys are built upon data available about the document. In its most basic form that is the information Yahoo is keeping in its index – titles, descriptions and other meta information. You can use this information to add more HTML to the SERP showing it, but the harvest is on the meager side of things.

To battle that problem, SearchMonkey allows you to offer “Custom Data Services” to get more information to display. These services could be an XSLT that grabs information from the document, an API that returns an XML in a certain DataRSS format or – and here is where it gets really interesting – RDF or microformats information in the document.

Scraping a document with XSLT always feels wrong to me as the HTML structure is impacted by so many factors – the other issue of course is that it is very memory and computation hungry.

Providing an own API returning XML is of course the optimal way of offering data, but you may not have the resources or drive to do so.

Which leaves microformats and RDF. Right now we support hAtom, hCalendar, hCard, hReview and XFN in terms of microformats and embedded RDF and that makes it a lot easier to retrieve information.

There is a leveraging the Data Web section in the SearchMonkey documentation that explains this in detail (and I am sorry someone considered “leveraging” good language).

So, all in all SearchMonkey is not only a massive step in opening the web for developers (you can style search results without paying for that – something that would have given marketing people seizures not too long ago), it also means that we finally have a channel to display the data we add in microformatic or RDF format that is not a Firefox extension or a specialized search engine. With information in the page developers (either in-house or external) can create customized search results in one step without having to rely on the structure on your document and without having to set up both a monkey app and a data service.

On the flipside, even the idea that someone can use SearchMonkey to enhance search results with data provided by you (and link back to you) if it is easy to write an XSLT to get to the data is a mighty argument to show to people when you want to promote clean HTML structure (no, I won’t call it POSH, as there is nothing plain and old about good, semantic HTML - it is not common practice, sadly enough).

Check out the SearchMonkey information page to get started or just dive into developing your first monkey – the options are endless.

In praise of mistakes

Thursday, May 15th, 2008

I’ve been talking to a lot of developers lately in order to make people work better together and get things done faster. One thing that keeps fascinating me is the length people go to not to communicate mistakes and problems.

We are very much happy to celebrate successes and point out the obvious benefits of what we produce and do, but there is an innate tendency not to own up to mistakes or to welcome people telling you what is wrong.

I once heard the quote “a good friend tells you they like your shirt, a great friend tells you that you have dirt on your nose before you go on a date” (I think it was on Sherman’s Lagoon).

Mistakes are a great thing. They hurt, they make us think, they make us angry and they make us learn. The one wrong thing we do with them is to take these negative gut reactions and vent them in the wrong direction in most of the cases.

This is how we are wired, the first cave-man finding a burning branch after a lightning storm will have put his hand in the fire and quickly found out that it is a bad plan. He then most probably told others (the ones he liked) not to do that. He probably also pondered to use that fire against the ones he doesn’t like – sadly enough something we seem to excel at as humans.

Making mistakes makes us fell inadequate and uneasy – we failed as the crown of evolution and should be better than that. They also makes us feel protective – we don’t want to own up that we make mistakes as that would make us look weak or stupid in the eyes of others. This is a mistake in itself as we all are fallible and do stupid things all the time.

Case in point: I was chuffed when my first A List Apart article was posted – having been a fan of the site for ages. The article JavaScript image replacement sparked a very strong counter-post by Peter-Paul Koch, aptly named Why ALA’s ‘JavaScript Image Replacement’ Sucks.

I was shattered. PPK was someone I looked up to and his post accused me of stealing his idea and code (we solved that riddle on a mailing list proving with time stamps that I did indeed neither) and his critique was partly very right but in other parts just over-zealous.

I was hurt, I was confused and – well – pissed off. Instead of grumpingly packing in or shooting back I actually looked at my code and fixed the issues PPK talked about. I also started communicating more closely with him and this started a long, great discussion and sharing of information and contacts.

I grew a lot by my mistake, and there are no hard feelings between PPK and me – I will be one of the experts talking at the conference he organizes later this year.

In short – making a mistake, being called upon it and fixing it made me a much better developer. It humbled me and made me realize that working with others (even if it is only for a quick sanity check) makes me a lot more effective than I could ever be on my own.

This can be applied to products: instead of celebrating the successes, we should be celebrating issues that have been brought to our attention. A bug is a chance to analyze what lead to it to fix it.

This is the real opportunity – we have to learn not to do stupid things. Instead of huffing and puffing and – worst case – putting in a “stop gap solution” or a “quick fix” we should spend time to see where the bug came from, what lead to it, what impact it has and document the way to fixing it. There are millions of “best practice” tutorials and examples but not many “here’s how we fixed a nasty bug that caused this and that” articles.

The reason is guilt and fear of appearing weak and fallible. Well, I personally think that owing up to the fact that you do make mistakes actually makes you strong. The same way that appreciating good work of competitors makes you more believable than just banging on about what you do and act as if others don’t do great things, too.

I am quite sure that if we managed to turn the development culture around to see mistakes as a positive, we’ll make a massive step forward. A person pointing out an obvious flaw is not a whistle-blower and “not on message” but someone you have to thank for providing a chance to really improve our work and learn not to do things wrong the next time around.

Mistakes are OK - they do happen.