Published 2006-02-04 09:23:56

A few weeks ago, a friend sent me a link to zimbra , While I quite liked the look of their calendar, i was not too keen on the java stuff. The XUL based Calendar I wrote a while back, while getting heavily used, (I basically track all my work in it). had not been developed any further, mainly due to the Proof of concept style hack that the code had ended up as.

And as I had recently upgraded most of my browsers to Firefox 1.5, and almost got my name in the mozilla hall of fame for fixing a tiny bug in it;). I was a little intreged by the SVG (and canvas) that comes built into it.

SVG CalendaerSo in a quiet afternoon, I sat down and started hacking away to see how easy it would be to write a calendar application using these new graphic libraries.

You have to click on more, for a detailed breakdown of what is involved in interactive SVG...

Canvas

Mozilla's tutorial
My first attempt was to use the new <canvas> element, While I spent a little time to getting this to work, It became quite clear very early that drawing text on the graphics area is currently not possible (although you can use various hacks to take screenshots of hidden areas with text on, and overlay them on the canvas) - another option may be using XUL stack or similar in HTML to overlay text ontop of the graphics - but this all seemed like one kludge to many for my calendar.

One of the attractions of Canvas over SVG is that the drawing code in javascript is slightly cleaner (eg. you dont have to keep creating DOM nodes to draw stuff). but as I later discovered, that is really a very small part of doing interactive applications in SVG.

And finally to SVG

So after abandoning my Canvas effort, I began on my explorations with SVG. Most of the code still inherit's a ghost of the first efforts, as I used the variable name 'canvas' to represent the SVG DOM node.

Over the week, it must have taken to build the first working version, I ran into quite a few quirks of the current implemenation in Firefox, but in the end managed to turn my unmaintainable code from the first XUL calendar into something considerably more usable, and flexible.

Speed

SVG, while not dog slow, is a touch slower than pure XUL for the calendar. It is usable, and I expect it will probably improve over time, as by the looks of things, SVG support was added to get more real world testing.

My XUL calendar had also begun to suffer from speed issues in Firefox 1.5, although alot of this was due to the event model I had used. In the XUL calendar, I had used {node}.addEventListener('mousedown'....) etc. to all the calendar entries as i created them. Then also added an event listener to the background grid that made up the calendar. My guess is this complex tree of event handlers which could be called slowed down the event handler in firefox. So for the SVG version, I only added a single set of event handlers to the top level <svg> node.

When these event handlers are fired, they recieve the event.target, and decide which event hander object to instantate to deal with the event. (normally on mouse down). The main handlers are Drag/Resize/Create/Edit/ProjectSelect. each one has it's own class which handle all the later events (like mousemove/up/keypress etc.), These all extend from a dummy base class, which does nothing in most cases (so I dont have to keep checking if there is a current event handler method) This change is probably the most significant from the original XUL code, and greatly enhanced the readability of the code.

Debugging

One of the first bugbears of working with javascript has always been debugging. While Firefox has quite a few tools, The Javascript Console being essential (now greatly enhanced with CSS error messages). I find the JS debugger a bit of an overkill. I have been far too used over the years to doing 'print_r($somevar)" or similar in PHP to see what is going on when. and while javascript's alert() is usefull for the occasional debugging message, it can get a bit annoying when testing. (Especially if you accidentally loop for ever doing alert()).

After a bit of googling I came across the idea of debugging to another window - while that sounded reasonable. I guess tabbed browsing and popup blockers have numbed me to the attraction of them. So I slightly modified the concept to write to a <PRE> element within the body of the document. - this one's for the library:

        function showDebug(str)
{

if (typeof(debug_node) == 'undefined') {
debug_node = document.getElementById('debug');
// now == null if not found!
}
if (!debug_node) {
return;
}
var data = document.createTextNode(str+"\n");
var before = null;
if (debug_node.childNodes.length) {
before = debug_node.childNodes[0];
}
debug_node.insertBefore(data, before);
if (debug_node.childNodes.length > 50) {
debug_node.removeChild(debug_node.childNodes[50]);
}

}

