Detects Support for HTML5 and CSS3

There are lots of new and cool features in HTML5 such as canvas, geolocation, etc. Unfortunetely not all browsers support the features. So, how do we detects whether or not a browser supports a particular feature? Here are four basic techniques I extracted from diveintohtml5.org:

1. Check if a certain property exists on a global object (such as window or navigator).


function testGeolocation() {

    return !!navigator.geolocation;

}

2. Create an element, then check if a certain property exists on that element.


function testCanvas() {

    return !!document.createElement('canvas').getContext;

}

3. Create an element, check  if a certain method exists on that element, then call the method and check the value it returns.

function testVideo() { 
 if (!document.createElement('video').canPlayType) {
   return false;
 }
  var v = document.createElement("video");
  return v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
}

4. Create an element, set a property to a certain value, then check if the property has retained its value.

   var i = document.createElement("input");
   i.setAttribute("type", "datetime");
   return i.type !== "datetime";

Of course there is the 5th way, which is to use a HTML5 detection library. it is called Modernizr. It is an open source and MIT-licensed.

Modernizr is a small and simple JavaScript library that helps you take advantage of emerging web technologies (CSS3, HTML 5) while still maintaining a fine level of control over older browsers that may not yet support these new technologies.

To use it, simply include the Modernizr script into your page. You can download the script from http://www.modernizr.com/

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="modernizr.min.js"></script>
</head>
<body>
  ...
</body>
</html>

Looking back at the examples from the 4 basic techniques above, here is how it looks like using Modernizr

if (Modernizr.geolocation){
  navigator.geolocation.getCurrentPosition(function(position) {
    setUserLatandLong(position.coords.latitude,position.coords.longitude);
  }); 
} else {
 // Sorry, your browser is very old.
}

There are more example on the documentation site.

Advertisements

Dynamically Loading Ext.NestedList

After developing the USGS earthquake for mobile, I want to extend it to display more information. One of the challenge I faced was how to dynamically load data into the nested list. I did a bit reading in the forum and someone suggested to implement the following.

var nestedList = new Ext.NestedList({
items: []
});

var list = [
{
text: 'Test'
},{
text: 'Test 2'
}];

nestedList.setList(list, true);

I did try out to use setList(), however I noticed a problem when I clicked the back button. The nested list still remember the original “items” which was set to blank or []. It is correct that setList() will rebuild the entire list, however in this instance it is not the right method to use. One thing to highlight is the setList() is called when item is tapped and when we click back button, however everytime it is called the new list is appended to the end of the collection as shown in the code below. As we tap the item and click back, the index counter become wrong.

if (!this.lists.contains(list)) {
this.lists.push(this.add(list));
}

I will cover 2 scenarios related to this topic:

  1. Dynamically load nested list after the UI is launched.
  2. Dynamically load list when item is tapped.

The alternative solution to dynamically set the nested list is to extend the original object and implement a new method “reset” as shown below. Perform exactly as It is in setList() but instead of joining the list, reset it. This will address scenario 1.

Ext.ws.NestedList = Ext.extend(Ext.NestedList, {
resetList : function(list, init) {
//...
if (!this.lists.contains(list)) {
this.lists[0] = this.add(list);
}
//...
},

onItemTap : function(item) {
//...
}
});

In order to address scenario 2, override the onItemTap() as shown below. In this example, I added a new attribute “fid” into the item which helps to decide what data to load. In your implementation you might call JSONP request to load the data.

Ext.ws.NestedList = Ext.extend(Ext.NestedList, {
resetList : function(list, init) {
//...
},

onItemTap : function(item) {
item.el.radioClass('x-item-selected');
if (item.items) {
this.backButton.show();
if (item.fid == 'A') item.items = [ {text: 'List Z.1'} ];
this.setList(item);
this.listIndex++;
}
this.fireEvent('listchange', this, item);
}
});

The following is the full source code used to demonstrate the 2 scenarios. If you have alternative solution to the above, do drop me comment and share it. 🙂

<script type="text/javascript">

Ext.ns('Ext.ws');

