How to filter a calendar in Outlook Today for Outlook 2003

I use Outlook as a calendar program and I spend most of the time with the application in a small window showing the Outlook Today page. That gives me a quick overview of where I needed to be.

I like the window to be really small. This means I can keep it around on the screen when doing other things.

However, I also have several repeating elements on my calendar which I use to stop people booking me into meetings at inopportune times. These include blocking out time before I get to work in the morning, a reminder for lunch so I don't forget to go to the canteen before it closes and other bits and pieces. I don't really need these on Outlook Today and the spoil the neat look of the calendar.

So, a few months ago I wondered if it was possible to tidy up the display to remove these repeating items. A lot of searching on the web didn't get me an answer. I couldn't find an easy way to filter which calendar items appear on Outlook Today. If it is possible, I couldn't find the information.

In the end, I had a bit of a brainwave and realised that since Outlook Today is a web page and since you can alter which web page it uses and since the web page can contain Javascript I could be sneaky.

So, for the benefit of anyone else trying to solve the same problem, I'm recording here what I did.

First a note: I took this solution as far as I needed to to solve my problem. I've not polished it. I was intending to polish it before I wrote this article. However, I've not had time. Since shipping is a feature I've decided that 8 months of sitting on the solution should come to an end. The time has come just to publish what I've got so other people can use it. If you want to polish this up, feel free. I'll put some suggestions in various places.

I started with the reference two column Outlook Today HTML page I got off the web probably from the Microsoft web site. To be honest I then dove straight in and started hacking on the hard part of the problem to see if I could get a solution. One of the last things I did was a major tidy up. However, to make it easier to see the important part of my changes, I've generated a version with just the tidy up and without my significant changes to make it easier for you to see what's going on.

So, the file OutlookToday-tidied.htm has had: a major tidy up of the Microsoft supplied file to improve the Javascript and the CSS; the customize button has been removed; the colours and the layout have been tweaked; the column holding the next appointment arrow has been removed; and, any references to graphics have been removed.

Note that there seems to be a bug in this which I inherited from the original files. As far as I can tell, when the day changes, the date is not updated. This appears to be because the line:

document.all.dateScript.innerHTML = window.external.GetDate();
    

appears just once in the file so is executed only when the page is first loaded. As it happens I fix this bug later.

The next step is to write the Javascript to do the filtering. The basic idea is to use Javascript to traverse the HTML DOM for the auto-generated table that Outlook puts into the document dynamically. When we find an entry we want to delete we simply set the display style to none.

One trick is that we then need to update the little arrow that indicates the next appointment if it would have pointed to one of the appointments we're deleting. Another trick is that if a day ends up completely empty we need to remove the header for that day.

We also need a criterion to decide which appointments to delete. We have only the HTML to play with, not the underlying calendar objects. Since the appointments I want to delete are private appointments I went for the simple expedient of adding [NO OT] to the subjects of those appointments and then deleting any calendar rows that contained that string. This is a bit dirty but solved my problem. A more polished solution might allow the user to specify a list of substrings (or regexps for advanced users) on a configuration page. Alternatively, you could hack the test in the Javascript yourself.

There are no comments in the code so I'm going to talk you through the structure here showing how it was built up. This means taking the code out of sequence to emphasise the structure. Some of the quoted code will be missing lines.

The function to do the hiding, called hide_appointments(), starts off by getting the table containing the calendar by saying:

  var cal = document.getElementById("CalendarLiveTable")
    

Then we're going to iterate over the rows in that calendar with:

  var rows = cal.rows;
  for (var i = 0; i < rows.length; i++) {
    var r = rows[i];
  }
    

For the benefit of anyone else who makes the same mistake, I'll note that my Awk and Perl experience wanted me to treat rows as a container object that I could iterate over with for (var i in rows). This doesn't work as it iterates over all properties of the Array object rows rather than all elements. Such a loop does get all the elements but it gets other things as well.

Now we go through the cells on the row looking for one with the dataFld attribute of SubjectLocation. That contains the subject of the appointment and is the field we're going to filter on.

    var cells = r.cells;
    for (var j = 0; j < cells.length; j++) {
      var chld = cells[j] == null ? null : cells[j].firstChild;
      if (chld != null) {
	if (chld.dataFld == "SubjectLocation")
	  sl = chld;
	}
      }
    }
    

