An XForms case study, part 2: triggers for setting values

[20 February 2012]

This is another in an ongoing series of posts describing the design and implementation of the evaluation forms we put together last year for Balisage: The Markup Conference. The first post in the series discussed the overall look and feel of the forms.

The conference has used printed feedback forms in the past, and the online forms ask essentially the same questions as the paper forms have always asked. Overall, what’s your impression of the registration process? the materials (the on-site guide to the conference, the proceedings), etc. etc. For each question, the paper forms ask for an overall judgement (Good, Okay, Bad) and provide a space for comments. In recent years, the space for the overall judgement has taken the form of three small graphic images, showing smiling, neutral, and frowning faces:

A first version of the form

The XML document created by the form has a sequence of elements for the various topics: registration, materials, presentations, etc. Each covers one question in the form. For most questions, the element has an attribute named overall to record the user’s overall judgement of how well we did in that area, and its contents include any comments the user types into the comments field. (A few questions have a more complex XML representation, which will be discussed in separate posts.) If you would like to see the XML produced by the form in full detail, you can: go to the overview page, select the form you want to examine, optionally fill it in with some sample data, and select the Show XML button at the bottom of the page.

A straightforward translation into XForms of the question about registration would use a select1 element with the three values Good, Okay, and Bad, and an associated comments field, like this:

  <xf:group ref="instance('eval')/registration">
    <xf:label>Registration process</xf:label>         
    <xf:select1 ref="@overall" appearance="full">
      <xf:label/>
      <xf:item>
        <xf:label>Good</xf:label>
        <xf:value>good</xf:value>
      </xf:item>
      <xf:item>
        <xf:label>Okay</xf:label>
        <xf:value>okay</xf:value>
      </xf:item>
      <xf:item>
        <xf:label>Bad</xf:label>
        <xf:value>bad</xf:value>
      </xf:item>
    </xf:select1>
    <xf:textarea ref=".">
      <xf:label>Comments</xf:label>
    </xf:textarea>
  </xf:group>

In a browser, it would look something like this:

You can also load the entire form into your own browser to examine it.

A first enhancement: smiling faces

The first obvious enhancement is to follow the paper form in using images of smiling and frowning faces for the various values. A few hours on the Web turned up a large number of public-domain clip-art sites, many with sets of widgets including smiling and frowning faces. It turns out that there are also smiling-face and frowning-face characters in Unicode (in the Miscellaneous Symbols block U+2600 to U+26FF), but no corresponding neutral face. And nowadays there is an entire block of Unicode emoticons (U+1F600 to U+1F64F). I was tempted by the Unicode characters, but ultimately decided that on my screen, at least, they were too hard to read clearly. So eventually I settled on a set of icons from a clip-art site.

Integrating the smiley images into the labels is simple: just embed an img element in the label, as shown here.

   <xf:group ref="instance('eval')/registration">
     <xf:label>Registration process</xf:label>       
     <xf:select1 ref="@overall" appearance="full">
       <xf:label/>
       <xf:item>
         <xf:label>
           <img src="lib/smiley_thumbs_up.png" 
                class="emoticon" 
                height="20" 
                alt="Good"/> Good
         </xf:label>
         <xf:value>good</xf:value>
       </xf:item>
       <xf:item>
         <xf:label>
           <img src="lib/smiley_pleased.png" 
                class="emoticon" 
                height="20" 
                alt="OK"/> Okay
         </xf:label>
         <xf:value>okay</xf:value>
       </xf:item>
       <xf:item>
         <xf:label>
           <img src="lib/smiley_thumbs_down.png" 
                class="emoticon" 
                height="20" 
                alt="Bad"/> Bad
         </xf:label>
         <xf:value>bad</xf:value>
       </xf:item>

     </xf:select1>
     <xf:textarea ref=".">
       <xf:label>Comments</xf:label>
     </xf:textarea>
   </xf:group>

This provides a bit more color in the form:

To make it easier to see what the images are saying, the full form includes a large version of the images at the top, with explanatory labels. (This version of the form actually uses two different sets of clip-art images, so I could look at each of them in context and decide which one I wanted to use.)

A second enhancement: clicking on the images

The overall Good / Okay / Bad ratings still take up a lot of screen real estate, though.

And what I really wanted was not just to use the images as part of the labels for the radio buttons, but to have an even simpler interface: ideally, I wanted just to show the three icons and let the user click on them to specify that they thought things were good, back, or okay. (There is a certain amount of danger in this way of thinking: part of the point of XForms is to provide device-independent forms that might be rendered in different ways on different devices: getting really specific about details like this may interfere with device independence.)

This can be achieved in XForms, too. After some experimentation, I ended up a method of handling this that looks like this in the browser: a large image showing the current rating for the topic, and three small images (but larger than the images in the earlier versions of the form) for changing the rating.