Ext.ws.NestedList = Ext.extend(Ext.NestedList, {
resetList : function(list, init) {
var items = init ? list : list.items;
if (!list.isList) {
list = new Ext.Container({
isList: true,
baseCls: 'x-list',
cls: 'x-list-flat',
defaults: {
xtype: 'button',
baseCls: 'x-list-item',
pressedCls: 'x-item-pressed',
ui: null,
pressedDelay: true
},
listeners: {
afterrender: function() {
this.getContentTarget().addClass('x-list-parent');
}
},
scroll: 'vertical',
items: items,
text: list.text
});
}

this.lists = this.lists || [];
if (!this.lists.contains(list)) {
this.lists[0] = this.add(list);
}

var isBack = (this.lists.indexOf(list) < this.lists.indexOf(this.activeItem));
if (this.rendered) {
this.setCard(list, init ? false : {
type: this.animation,
reverse: isBack
});
}
this.activeItem = list;
},

onItemTap : function(item) {
item.el.radioClass('x-item-selected');
if (item.items) {
this.backButton.show();
if (item.fid == 'A') item.items = [ {text: 'List Z.1'} ];
this.setList(item);
this.listIndex++;
}
this.fireEvent('listchange', this, item);
}
});

Ext.setup({
onReady: function() {

var list = [ {text: 'List A', fid: 'A', items: [] },
{text: 'List B', fid: 'B', items: [ {text: 'List B.1'} ] },
{text: 'List C', fid: 'C', items: [ {text: 'List C.1'} ] }
];

var nestedList = new Ext.ws.NestedList({
items: [],
fullscreen: true
});

nestedList.resetList(list, true);

} // end onReady
}); // end ext.setup

</script>

Update: the example above was based on Sencha Touch 0.91

10 Informative Infographics

Complex and huge amount of data are hard to comprehend and navigate, especially when they are presented in words or text. Presenting it in words or text is just plain boring and turn off the audience. Information graphics or Infographics is a visual representation of those data. It is an excellent visual representation of data, it is easier to comprehend and the graphics helps us relate with the data. It does add some “emotional aspect” also. This is an excellent way to communicate clearly and it is self explanatory.

Information graphics or infographics are graphic visual representations of information, data or knowledge. These graphics present complex information quickly and clearly – Wikipedia

In order to appreciate them, here are some of the creative, informative and well-designed inforgraphics I have found.

1. World Cup 2010 Calendar – http://www.marca.com/deporte/futbol/mundial/sudafrica-2010/calendario-english.html

2. Revisit is a real-time visualization of the latest twitter messages (tweets) around a specific topic. – http://moritz.stefaner.eu/projects/revisit/

3. It’s 140 most influential Twitter users – http://informationarchitects.jp/c140/

4. The Evolution of Privacy on Facebook – http://mattmckeon.com/facebook-privacy/

5. A Visual History of the American Presidency – http://timeplots.com/potus/

6. An infographic résumé – http://joaocorreia.pt/infographics/infographic-resume/. A very interesting way of presenting resume, this is good for job in design industry.

7. Japan, the Strange Country – by  Kenichi Tanaka http://www.kenichi-design.com/. This one is a video and in Japanese. Unfortunetely the English version was deleted. But it is still interesting too watch.

Japan – The Strange Country (Japanese ver.) from Kenichi on Vimeo.

8. ASK KEN™ is sort of a Node-Link diagram that allows to visually navigate through interconnected topics. – http://askken.heroku.com/ . Btw, this site only support FF, Safari and Chrome.

9. Every Country’s #1 at Something – http://www.informationisbeautiful.net/visualizations/because-every-country-is-the-best-at-something/

10. An information graphic for AOL Autos comparing Teen Drivers to Senior Drivers, visualizing various statistics on who are the safer drivers. http://gavinpotenza.com/old-young-drivers/

Web Scraping using C#

Yesterday, I was looking at the SBS Transit website and looking for “easy” way to get the bus arrival time. Yes, the site has its own mobile version but I thought I can do more if I have the raw data. Unfortunetely the site does not provide any web services. After searching for awhile, found someone (Deepak Sarda) that created a simple JSON API to get the arrival time via http://sbsnextbus.appspot.com/. What he did was web scraping the SBS Iris site run it on GAE.

