O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

19.6. Convert hCalendar Microformat Annotations into a Canvas Timeline

Problem

You want to plot the events annotated with the hCalendar Microformat on a Canvas-based graph. The hCalendar event syntax can differ, as the following two legitimate variations demonstrate:

<p><span class="vevent">
 <span class="summary">Monkey Play Time</span>
 on <span class="dtstart">2010-02-05</span>
 at <span class="location">St. Louis Zoo</span>.
</span></p>

<div class="vevent" id="hcalendar-Event">
  <abbr class="dtstart" title="2010-02-25">February 25th</abbr>,
   <abbr class="dtend" title="2010-02-26"> 2010</abbr>
  <span class="summary">Event</span></div>

With one format, the dtstart class is on a span element; with the other format, the dtstart class is on an abbr element.

Solution

Find all elements with a class of vevent:

var events = document.querySelectorAll("[class='vevent']");
var v = events;

Within each, find the element with a class of dtstart. There should only be one, and there should always be one. By Microformat convention, if the dtstart element is an abbr, the start date is found in a title attribute on the element. If the dtstart element is a span, the start date is found in the element’s textContent, and is then split out of the date string to find the actual day:

var days = new Array();
for (var i = 0; i < events.length; i++) {
   var dstart = events[i].querySelectorAll("[class='dtstart']");
   var dt;
   if (dstart[0].tagName == "SPAN") {
      dt = dstart[0].textContent;     }
   else if (dstart[0].tagName == "ABBR") {
      dt = dstart[0].title;
   }
   var day = parseInt(dt.split("-")[2]);
   days.push(day);
}

The value is then used to graph the line in a canvas element.

Discussion

Microformats are both simple and complicated. They’re simple in that it’s easy to find the data, but they’re complicated because the rules surrounding the data are very loose. As the solution demonstrates, an hCalendar event start date can be recorded in span elements or abbr elements; the dates are ISO 8601, but could be just dates, or datetime.

The advantage to working with Microformats using client-side JavaScript is that we usually have some control over the format of the Microformats. For instance, if we have a social-networking site where people are entering events, we have no control over what events are created, but we do have control over the format and can ensure that it’s consistent.

Once we know the form of the Microdata used in the page, it isn’t complicated to get the data. For the most part, we’re retrieving elements based on class name, and making queries on these elements’ subtrees for elements with different class names. Though there are few rules to Microformats, there are rules. For instance, the hCalendar data used in the solution has at least three rules: the outer element has a vevent class, there must be a summary and a dtstart element, and if the dtstart element is a span, the data is the textContent; if it is an abbr, the data is the title.

Since IE8 does not support textContent, the code performs a test for textContent. If it isn’t found, then IE8 gets the text from the (IE-originated) innerText or innerHTML property:

for (var i = 0; i < events.length; i++) {
   var dstart = events[i].querySelectorAll("[class='dtstart']");
   var dt;
   if (dstart[0].tagName == "SPAN") {
      if (dstart[0].textContent)
         dt = dstart[0].textContent;
      else
        dt = dstart[0].innerText;
   } else if (dstart[0].tagName == "ABBR") {
      dt = dstart[0].title;
   }
   var day = parseInt(dt.split("-")[2]);
   days.push(day);
}

Make sure to include the ExplorerCanvas excanvas.js library before your script, in order to ensure the canvas element and commands work with IE:

<!--[if IE]><script src="excanvas.js"></script><![endif]-->

Example 19-3 pulls all of the components together into a full-page application, including the Canvas drawing. The tick marks in the Canvas element are expanded to 10 times their size to make them easier to see on the line. The page is an event calendar for a month at the zoo.

Example 19-3. Extracting Microformat events from page and charting them on a Canvas line graph

<!DOCTYPE html>
<head>
<title>Microformats</title>
<!--[if IE]><script src="excanvas.js"></script><![endif]-->
<script>

window.onload=function() {

  var events = document.querySelectorAll("[class='vevent']");
  var v = events;
  var days = new Array();
  for (var i = 0; i < events.length; i++) {
     var dstart = events[i].querySelectorAll("[class='dtstart']");
     var dt;
     if (dstart[0].tagName == "SPAN") {
        if (dstart[0].textContent)
           dt = dstart[0].textContent;
        else
          dt = dstart[0].innerText;
     } else if (dstart[0].tagName == "ABBR") {
        dt = dstart[0].title;
     }
     var day = parseInt(dt.split("-")[2]);
     days.push(day);
  }

  var ctx = document.getElementById("calendar").getContext('2d');

  // draw out
  days.sort(function(a,b) { return a - b});

  ctx.fillStyle="red";
  ctx.strokeStyle="black";

  ctx.beginPath();
  ctx.moveTo(0,100);
  ctx.lineTo(280,100);
  ctx.stroke();

  for (var i = 0; i < days.length; i++) {
    var x1 = days[i] * 10;
    var t1 = 70;
    var x2 = 5;
    var t2 = 30;
    ctx.fillRect(x1,t1,x2,t2);
  }
}

</script>
</head>
<body>
<div>
  <p><span class="vevent">
    <span class="summary">Monkey Play Time</span>
    on <span class="dtstart">2010-02-05</span>
    at <span class="location">St. Louis Zoo</span>.
    </span>
   </p>
</div>
<div class="vevent">
  <abbr class="dtstart" title="2010-02-25">February 25th</abbr>,
  <abbr class="dtend" title="2010-02-26"> 2010</abbr>
  <span class="summary">Event</span>
</div>
<p>
   <span class="vevent">
   <span class="summary">Tiger Feeding</span>
   on <span class="dtstart">2010-02-10</span>
   at <span class="location">St. Louis Zoo</span>.
   </span>
</p>
<p><span class="vevent">
   <span class="summary">Penguin Swimming</span>
   on <span class="dtstart">2010-02-20</span>
   at <span class="location">St. Louis Zoo</span>.
   </span>
</p>
<div class="vevent">
     <abbr class="dtstart" title="2010-02-19">February 19th</abbr>,
     <abbr class="dtend" title="2010-02-26"> 2010</abbr>
     <span class="summary">Sea Lion Show</span>
</div>
<canvas id="calendar" style="width: 600px; height: 100px; margin: 10px; ">
    <p>Dates</p>
</canvas>
</body>

The application works in all of our target browsers, including IE8, as shown in Figure 19-2. IE7 does not support the querySelectorAll method.

See Also

For more on Microformats, see the Microformats website. See more on the canvas element in Chapter 15, and more on ExplorerCanvas in Recipe 15.2.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required