2005-08-22

More calendar widgetry

This is a follow-up article to my recent entry on setting up Blogger with calendar navigation. I've been polishing it some more to better fit my preferences.

Actually, I was just following my own suggested further improvement, in adding the popup variant, which has some really nifty keyboard navigation added in, right out of the box --

  • ←, →, ↑, ↓ -- select date
  • CTRL + ←,→ -- select month
  • CTRL + ↑,↓ -- select year
  • SPACE -- go to today's date
  • ENTER -- go to the selected date
  • ESC -- cancel selection
-- but come that far, I noticed that all the disabled dates in view could still be focused, only just not visibly so. So it was really a bit like navigating with a paper with holes punched for the legal choices stuck in front of the screen. You'd notice when you hit an available date, but otherwise be generally lost. Not pretty.

So I set about tweaking the code to allow my preferred mode of browsing -- focus snapping only to dates that can actually be focused and selected. As it turns out, jscalendar is really a SourceForge project too, besides being LGPL, so I took the liberty of submitting a patch with my changes when I was happy with them.

An example setup is running on my real life blog; click the calendar icon in the sidebar to take it out for a spin, if I still haven't already updated the template in this blog too, by the time you read this.

So, what changes do you need to do to get this baby running?

Not much, really. It's mostly a matter of adding a method that will seek out a date to snap to, and provide it during Calendar setup. I did it like this:

  Calendar.setup(
{
step : 1, // show *every* year in the year menus
date : thisDate, // the date selected by default
align : 'br', // below/right from top/left corner
button : 'navigate', // click this element to open
onUpdate : dateChanged, // navigates to chosen entry
pickPrevNext : pickPrevNext, // can only focus our dates
dateStatusFunc: disableDateP // which dates to show/hide how
});

where most is kept intact from before (I've added an <img id="navigation" style="cursor:hand;cursor:pointer;" .../> to the mix, to trigger the popup, too -- the ugly style rule is to get a mouseover hint that works in most browsers too while hovering the icon).

You also have to provide the pickPrevNext method, though, and in my case it looks like this (together with the slightly changed disableDateP and init code):

// prevP == true: find the last value in ok to satisfy ok[n] <= pick,
// prevP != true: find the first value in ok to satisfy ok[n] >= pick
function pickPrevNext( pick, prevP )
{
var ok = top.notelist;
pick = pick.getTime();
if( prevP ) // find last x <= pick
{
for( var i=ok.length-1; i>=0; i-- )
if( ok[i] <= pick )
return new Date( ok[i] );
}
else // find first x >= pick
for( var i=0; i<ok.length; i++ )
if( ok[i] >= pick )
return new Date( ok[i] );
return null;
}

function disableDateP( date, y, m, d )
{
var now = new Date;
if( (y == now.getFullYear()) &&
(m == now.getMonth()) &&
(d == now.getDate()) )
return false;
var t = new Date( y, m, d );
return noteFromDate( t ) ? false : true;
}

function noteFromDate( date )
{
return top.notes[date.getTime()];
}

function renderCal()
{
var archive = document.getElementById( 'archive' );
if( archive )
{
archive.style.display = 'none';
var notes = {};
top.notelist = [];
var links = archive.getElementsByTagName( 'a' );
var i, j, node, date, y, m, d;
for( i=0; i<links.length; i++ )
{
node = links[i];
date = node.innerHTML.split('-'); // YYYY-MM-DD
y = parseInt( date[0], 10 );
m = parseInt( date[1], 10 );
d = parseInt( date[2], 10 );
t = (new Date( y, m-1, d )).getTime();
notes[t] = node.href;
top.notelist.push(t);
}
var dates = document.getElementsByTagName( 'h2' ), thisDate;
for( i=0; i<dates.length; i++ )
if( dates[i].className == 'date-header' )
{
var ymd = dates[i].innerHTML.split('-'); // YYYY-MM-DD
thisDate = new Date( parseInt( ymd[0], 10 ),
parseInt( ymd[1], 10 )-1,
parseInt( ymd[2], 10 ) );
break;
}
top.notes = notes;
// the above Calendar.setup() call goes here.
}
}

That wasn't very hard either, was it? Yes, one more thing -- I changed the init method to read renderCal, after having read in the docs that the popup code might want to zap window.calendar, hence not making that a very good name to use for our init method. So you'll want to update your body tag to read <body onload="renderCal()"> as well.

It's still not quite all I expect of it -- for instance, the dropdown menus don't adhere to the same navigation principle -- you can still pick months there which have no legal dates, or even if you pick one that does, you won't arrive at the note closest to the note you came from. I'll just have to leave that for another day, or another hacker.

7 comments:

  1. HELLO ecmanaut!!

    Good Morning from France !

    Sorry to annoy you with any technical question, but I LOVE YOUR CALENDAR ON YOUR BLOG !

    See, I wich to implement some photo private workshops.

    For this purpose, I will need a CALENDAR showing at the top of my 'Links' list.

    This YEARLY 2006 calendar should show, in any chosen colours, months by months, the periods (schedules) of my courses.

    Please could you suggest, help or tip for doing this, KNOWING I HONESTLY HAVE NO SKILLS FOR HTML editing ..! ;o)

    Thanks a million for your help, and may you need my code to see the blog and act in it, please ask!

    the link: http://thecreativephotoworkshop.blogspot.com/

    Yours!
    etoile

    ReplyDelete
  2. Good Day From Singapore...

    I like your calendar alot... Do you mind to guide me to put a same one like u inside my blog... pls... pls..

    Can kindly tel me how u put your email under about me... I try but i cant :(...

    Thanks alot and have a great day ahead...

    ReplyDelete
  3. A link to my calendar howto is in the first sentence of this page. Another article on showing email addresses, in combination with Blogger's help about editing your blog template might help you with the latter problem.

    ReplyDelete
  4. I don't understand. How do i place the code? In my template or in the "page element" in Blogger beta?

    I try this code:
    http://www.zapatec.com/website/main/coding.jsp

    but nothing is working :(

    ReplyDelete
  5. I put mine in the template, non-beta. I haven't tested this with in blogger beta, so I can't guide you. If you used something from zapatec, try their support channel. Good luck, and sorry if this wasn't helpful.

    ReplyDelete

Limited HTML (such as <b>, <i>, <a>) is supported. (All comments are moderated by me amd rel=nofollow gets added to links -- to deter and weed out monetized spam.)

I would prefer not to have to do this as much as you do. Comments straying too far off the post topic often lost due to attention dilution.

Note: Only a member of this blog may post a comment.