Web scraping, sound old, messy and tidious. In most cases, it is hard to keep up with the changes and your code will break if the site change layout or content often. Unfortunately, not everyone make the data readily available via web services or json API. I created a simple apps to test the API – http://bit.ly/caXufL – Curious, I decided to work on my own web scraping using c#.

Before you start web scraping, it is good to understand the site behaviour. You can use tools like HttpFox (FF) or HttpWatch (IE) to simulate and find out whether or not the sites uses cookies. For form posting, You need to know all the input parameters. Another special case is ASP.NET site, You need to maintan the ViewState. I have 2 examples below, 1) web scraping web content and 2) posting data.

1. Extracting web content – The simplest way to harvest those HTML is to use HttWebRequest.

    CookieContainer cookies = new CookieContainer();
    string baseUrl = @"web site url";
    System.Net.HttpWebRequest httpwr = (HttpWebRequest)WebRequest.Create(baseUrl);
    httpwr.Method = "GET";
    httpwr.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8";
    httpwr.ContentType = "text/html";
    httpwr.CookieContainer = cookies;

    HttpWebResponse res = (HttpWebResponse)httpwr.GetResponse();
    res.Cookies = httpwr.CookieContainer.GetCookies(httpwr.RequestUri);
    string resStr = new System.IO.StreamReader(res.GetResponseStream()).ReadToEnd();
  Console.WriteLine(resStr);

2. Posting data

    string postData = String.Format("__EVENTTARGET=&__EVENTARGUMENT=&param0={0}&param1={1}&param2={2}", "input1", "input2","input3");
    byte[] data = encoding.GetBytes(postData);

    httpwr = (HttpWebRequest)WebRequest.Create(url);
    httpwr.Method = "POST";
    httpwr.AllowAutoRedirect = true;  // To handle redirection. Set to false if it is not required.
    httpwr.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8";
    httpwr.ContentType = "application/x-www-form-urlencoded";
    httpwr.CookieContainer = cookies;
    httpwr.ContentLength = data.Length;
    //Post content
    using (var stream = httpwr.GetRequestStream())
    {
        stream.Write(data, 0, data.Length);
    }
    res = (HttpWebResponse)httpwr.GetResponse();
    using (var sr = new StreamReader(res.GetResponseStream()))
    {
        resStr = sr.ReadToEnd();
    }
  Console.WriteLine(resStr);

You can then extend further by exposing the harvested data via web services using the WebMethod in ASP.NET.

  [WebMethod()]
  public static Data[] GetData(string input)
  {
    // web scraping the site
    // parse the content
    //store in db or memcache
    return result.ToArray();
  }

In some cases, web scaping is not as straight forward as getting and posting data. Some site maintain specific flow base on specific parameter. In this case, you may need to combine a few call (combination of 1 and 2) to get the result you want. Once the HTML content is harvested, you will then need to parse it. I found a good parser for .NET, it is call HTMLAgilityPack. It supports XPATH or XSLT and Linq, and easy to use.

 HtmlDocument doc = new HtmlDocument();
 doc.Load(responseString);
 foreach(HtmlNode form in doc.DocumentElement.SelectNodes("//form[@action"])
 {
    HtmlAttribute att = form["action"];
    Console.WriteLine(att.Value);
 }

USGS Earthquakes Map for Mobile

Another good example of how fast and easy it is to develop a mobile application using Sencha Touch. I spent 2 nights and finally get it up and running. It is a very simple application that read feed from USGS feed and plot the earthquake location into Google Maps. For this application, I used the “kitchen sink” example as the base. It is a really good example to start with. The UI implementation allows the apps to run both on desktop (on HTML5 ready browser) and mobile without compromising the look and feel. See the screenshot below. The mobile version has the iPhone UI touch 🙂

I did encounter one problem during the implementation. The USGS data is in XML format, so I tried using the XMLReader provided in Ext. Somehow, I keep getting error from the XMLReader saying I am accessing property (totalProperty) on undefined object. Not sure whether or not it is a bug. Thanks to YQL, I converted the XML feed to JSON. The SQL like syntax looks friendly, see below