And to make it work, in XUL I add
<html:pre id="debug" style="height:100px;width:100%; overflow: auto;">
hello world
</
html:pre>
Basically use DOM add node's to add children to the PRE tag.


Drawing in SVG

The actual drawing component of SVG is quite simple, you just create DOM nodes, and add them to your <svg> or <g> elements.
Using the XML view from Inkscape helps quite a bit, although Firefox doesnt support quite as many tags as Inkscape. This example is from the method that draws horizontal lines in the calendar.

           for (var i = 0;i < 18; i++) {
var shape = document.createElementNS(svgns, "line");
shape.setAttribute("x1", canvas.hour.leftmargin);
shape.setAttribute("y1", canvas.hour.height * i);
shape.setAttribute("x2", this.canvasbox.width);
shape.setAttribute("y2", canvas.hour.height * i);
shape.setAttribute("style",
"
stroke:#cff;fill:#cff;stroke-width:5");
group.appendChild(shape);
}
I also tended to add extra attributes (eg. "cal:id") to the group describing a calendar event, and add the javascipt object describing the event to the dom node group (eg. g_domnode.calendarEvent = this;)

Dragging is done by mousemove events and changing the transform attribute of the group containing the calendar event.
this.g.setAttribute("transform", "translate(" + 
this
.g.getAttribute("cal:x") + ',' +
this.g.getAttribute("cal:y") + ')');
Resizing is a little more complex, in that it has to resize the body rectangle, along with moving the time information, and a line I added to indicate that you are resizing.

Text issues

Text on SVG (in Firefox's current implementation) is a little tricky. as there is no <foriegnobject> support, you can not embed a <div> of text. you have to create text nodes.

Text nodes unfortunatly do not wrap, and require a little bit of work to clip. For the body description on the calendar event, we effectively build this svg
<clipPath id="cp123">
<rect x="2" y="50" width="80" height="120">
</clipPath>
<g clip-path="url(#cp123)">
<text x="5" y="10" fill="#000" style="font-style.....">
.. more text nodes....
</g>
We then adjust the clipPath/rect when we resize the event.

Interaction - Text editing

The two key differences between my original Calendar, and the one you get in Sunbird where
  • editing inline (double click and you edit the text in the box, rather than a silly popup appearing)
  • project tagging of events - for use later will billing/review apps.
The editing inline makes a huge difference from a usability perspective, but proved a bit of a challenge with SVG/XUL. In the original XUL calendar, a <stack> is used to position a editing calendar entry ontop of the shown one, then hide the original.

SVG does not provide any editing widgets, and as I mentioned before, there is currently no <foriegnobject> support. So I basically did the same trick as before, use a <stack> to position a <textbox> ontop of the calendar event (and a little clever maths to get the location and size correct.

The result however is a little odd, the cursor does not flash when you overlay the textbox ontop of the SVG area, and there is no natrual visual indication that you are editing. (other than it changes when you type...) - To try and alieviate this, I just thickened the border of the editing area.

Scaling the graphics - and wonky layout.

If you have ever loaded a svg url into firefox 1.5, you will notice that most of the time the graphic ends up huge, and you have to scroll around the window to see the picture. When embedding an graphic into a web page, you use the <svg> tag which has a few usefull attributes:
 <svg viewBox="0 0 1200 600" preserveAspectRatio="none" 
width="98%" height="600">
- viewBox tels the browser to scale the coordinates so that you can see that range within the box containing the svg element.
- preserveAspectRatio can be used to tell the renderer if you want the x/y ration to be fixed or scaled. If you are displaying text inside the image, then it's usually not a good idea to stretch the aspect ratio.

I tended to find that using 98% as the width prevented a scrollbar appearing at the bottom of the screen. However to get the layout correct, we have to manually query the pixel width of the svg, and re-adjust the viewBox to match the actual width in javascript on the window.onload event.

However for some reason after doing this, the layout did not render correctly until you edited a event (overlaying the textbox) - then making it disappear. - In the end, I faked this by going through this process at the onload stage.

Talking to the backend

Having just spend a few months working with SOAP, this is one technology which should have been burnt at the stake the day it was born!. - dumb idea, from start to finish (undebuggable, slow, uncachable). - So that definatly got ruled out ;)

Almost all the "AJAX"(and I still hate that acronym) code I'm doing sends HTTP GET/POST requests in standard syntax (eg. just like a HTML form).. When you move the event, it sends a url with day=3&startdate=2005-12-01&... in the body. This makes the backend code clean, simple, and easy to debug.

At present I'm sending XML back from the server, JSON may be a possibilty here, but I'm not too keen on running eval() on the client side, or including an extra library, when DOM parsing is so simple.


Let's have a play?

you can have a play with it by visiting the bug tracker, and just logging in as guest (press the guest button)
- creating entries is done by selecting a timeblock click and move, then pressing any key.
- drag either the top or bottom to resize / move. and double click to edit.
- change colour (or really project) by right hand mouse clicking on the top menu bit.

If it's not working, It's probably as I'm hacking on it..

The rest of the bug tracker is pretty broken at present.. - It's an ongoing disaster, that I toy with occasionally - one day you will be able to report bugs ;)

If you want to explore the code, either look at the svn/xul viewer under FlexyBugs/FlexyBugs/templates/week.xul and FlexyBugs/FlexyBugs/templates/images/*.js
or browse to the svn on-line
http://www.akbkhome.com/svn/FlexyBugs/FlexyBugs/templates/
The PHP backend code for it is pretty simple
http://www.akbkhome.com/svn/FlexyBugs/FlexyBugs/Calendar/
although this does not include the queries related to the Project database.

TODO's

There's quite a few todo's on the list, some may get done quicker that others
- editing 1/2 hour events is probably the biggest - it's done in the XUL version, by having a minimum textbox size.
- stacking, I think would had hugely to the visual appeal.
- merge dragging and resizing, and base it on mousedown position, rather than rectangle...
- lots more..


Interesting technology.. - things like project managment tools look feasible..
Mentioned By:
iandavis.com : Internet Alchemy &raquo; SVG Calendar (1085 referals)
google.com : xul calendar (664 referals)
www.josephguhlin.com : Playing around with XUL and SVG &raquo; Cooling Tower of Technology: VoIP, Xul, and More (634 referals)
swik.net : PHP: Hypertext Preprocessor : Planet PHP : SVG/XUL Outlook style Calendar - SWiK (344 referals)
google.com : xul svg (248 referals)
google.com : php outlook calendar (229 referals)
www.phpdebutant.org : Bienvenue sur PHP Dbutant (224 referals)
google.com : javascript outlook calendar (178 referals)
google.com : svg xul (175 referals)
www.planet-php.net : Planet PHP (168 referals)
google.com : (168 referals)
google.com : february (153 referals)
google.com : svg calendar (104 referals)
google.com : php calendar outlook (103 referals)
google.com : outlook calendar php (102 referals)
www.la-grange.net : Mes pas anonymes - 2006-03-18 - Carnet Web Karl (98 referals)
www.phpn.org : SVG/XUL Outlook style Calendar (87 referals)
google.com : PHP SVG (68 referals)
www.reddit.com : programming (67 referals)
www.ilkebenson.com : Ilke Benson News: Dibujando con SVG (66 referals)

Comments

Screenshot?
Hi,
above you mention "although you can use various hacks to take screenshots of hidden areas with text on, and overlay them on the canvas". This sounds very interesting, and I would need exactly this functionality for my project. Could you please give me some hints where I can find out how this is possible?

Thanks, Oliver
#0 - Oliver ( Link) on 2006-11-13 21:05:23 Delete Comment

Add Your Comment

Follow us on