We need to skip the header rows for each day. We can detect these because the cell we identified in sl contains an object with the class dayHeaders. So we can build a little helper function called find_class() which looks like:

function find_class(ob,cl) {
  for (; ob != null; ob = ob.nextSibling) {
    if (ob.className == cl)
      return 1;
    if (find_class(ob.firstChild, cl))
      return 1;
  }
  return 0;
}
    

We can then use it like this:

    if (find_class(sl, "dayHeaders")) {
      continue;
    }
    

For the moment I'm skipping the code that hides headers for days that end up without any appointments.

And finally, we extract the text of the subject of the appointment, see if it contains the magic string and, if so, hide the row.

    var subloc = sl.innerText;
    if (subloc.match(/\[NO OT\]/)) {
      r.style.display = "none";
    }
    

That's the core of the algorithm. Now we can look at the code that hides the headers for days which end up without any appointments. The code has a variable called dayhead this is either null or points to the day header we should hide if we get to the end of a day without finding any appointments. So, at the top of the function we create the variable. When we find a day header in the code shown earlier we set dayhead to point to that row but first, if dayhead was already not null we hide the previous by saying dayhead.style.display = "none". If we find a calendar entry we're not going to hide — that is, in the else clause I omitted from the code that hides appointment rows — we set dayhead to null. And finally if we get to the end of the table and dayhead is not null we hide the row it points to.

Working out how the pointer to the next appointment is handled is left as an exercise for the reader. Note that the version of code here doesn't have a column in the table for the next appointment arrow, instead it just colours the next appointment(s). I did this to reduce the horizontal space of the table.

The next question is to work out when to call this function. Outlook repopulates the table periodically — such as when the next appointment changes or if your calendar is updated. In the end I couldn't work out how to do this so I simply set a timer by using the code:

window.setInterval("hide_appointments()", 5000);
    

Sorry, that's not very subtle but it appears to work. Also, by adding code to the end of the hide_appointments() function to reset the date, we can fix the problem of the date not being updated when the day changes.

The complete file is in OutlookToday-filter.htm

One slight issue with this solution is that you see Outlook populate the table with all calendar entries and then a few seconds later the uninteresting ones disappear. I think the way to fix this would be to put the auto-generated table into a div with a display style of none and then copy it into another div which is shown. Maybe the IDs would need to be fixed up on the way. I've not done this. I quite like seeing the script do its work.

The version of the script I use goes slightly further. It processes the appointment subjects to remove extraneous words. In the else clause of the code that identifies appointments to be removed, it says something like:

      var inner = sl;
      while(inner.firstChild != null && inner.firstChild.innerHTML != null)
        inner = inner.firstChild;
      inner.innerHTML = inner.innerHTML.replace(/^Updated: /,"").
        replace(/^FW: /,"");
      inner.innerHTML = inner.innerHTML.substr(0,1).toUpperCase() +
        inner.innerHTML.substr(1);
    

The first bit gets down to the HTML element containing the actual text. The second bit does as many regexp replacements as I want. The third bit capitalises the first word of the remaining text.

It would be nice if this prefix removal were customisable.

Another suggestion is that you don't have to hide entries, once you've matched them you could change their colour or do anything else the Javascript will let you do.

The version I actually use has a shared calendar on as well (unfiltered). There are various tutorials on the web that tell you how to do this. If anyone gets as far as getting customisations sorted, then a customisable second calendar location would be nice.

If you want to use any of the Outlook Today calendar's I've put on here then copy them to your local drive. Do not link directly to them on this web site. Firstly, I have only a limited amount of bandwidth so if I find people abusing this I'll hide the files in a zip archive. Secondly, if you're not on the network it probably won't work.

To use one of these files, once you've saved it locally, go to the folder list on Outlook Today. Right click on the Mailbox and select Properties for "Mailbox"... Go to the Home Page tab. Click on Browse... Find the file you just saved. Press OK in the file browser. Press OK in the Properties window. A warning will come up about certain file types not being suitable. This is harmless — the file you selected is suitable as it's on your local disk. Press OK to dismiss this warning. Then press cancel on the Properties window (otherwise you'll get the message again).

I hope you found this useful.


This page is copyright 2010 Steven Singer. I would say that the two Outlook Today files I've attached are in the public domain in their entirety but I can't. They're based on files produced by Microsoft. So, I'll say this: if you have a licence to use the files Microsoft produced then you have a licence to use my modified files.


[Up] Up to the welcome page.
Steven Singer