var makeAjaxRequest = function() {
var params = {
rssUrl: 'http://earthquake.usgs.gov/earthquakes/catalogs/shakerss.xml'
};
var usgsRssQuery = new Ext.Template('select * from rss where url="{rssUrl}"');
var query = usgsRssQuery.applyTemplate(params);

Ext.util.JSONP.request({
url: 'http://query.yahooapis.com/v1/public/yql',
callbackKey: 'callback',
params: {q: 'select * from rss where url="<a href="http://earthquake.usgs.gov/earthquakes/catalogs/shakerss.xml&quot;'">http://earthquake.usgs.gov/earthquakes/catalogs/shakerss.xml"'</a>, format: 'json'},
callback: function(data) {
// Process feed data.
}
});
}

The apps UI comprise of 2 part, first is the list that display the feed data and second is the panel that display the map. The main UI is based on the nested lists components and using onListChange event to handle touch or click on the data item. Here is the setup

this.navigationPanel = new Ext.NestedList({
items: this.navigationItems || [],
dock: 'left',
width: 250,
height: 456,
hidden: !Ext.platform.isPhone && Ext.orientation == 'portrait',
toolbar: Ext.platform.isPhone ? this.navigationBar : null,
listeners: {
listchange: this.onListChange
,scope: this
}
});
var pos=new google.maps.LatLng(feedArray[i].lat, feedArray[i].long);
var desc = feedArray[i].description;
var map = new Ext.Map({
title: i,
markerDesc: desc,
markerPos: pos,
mapOptions: {
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
,center: pos
},
listeners: {
delay: 500,
afterrender: function(){
addMarker(this.map, this.markerPos, this.markerDesc);
}
}
});
var panel = new Ext.Panel({
items: [map]
});

Similar to the Nearby Tweet, I couldn’t test it on Android. If you do try it on android, drop a comment and share whether or not it works. To try the apps visit http://bit.ly/a7KJj4

Note: For desktop, try on HTML5 ready browser. I used Chrome to test it out.

Update: See the updated version implemented using MVC here.

Hello HTML 5

Since the “fight” between Apple and Adobe on Flash, HTML5 technology has recently into the spotlight. It was then follow by Apple launched its HTML5 showcase site, then Google announced its HTML5 Rocks. Although HTML5 is still in working draft (http://www.w3.org/TR/html5/) and expects the specification to reach the Candidate Recommendation stage during 2012. Most of the browsers are prepared for it and HTML5 ready, e.g. 
  • Firefox
  • Google Chrome
  • Opera
  • Apple Safari

Microsoft IE is currently a bit late but they are catching up with IE9. You can follow the development here http://blogs.msdn.com/b/ie/. For the IE fan, to browse HTML5 sites you will need 

What is so cool about HTML5? Here are some of the new features: 

  • The canvas element for immediate mode 2D drawing.
  • Timed media playback
  • Offline storage database (offline web applications).
  • Document editing
  • Geolocation
  • and more…

For all of us to appreciate all these features we must see it in action, right. Seeing is believing, here is the list of 5 sites and gallery you must explore (in no particular order :)): 

 

  

Apple HTML5 Showcase

 

  

HTML5Rocks

 

  

HTML5 Gallery

 

  • HTML5 Demos – HTML5 Demos offer a good overview of how HTML5 is changing the landscape and its capabilities.

  

HTML5 Demo

 

Checkout those links and post your opinion.

Nearby Tweet (Makeover)

After spending my weekend reading Sencha Touch, I decided to do a bit of makeover for my “Nearby Tweet”. Sencha Touch kind of save me the time from learning the native mobile SDK like iOS or Andoid. It is using standards such as JavaScript, HTML 5 and CSS3, all I need to learn is the framework.

The previous version has a very important limitation which no geolocation, it is hardcoded to Singapore. Using the Geolocation feature in HTML 5, the application is now able to locate the user and mark the nearby tweets into the map. Another improvement is the use of “Tab Panel” to show both map and the tweets. Here is the screenshots:

So , how is it done?

Step 1: Include Sencha Touch CSS & JavaScript, Google Map API and own CSS & JavaScript.

<!DOCTYPE html>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <title>Nearby Tweets Mobile</title>
 <link rel="stylesheet" href="css/ext-touch.css" type="text/css">
 <link rel="stylesheet" href="css/NearByTweetsMobile.css" type="text/css">
 <script type="text/javascript" src="<a href="http://maps.google.com/maps/api/js?sensor=true">http://maps.google.com/maps/api/js?sensor=true</a>"> </script>
 <script type="text/javascript" src="js/ext-touch.js"> </script>
 <script type="text/javascript" src="js/NearByTweetsMobile.js"> </script>
</head>
<body> </body>
</html>

Step 2: Implement the JavaScript for loading the UI, getting the Tweets and marking it on the map. There no change to the code that add marker and get location from previous implmentation.

Ext.setup({
    icon: 'icon.png',
    glossOnIcon: false,
    onReady: function() {
      // nbTweets - tweet list. Using Template to generate the html.
        var nbTweets= new Ext.Component({
            title: 'Nearby Tweets',
            scroll: 'vertical',
            tpl: [
                '<tpl for=".">',
                    '<div>',
                            '<div><img src="{profile_image_url}" /></div>',
                            '<div>',
                                '<h2>{from_user}</h2>',
                                '<p>{text}</p>',
                                '<p>{location}</p>',
                            '</div>',
                    '</div>',
                '</tpl>'
            ]
        });

        var map = new Ext.Map({
            title: 'Map',
            getLocation: true,
            mapOptions: {
                zoom: 12,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            }
        });
       
    var geocoder = new google.maps.Geocoder();

    // Create tab panel for the map and tweet list timeline
        var panel = new Ext.TabPanel({
            fullscreen: true,
            animation: 'slide',
        ui: 'light',
            items: [map, nbTweets]
        });

    // handler for refresh button    
        var refresh = function() {
            var coords = map.geo.coords; // get user geolocation

            Ext.util.JSONP.request({
                url: 'http://search.twitter.com/search.json',
                callbackKey: 'callback',
                params: {
                    geocode: coords.latitude + ',' + coords.longitude + ',' + '10km',
                    rpp: 30
                },
                callback: function(data) {
                    data = data.results;

                    // Update the tweets in nbTweets
                    nbTweets.update(data);
                    // Add points to the map
                    for (var i = 0, ln = data.length; i < ln; i++) {
                        var tweet = data[i];
                        getTweetLocation(map, geocoder, tweet);
                    }
                }
            });
        };

        map.geo.on('update', refresh);

        var tabBar = panel.getTabBar();
        tabBar.addDocked({
            xtype: 'button',
            ui: 'mask',
            iconCls: 'refresh',
            dock: 'right',
            stretch: false,
            align: 'center',
            handler: refresh
        });

    }
});

// These are all Google Maps APIs
function getTweetLocation(map, geocoder, tweet) {
 // insert the code to get tweet location either from the geotag or tweet.location
    if (tweet.geo && tweet.geo.coordinates) {
        var position = new google.maps.LatLng(tweet.geo.coordinates[0], tweet.geo.coordinates[1]);
    addMarker(map, position, tweet);
    } else {
    var geocode = tweet.location.split(": ",2);
    var addr = geocode[1];
    if (addr == undefined) addr = tweet.location;
      if (geocoder) {
        geocoder.geocode( { 'address': addr }, function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {
           addMarker(map, results[0].geometry.location, tweet);
          } else {
            //alert("Geocode was not successful for the following reason: " + status);
          }
        });
      }
    } // end if tweet.geo
} // end addMarker
       
function addMarker(map, position, tweet) {
 // insert the code to add marker to map.
} // end addMarker

So far, I have tested in iPhone Safari (on iOS4) and it works fine. Not sure how it performs in Android yet. One thing to note is the mobile browser must support HTML 5. 🙂 If you try the links below on Android, drop me a comment whether or not it works and what Android version you are using.

Nearby Tweets Mobile: http://www.wswijaya.com/prototype/NearByTweetsMobile.php

Sencha Touch Version: 0.91 (public beta)