Posts Tagged ‘JSON’

Scrabblr - A scrabble calculation/validation API

Friday, August 22nd, 2008

As it is Friday and I am jetlagged I declare “pointless API Friday” and give you Scrabblr.

Scrabblr allows you to send a word to it and it returns the scrabble value or an error in case the word has too many instances of a character than there are tiles available. Output formats are either a simple string (no format defined), XML or JSON. For JSON you also can have a callback parameter to use the API in script nodes.

Examples:

Example of an unobtrusive, lazy-loading badge using the Twitter API

Friday, April 11th, 2008

Following questions I had about my talk at Highland Fling about badges for distribution and a twitter nagging by Tantek about the official twitter badge I thought I’d have a go at creating a twitter badge following some of the best practices I mentioned in my talk. Here’s the result.

The markup

Instead of HTML containers that will be seeded with real data when JavaScript is available and pointless when it isn’t, I wanted to build on top of HTML that makes sense without scripting and get all the info my script needs from there.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 1//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>Example of a unobtrusive, lazy loading twitterbadge</title>
  </head>
  <body>
  <div id="twitterbadge">
    <h2><a href="http://twitter.com/codepo8">My twitter updates</a></h2>
  </div>
  <script src="twitterbadgev2.js" type="text/javascript"></script>
  </body>
</html>

In order to customise the badge, I allow for CSS classes with information to be added to the main container:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 1//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>Example of a unobtrusive, lazy loading twitterbadge</title>
  </head>
  <body>
  <div id="twitterbadge" class="amount-10 skin-blue userinfo">
    <h2><a href="http://twitter.com/codepo8">My twitter updates</a></h2>
  </div>
  <script src="twitterbadgev2.js" type="text/javascript"></script>
  </body>
</html>

They mean the following:

  • amount-n defines the amount of tweets to be displayed with n being an integer
  • skin-name defines the skin you want to use (for now this is grey and blue)
  • userinfo defines if the user’s avatar, name and location should be displayed.

The script

Here’s the full script and we’ll go through the bits one by one.