The large display is handled with a sequence of xf:group elements, each of which contains an img element and each of which binds to the overall rating for a given topic if that rating has the particular value associated with the image.

   <xf:group ref="instance('eval')/registration">
     <xf:label>Registration process</xf:label>	     
     <div class="overall-rating">
       <xf:group ref=".[@overall='good']">
         <img src="lib/smiley_thumbs_up.png" 
              height="150px" 
              alt="good"/>
       </xf:group>
       <xf:group ref=".[@overall='okay']">
         <img src="lib/smiley_pleased.png" 
              height="150px" 
              alt="okay"/>
       </xf:group>
       <xf:group ref=".[@overall='bad']">
         <img src="lib/smiley_thumbs_down.png" 
              height="150px" 
              alt="bad"/>
       </xf:group>
       <xf:group ref=".[@overall='dunno']">
         <img src="lib/smiley_no_speak.png" 
              height="150px" 
              alt="good"/>
       </xf:group>
     </div>
    ...
   </xf:group>

So the big image for the registration topic will show a smiling face if the overall attribute on the registration element has the value good, and a frowning face if it has the value bad, and so on.

The value of the overall attribute could be set by the select1 elements shown above, but in the final version we used a set of XForms triggers (user-activatable controls — on a laptop screen these are typically buttons) labeled with the images for the values:

 <div class="simple">
   <xf:group ref="instance('eval')/registration">
     <xf:label>Registration process</xf:label>         
     ... <!--* read-only display image, as shown above *-->
     <div class="overall-triggers">
       <xf:trigger>
         <xf:label><img src="lib/smiley_thumbs_up.png" 
                   height="40" alt="good"/></xf:label>
         <xf:setvalue ev:event="DOMActivate" 
                   ref="@overall" 
                   value="'good'"/>
       </xf:trigger>
       <xf:trigger>
         <xf:label><img src="lib/smiley_pleased.png" 
                   height="40" 
                   alt="okay"/></xf:label>
         <xf:setvalue ev:event="DOMActivate" 
                   ref="@overall" 
                   value="'okay'"/>
       </xf:trigger>
       <xf:trigger>
         <xf:label><img src="lib/smiley_thumbs_down.png" 
                         height="40" alt="bad"/></xf:label>
         <xf:setvalue ev:event="DOMActivate" 
                      ref="@overall" 
                      value="'bad'"/>
       </xf:trigger>
     </div>
     <xf:textarea ref=".">
       <xf:label>Comments</xf:label>
     </xf:textarea>
   </xf:group>
 </div>

As you can see, each trigger contains a label element which in turn contains the image, and a setvalue element which specifies the action to be run when the trigger is activated. In each case, the action is to set the value of the overall attribute to the appropriate value.

The group as a whole, the large image, and the set of three triggers are each wrapped in an XHTML div element with a different class value, to make it easier to style the display appropriately using CSS.

With XForms, it was easy to get a first working version of the form, much easier than it has ever been for me to get an equivalently complex form running in straight HTML forms, with its model of form content as a flat undifferentiated sequence of attribute-value pairs. And with XForms and CSS, it was (relatively) easy to change the look and to replace the radio buttons of the first version of the form with image-labeled buttons. Working with Javascript libraries has never been anything like this straightforward for me.

How many attributes?

[9 March 2011]

Beginning programmers are often taught to avoid arbitrary limits in their software, but in reality it’s not unusual for programmers to write capacity limits into their software: fixed-length fields are often easier to deal with than variable-length fields. So we have fixed-length IP numbers, fixed-length addresses in most machines, and who knows what all else.

So I wasn’t surprised today when I found a hard limit to the number of attributes allowed on an XML element in an XQuery implementation I use and think highly of.

But every now and then, the fixed limit turns out to be too small. That’s one reason beginners are instructed to treat such limits with suspicion.

When I first learned about IP numbers, for example, I remember being told there were enough IP numbers for everyone in the world to have one, including all the people in China. That seemed an extravagant plenitude of IP numbers, partly because we didn’t really expect everyone in China to want one. We didn’t expect everyone in the U.S. to want one, either: at the time, the only computer anyone in the room used for network activities was a shared mainframe, so we needed far fewer than one IP number per person then: looking ahead to a time when people would want their microcomputers to be on the internet felt like being farsighted. We did not foresee (at least, I certainly didn’t) that individual machines without a human in attendance might also want IP numbers, so that the world would need more than one per human. So IPv6 became necessary. (If you don’t know what I’m talking about, don’t worry: sometimes limits that look reasonable at the outset turn out to be too low, especially if the system they are built into becomes highly popular.)

Memory addresses of 32 bits also seemed extravagant for a long time (that mainframe I worked on supported several hundred simultaneous users in a 24-bit address space; the actual hardware supported 31-bit addresses, but only specialized parts of the operating system used more than 24 bits). But nowadays more and more machines now are moving to 64-bit addresses.

So I also wasn’t surprised that the reason I learned about the hard-coded limit in the XQuery engine was that it turned out to be set too low. The software declined to handle some XML I need to work with for a client’s project. It turns out that current versions of the Unicode Character Database are available in XML, which makes my life a lot easier. But most of the many character properties defined in the Unicode Database are represented as attributes, which blew well past the limit of (are you sitting down? ready?) 32.

I was a little surprised by the actual value of the limit: 32 seems like a very low number for such a limit. Who in their right mind (I found myself thinking) would expect a limit like that to suffice for real work? Who, indeed? But upon reflection, I remembered that I’ve been using this XQuery engine without incident for at least two or three years, doing a good deal of real work. And all the XML I’d dealt with in that time came in under the limit. So while 32 seems like a low limit, it seems to have been fairly well chosen, at least for the work I’ve been doing.

The happy ending came when I wrote to the support list asking if there were some way to change the configuration to lift the limit. Less than ten minutes later, a reply was in my inbox (from the indefatigable Christian Grün, who must work very late hours to have been at his desk at that time of the night) saying that in current versions of the software (BaseX, for those who are curious, one of a number of excellent XQuery implementations available today), the limit has been changed to 231, so now it can handle elements with a little over two billion attributes. (Hmm. Will that do? Well, let’s put it this way: if I wanted to experiment with a restructuring of the Unicode database that had one element per character property or property value, and a boolean attribute for each character indicating whether that character had that property [or that value for the property], the software could handle that many attributes. Actually, it could handle about a thousand times that many.

Moral: it’s not necessarily an error when software has a fixed capacity limit. But as a user, you normally need to take care that the limits are appropriate to your needs.

Moral 2: when you do bump your head on a limit of this kind, it’s very handy if those responsible for the software are responsive to user queries. Even better, of course, if they turn out to have fixed the problem before you asked about it.

Why do some XML-oriented users avoid Cocoon 2.2?

[24 January 2011]

Over time, I’ve become aware that I’m not the only user of Cocoon 2.1 who has not yet moved to Cocoon 2.2.

In my case, the basic story is simple. When I first considered installing Cocoon 2.2, I expected installation to be very similar to installation of Cocoon 2.1; it is after all a decimal-point release. So I looked for a zip or tgz file to download and couldn’t find one. A little puzzled, I read the getting-started documentation, which informed me to my dismay that the first thing I had to do was install a particular Java build manager and start building an extension to Cocoon. (I have nothing whatever against Java build managers in general or Maven [the build manager in question] in particular. It’s just that the large majority of lines of code I’ve written in the last ten years are in XSLT, with Prolog and XQuery a distant second and third, with Common Lisp, Emacs Lisp, C, Java, Rexx, and other languages bringing up the rear like the peloton in a bicycle race. So no, I didn’t have Maven installed and didn’t have it on my list of things to do sometime soon or ever.) Now, I like Cocoon a fair bit and moving to 2.2 still seemed like a good idea, so I got as far as downloading Maven and working through its five-minute introduction before I went back to the Cocoon 2.2 intro and learned that the first thing I was to do was develop a Java extension to Cocoon. That was when I lost patience, said “This is nuts” and re-installed Cocoon using an old Cocoon 2.1 .war file.

Some months later I thought about it again and decided I should give it another try. I did. And essentially the same thing happened again.

In the time since then I’ve encountered two or three other XML-oriented people who have told me similar stories as explanations of why they are still using Cocoon 2.1.

Recently I’ve come to believe that the problem here (if it’s a problem — and as a Cocoon 2.1 user, I think it is) is simple: the Cocoon 2.2 documentation is written (I guess) by people who think of themselves in some primary sense as Java programmers, and they have written it (not surprisingly, if in this case perhaps not quite reasonably) for people much like themselves: i.e. people who want to use Cocoon as a framework to write and deploy Java code and/or to extend Cocoon. There is no highly visible documentation for Cocoon 2.2 aimed at people who want to use Cocoon’s out-of-the-box features to create XML-based web sites where all the heavy lifting is handled by XSLT transformations under the control of Cocoon pipelines, and who are more likely to be interested in writing XSLT than Java. Me, I got interested in Cocoon precisely because I could do nice things without writing Java code for it. I am happy to know, and occasionally to be reminded, that I can extend Cocoon if I ever need to by writing Java code; I’ve done that in the past and I expect to do it in the future.

I think Cocoon 2.2 could get better uptake among XML-oriented users if there were some highly visible documentation aimed at that demographic. It might also help if there were a document on “Cocoon 2.2 installation for Cocoon 2.1 users” to explain that while Maven is indeed targeted at Java developers, you really don’t need to be a Java developer to find it useful: you can just think of it as a Java-specific package and dependency manager or a much-smarter FTP client specialized for downloading and installing Cocoon in just the way you want it.

More on this topic later.