twitterbadge = function(){
  var config = {
    countDefault:5,
    badgeID:'twitterbadge',
    userID:'twitterbadgeuser',
    tweetsID:'twitterbadgetweets',
    userinfo:'userinfo',
    stylesmatch:/skin-(\w+)/,
    amountmatch:/amount-(\d+)/,
    styles:{
      'grey':'twitterbadge.css',
      'blue':'twitterbadgeblue.css'
    }
  };
  var badge = document.getElementById(config.badgeID);
  if(badge){
    var link = badge.getElementsByTagName('a')[0];
    if(link){
      var classdata = badge.className;
      var head = document.getElementsByTagName(’head’)[0];
      var amount = config.amountmatch.exec(classdata);
      var amount = amount ? amount[1] : config.countDefault;
      var skin = config.stylesmatch.exec(classdata);
      if(skin && skin[1]){
        var style = document.createElement(’link’);
        style.setAttribute(’rel’,’stylesheet’);
        style.setAttribute(’type’,'text/css’);
        style.setAttribute(’href’,config.styles[skin[1]]);
        head.insertBefore(style,head.firstChild);
      }
      var name = link.href.split(’/');
      var resturl = ‘http://twitter.com/statuses/user_timeline/’ +
                    name[name.length-1] + ‘.json?callback=’ +
                    ‘twitterbadge.show&count=’ + amount;
      var script = document.createElement(’script’);
      script.src = resturl;
      script.type = ‘text/javascript’;
      function show(result){
        if(classdata.indexOf(config.userinfo) != -1){
          var user = document.createElement(’p');
          user.id = config.userID;
          var img = document.createElement(’img’);
          img.src = result[0].user.profile_image_url;
          img.alt = result[0].user.name;
          user.appendChild(img);
          var ul = document.createElement(’ul’);
          var data = ['screen_name','name','location'];
          for(var i=0;data[i];i++){
            if(result[0].user[data[i]]){
              var li = document.createElement(’li’);
              li.appendChild(document.createTextNode(result[0].user[data[i]]));
              ul.appendChild(li);
            }
          }
          user.appendChild(ul);
          badge.appendChild(user);
        }
        var tweets = document.createElement(’ul’);
        tweets.id = config.tweetsID;
        for(var i=0,j=result.length;i<j;i++){
          var username = result[i].user.screen_name;
          var li = document.createElement(’li’);
          var span = document.createElement(’span’);
          span.innerHTML = result[i].text+’ ‘;
          li.appendChild(span);
          var link = document.createElement(’a');
          link.setAttribute(’href’,'http://twitter.com/’ + username + 
                                   ‘/statuses/’+result[i].id);
          link.appendChild(document.createTextNode(relative_time(result[i].created_at)));
          li.appendChild(link);
          tweets.appendChild(li);
        }
        badge.appendChild(tweets);
      }
      function relative_time(time_value) {
        var values = time_value.split(" ");
        time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
        var parsed_date = Date.parse(time_value);
        var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
        var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
        delta = delta + (relative_to.getTimezoneOffset() * 60);
        if (delta < 60) {
          return ‘less than a minute ago’;
        } else if(delta < 120) {
          return ‘about a minute ago’;
        } else if(delta < (60*60)) {
          return (parseInt(delta / 60)).toString() + ‘ minutes ago’;
        } else if(delta < (120*60)) {
          return ‘about an hour ago’;
        } else if(delta < (24*60*60)) {
          return ‘about ‘ + (parseInt(delta / 3600)).toString() + ‘ hours ago’;
        } else if(delta < (48*60*60)) {
          return ‘1 day ago’;
        } else {
          return (parseInt(delta / 86400)).toString() + ‘ days ago’;
        }
      }
    }
  }
  return {
    show:show,
    init:function(){
      head.appendChild(script);
    }
  };
}();
twitterbadge.init();

I am using the revealing module pattern to keep code short and avoid global callback methods. However, there is a slight Opera oddity with generated script nodes in module patterns so we have to deviate from the norm there with an extra init() method call after the main module.

The first thing I thought of providing is a configuration object for the script. This makes it easy to change settings of it without having to hunt through the whole script and is just a nice service for the implementer:


twitterbadge = function(){  
  var config = {
    countDefault:5,
    badgeID:'twitterbadge',
    userID:'twitterbadgeuser',
    tweetsID:'twitterbadgetweets',
    stylesmatch:/skin-(\w+)/,
    amountmatch:/amount-(\d+)/,
    styles:{
      'grey':'twitterbadge.css',
      'blue':'twitterbadgeblue.css'
    }
  };

Here we have all the IDs in use, the style names and the corresponding file names and the regular expressions to get the data from the CSS class name. All of the IDs and classes are hooks to define your own skins. There is also a countDefault variable to define how many items should be shown when the amount class is not set.


  var badge = document.getElementById(config.badgeID);
  if(badge){
    var link = badge.getElementsByTagName('a')[0];
    if(link){

I test for the badge and that it contains a link as this is where we will get all our configuration data from.


      var classdata = badge.className;
      var head = document.getElementsByTagName('head')[0];
      var amount = config.amountmatch.exec(classdata);
      var amount = amount ? amount[1] : config.countDefault;
      var skin = config.stylesmatch.exec(classdata);
      if(skin && skin[1]){
        var style = document.createElement(’link’);
        style.setAttribute(’rel’,’stylesheet’);
        style.setAttribute(’type’,'text/css’);
        style.setAttribute(’href’,config.styles[skin[1]]);
        head.insertBefore(style,head.firstChild);
      }

Then I am ready to read the information from the class. I set a shortcut to the document head and read the amount of tweets to be displayed. If there is no amount-n class set I fall back to the default.

Next is the skin, I check if the class was set and if that is the case I create a new link element pointing to the right skin. I get the href from the configuration styles object.

Notice that I use insertBefore() to add the style to the head of the document and not appendChild(). This ensures to a degree that the skin css file will not override settings that might be in other stylesheets. The last included style sheet rules them all.


        var name = link.href.split('/');
        var resturl = 'http://twitter.com/statuses/user_timeline/' +
                      name[name.length-1] + ‘.json?callback=’ +
                      ‘twitterbadge.show&count=’ + amount;
        var script = document.createElement(’script’);
        script.src = resturl;
        script.type = ‘text/javascript’;

Now it is time to find the user name (by splitting the href attribute of the link) and assemble the REST url to get the twitter data. Normally I would have added the new script node to the head directly aftwerwards, but Opera doesn’t like this.


        function show(result){
          if(classdata.indexOf(config.userinfo) != -1){
            var user = document.createElement('p');
            user.id = config.userID;
            var img = document.createElement('img');
            img.src = result[0].user.profile_image_url;
            img.alt = result[0].user.name;
            user.appendChild(img);
            var ul = document.createElement(’ul’);
            var data = ['screen_name','name','location'];
            for(var i=0;data[i];i++){
              if(result[0].user[data[i]]){
                var li = document.createElement(’li’);
                li.appendChild(document.createTextNode(result[0].user[data[i]]));
                ul.appendChild(li);
              }
            }
            user.appendChild(ul);
            badge.appendChild(user);
          }

Now it is time to start the core functionality: the show method that will be invoked by the twitter REST API callback. I check if the userinfo has been set and create the markup accordingly. Nothing amazing here.


          var tweets = document.createElement('ul');
          tweets.id = config.tweetsID;
          for(var i=0,j=result.length;i<j;i++){
            var username = result[i].user.screen_name;
            var li = document.createElement(’li’);
            var span = document.createElement(’span’);
            span.innerHTML = result[i].text+’ ‘;
            li.appendChild(span);
            var link = document.createElement(’a');
            link.setAttribute(’href’,'http://twitter.com/’ + username + 
                                     ‘/statuses/’+result[i].id);
            link.appendChild(document.createTextNode(relative_time(result[i].created_at)));
            li.appendChild(link);
            tweets.appendChild(li);
          }
          badge.appendChild(tweets);
        }

Next I get the tweets information, assemble a list and add it to the badge.


      function relative_time(time_value) { 
        [...]
      }
    }
  }
  return {
    show:show,
    init:function(){
      head.appendChild(script);
    }
  };
}();
twitterbadge.init();

The relative_time method is actually taken from the original twitter badge and calculates how old the tweets are. I end the module with a return statement that defines the public methods (in this case only show) and add the script node to call the REST API in an init method. This is only necessary to fix the Opera issue.

Download and Example

You can download the twitter badge and see it in action.

Opera, REST APIs, Module Patterns and generated script nodes

Friday, April 11th, 2008

I just came across an annoying thing in Opera. I love using the Module Pattern for designing my JavaScripts and I love generating script nodes on the fly for REST APIs that give me back JSON. The following example using the twitter API works swimmingly on Firefox and Safari (have to check IE later, but I’d be surprised if it didn’t):


<script type="text/javascript">
    twitterbadge = function(){
      function show(result){
        alert(result);
      };
      var restURL = 'http://twitter.com/statuses/user_timeline/' +   
                    'codepo8.json?callback=twitterbadge.show' + 
                    '&amp;count=10';
      var s = document.createElement('script');
      s.setAttribute('src',restURL);
      document.getElementsByTagName('head')[0].appendChild(s);
      return {
        show:show
      };
    }();
</script>

However, if you test this in Opera you get an error:


JavaScript - file://localhost/Applications/MAMP/htdocs/operacallbackfail.html
Inline script thread
Error:
name: ReferenceError
message: Statement on line 1: Reference to undefined variable: twitterbadge
Backtrace:
  Line 1 of linked script http://twitter.com/statuses/user_timeline/codepo8.json?callback=twitterbadge.show&amp;count=10
    twitterbadge.show([{user : {screen_name : "codepo8",...08"}]);

It seems like the newly generated script node calls the method of the module before the module has been created. In other words, newly generated script nodes halt the execution of the code that generated them. The following example works across browsers, including Opera:


<script type="text/javascript">
    twitterbadgeOperaSafe = function(){
      function show(result){
        alert(result);
      };
      var restURL = 'http://twitter.com/statuses/user_timeline/' +   
                    'codepo8.json?callback=twitterbadgeOperaSafe.show' + 
                    '&amp;count=10';
      var s = document.createElement('script');
      s.setAttribute('src',restURL);
      return {
        show:show,
        init:function(){
          document.getElementsByTagName('head')[0].appendChild(s);
        }
      };
    }();
    twitterbadgeOperaSafe.init();
</script>

Not too daunting a fix, but it feels wrong to have to have an extra method and another line calling it.

Try it out for yourself: Opera bug with dynamic script nodes and module pattern

flickr trackr - my first attempt and dabbling with the iPhone/iPod touch

Thursday, February 14th, 2008

I was pretty amazed when I got a thick envelope the other day from Apple. Steven Woolcock, the Safari Evangelist in Europe sent me an iPod touch to play with as I complained about not being able to test something he told me about. Now, as every other 12 year old boy on a sugar rush would have done I delved right into dabbling with the new toy and here’s my first result:

demo of a search on an iphone with flickrtrackr

The code is pretty dirty at the moment, but does the trick. Thanks must go to Steven for lending me the iPod Touch, David Dorward, Neil Crosby and Michele Gera for on-the-spot testing and Norm for giving me his phone for the screenshots.

What do you think? The whole thing is of course Creative Commons and you can download the source package.

Retrieving del.icio.us tags for the current URL with JavaScript

Monday, February 11th, 2008

If you scroll down the older entries of this blog you’ll see that there is a new feature, namely a box that shows reader tags and a link to del.icio.us:

Screenshot of a list of tags with a link to del.icio.us

This is not a Wordpress plugin (although it would be easy to make and i’d be amazed if it hadn’t been done) but pure JavaScript. You can also download the script that does this and use the following to embed it in any page you’d like to know the delicious data for:


<div id="deliciousinfo"></div>
<script src="deliciousinfo.js"></script>

There is not much magic going on here, I basically souped up the example on the del.icio.us site,minified and embedded Paul Johnson’s implementation of MD5 in JavaScript and created the necessary HTML.

The HTML structure inside the DIV will be a definition list with tags as dd’s and the text as the dt and a paragraph with a link. You can style it by using the #deliciousinfo ID.

I like the outcome and I am always amazed what good tags readers of my stuff come up with. If you want to know, get the src commented version and check the information in there.

Shall I create a Wordpress plugin for this?

Edit: if you wondered what the difference to the tagometer is, there isn’t much, I just forgot about it….

@mediaAjax
Scripting Enabled - hacking the web to be more accessible - London, England 19th and 20th of September 2008
Wait till I come! is the blog of , a developer evangelist living and working in London, England. Download vcard.

Feed me, Seymour: Entries (RSS) and Comments (RSS).