I’ve been writing for some time now about the kinship between Apple’s Dashboard Widgets and web pages. I’ve recently written a time or two about Ajax and the various wonderful dynamic HTML (DHTML) JavaScript libraries that are now available to web developers. And when I first starting compiling the lists of available Ajax/DHTML JavaScript libraries, I was planning to grade Apple’s Widgets library along with all the rest. In explaining why I didn’t, here’s what I wrote last month about Widgets and DHTML pages:
It’s interesting that 2 months after an Adaptive Path essay coined the term “Ajax,” Apple released Mac OS X 10.4 “Tiger”, with its amazing and powerful dashboard widgets system. Within a couple of months, there were over 1,000 widgets available on the web, and these little babies were capable of completely replacing (almost all for free!) a number of system utilities, menubar items, and whole applications on the Mac. I’m tempted to think that awareness of Apple’s widgets helped promote awareness of, and interest in, what could be accomplished with rich Ajax/DHTML toolkits. After all, widgets are simply little Ajax/DHTML programs running in a special layer of Mac OS X called the Dashboard… They use exactly the same technologies as all of the Ajax/DHTML libraries, and in fact you can run them inside of Safari outside of the Dashboard.*
And so, it was fitting that when I finally found time to work on xCuts, a widget I’d been planning to build since last summer, I decided to use one of the leading Ajax/DHTML toolkits rather than Apple’s own, for most of the widget’s functionality. Having done most of my recent DHTML web work with Prototype and its light-hearted, freewheeling sidekick, Script.aculo.us, I naturally turned to those libraries to help me out.
Whenever I use script.aculo.us, I invariably get carried away and overdo the cool effects. (Witness this website!) But I do only use the effects when you’re supposed to–namely, as a visual cue to the user that some page content has changed. Without this visual cue, content can change so fast and smoothly on an Ajax page that users may not realize anything has happened… especially if they’re expecting the link they clicked to cause a page refresh that never comes. As an example, consider this link, which replaces this paragraph with different text stored on the server.
See what I mean? Even though your eyes were actually on the content that changed, the difference was so subtle you might have missed it. Now take a look at the lists of articles and news items in my navigation panels in the sidebar. Without Ajax, one list would give way to another so similar-looking that you might not notice. On the other hand, if you add an effect or two to the change, it’s impossible to miss. For example, try this link, which replaces this paragraph with another one from the server.
Now, having said all that, my primary failing is in going overboard with the effects. Rather than one simple yellow-fade, I prefer a yellow-fade combined with a slide-down, or perhaps a slide-down combined with a scale-up. But hey–don’t blame me! Blame that elvin genius Thomas Fuchs, who keeps thinking these things up. Why, just this week he published an article explaining how to do custom effects, as if the ones that come with script.aculo.us aren’t enough! If you’ve looked at this library, you know that what I’m saying is true. In addition to some powerful Core Effects (opacity, scale, moveby, highlight, and parallel), script.aculo.us comes with a series of Combination Effects, which are built using the core ones. In fact, it’s the “Parallel” core effect that makes this magic possible, allowing a developer to mix and match the core effects as much as their time and imaginations permit.**
But that’s not all! I haven’t even mentioned the possibility of using Callbacks, Transitions, and Effect Queues to fine-tune your effects… As much as I’d like to, I don’t have time right now to go into a full tutorial on the script.aculo.us effects, but fortunately, the script.aculo.us wiki is bursting with useful demos, reference documentation, and sample code to study. I usually start at the wiki’s All Pages view, which links to pages describing all the script.aculo.us functions, most of the relevant prototype functions, and all the demos and other documentation as well.
So I approached building the new xCuts widget as if it were basically a little web page that uses prototype and script.aculo.us to work its Ajax magic.*** Besides the animation, effects, and basic Ajax server calls, the widget incorporates the very popular live search form, which is my first implementation. It was actually surprisingly easy to build using prototype’s Ajax.Update function. Speaking of the search, the input element implements Apple’s HTML extensions, which were actually developed as part of Dashboard but can be used on any website as well. Using them, you can surprise Safari-toting visitors to your website with nice, rounded search forms… you know, the ones with the little magnifying glass filling one end and the spiffy focus, blur, and clear bits Mac OS X users have come to expect from native Cocoa apps. Though they are non-standard, these extensions do no harm to other browsers and merely enhance the web experience for WebKit users.
In addition, at the last minute I found the great DOMinclude library from Chris Heilmann, which came in handy to add the “i” supplemental information about certain shortcuts. Here’s a case where DHTML (Chris would prefer we start referring to it as DOM scripting, and he has a point, but habits are hard to break…) can come to the rescue: How can you present a lot of information on a keyboard shortcut in a compact space? There are always trade-offs in such a design, but I try to focus the user’s attention on the primary bits of info and provide secondary info in an opt-in format of some kind that doesn’t detract from the presentation or otherwise draw too much attention to itself. As I saw it, I had three options:
- Squeeze the “other info” data column in either a third column of the lists, or as a second paragraph in the second column. The first option ended up squeezing the first two columns too much, which seemed silly since only a few shortcuts need the third column. The second option mingles the “other” info column with the “action” column, which may not be appropriate in many cases. It also makes the whole list longer than necessary.
- Use DHTML to slide in the “other info” data as a new row, just beneath the row for its shortcut. This would have been an effective choice, but was going to take a bit more custom programming and would have caused the whole column to shift up or down a lot, which seemed a bit ungainly.
- Use traditional DHTML tooltips. Searching for a tooltip I liked is what led me to Chris Heilmann’s elegant solution. Before finding it, I was spending way too much time considering all the permutations of possible scripts, time that also involved learning how the script’s code was implemented and ensuring it was compatible with the other JavaScript functions in the widget. I ultimately bookmarked a handful of “tooltip” scripts that made the final running, but decided DOMinclude was perfect for my needs here. Besides its unobtrusive JavaScript foundation, I like the way it emulates the visual appearance of Apple’s Dictionary service.
Another spoonful of “syntactical sugar” I threw into the pot was Justin Palmer’s CSS event:Selector script. On Mars, I have been using Behaviour.js to enable unobtrusive, CSS-event-driven JavaScript functions, but then a month ago a leaner, more powerful script appeared that immediately synched with my brain. As Palmer says, event:Selectors are better integrated with Prototype, so the corresponding event rules require fewer “words.” In addition, they are more flexible, and presumably faster as well. So, I decided to try his script out in building xCuts rather than Behaviour, and I have to say it has an excellent aftertaste! As soon as time permits, I’ll rewrite the CSS Behaviour-style rules on Mars as event:Selector rules. The transition is easy, since, as Justin notes in his blog article, “Behaviour was the inspiration for event:Selectors,” and as such it adopts a similar syntax.
For convenience, I did use the Apple libraries for the flip animation and for the scrollbar and drag animations. Of those, the only one you really need the Apple libraries for is the flip animation, although the JavaScript cube animation script that Kava.net published recently could possibly be a replacement if you’re willing to accept a more jaggedy visual. Another area that prototype and script.aculo.us aren’t particularly strong in is scrollbars… they have no native function for them. On Mars, I’ve cobbled together a thin scrollbar for the navigation “panes”, but I went with a native OS scroll function in the “News Article” window that materializes in the middle of the page because it allows mousewheel scrolling and is much simpler to implement. I could do that with a Dashboard widget, too, but in this case native scrollbars can kind of ruin the look.
One small note is that as I got the widget built out I wanted users to be able to copy and paste from it if they chose. Looking around, I noticed that nearly all Dashboard widgets only had text-selectable sections in form fields. That’s because the onmousedown function that lets you drag widgets around the Dashboard also prevent the widget from understand a “copy” style mouse drag. I finally found one or two widgets that overcame this, and a quick visit to the Apple Dashboard docs revealed that the proprietary CSS code that implements the drag function is DOM-element-specific. In other words, though most developers seem to go with the default example code Apple provides, which applies the “-apple-dashboard-region” style to the whole face of the widget, you can instead restrict the region to whatever parts of the widget you want to be draggable. Cool.
For a back end, I had originally hoped to use Core Data, but I just didn’t have the time to learn how to access Core Data with JavaScript, and Apple doesn’t provide any roadmaps. Core Data would be ideal for this, but instead I fell back on existing skills and just set up a MySQL table and wrote a PHP page to access and format the data. Because of this, you won’t be able to use the widget when you’re disconnected from the Web. I still hope to transition it to Core Data in a future update, mostly for the learning exprience. On the other hand, with the database maintained remotely, I can easily continue to update the data source without having to publish new versions of the widget. Particularly in the first few months–when I have a lot of data I still want to add–that’s probably a good thing!
So… what did I use to build this widget? Gather round, and I’ll share this secret with you. Shhhh!! Here it is:
Find a widget you like, and use its code as a starting point.
As I pointed out last summer, one of the best things about building widgets is also one of the best things about building web sites: It’s all open-source code, and most developers release under free and open licenses that don’t make you feel like you’re stealing if you borrow a function here or there. That was certainly the case with one of my favorite widgets, SeeSS, a great CSS reference widget. Besides being at my fingertips when needed, I love this widget because it curls up into a tiny ball when you’re done, taking very little screen space. I also admire its overall layout and the fact that you can resize it as needed. By the time I was done, there really wasn’t very much of SeeSS left, but it was a great way to jump-start my work and gave me a nice framework to build from.
Tools? Definitely Widgetarium from Gandreas Software. I used this program for my first widget last July, and it has grown seriously more powerful since then. First of all, Widgetarium is great because you can start a project by choosing “File/New Project from Widget…”, after which you simply browse to any widget on your system, and select Open. Widgetarium automatically builds a new project for you from the components of that widget, including all image files, scripts, other source code, and–most conveniently–the XML .plist files without which the widget won’t work in Dashboard. After that, you can start tinkering with the code as you please without, of course, affecting the original widget.
Next, this tool still has that nice transparent window in which the widget builds, so you can stop and restart it all without leaving Widgetarium. As before, it has nice graphics tools for building pieces of the widget, but now you five tools instead of one. Besides the basic Panel Maker, you get a Button Maker, an Icon Maker, a “Roundie” Maker, and a slicing tool that lets you chop a panel into smaller pieces. I still export all the pieces to Photoshop for fine-tuning, and use Photoshop for the overall graphic design of the widget, but unless you’re extremely picky, the graphics tools in Widgetarium will give you a nice jump start on assembling the images you need. (You know, I’m seriously thinking of testing out other graphics editors since Adobe’s taking so long to get Photoshop Intel-ready. Anybody else thinking along those lines?)
New to Widgetarium is a truly useful JavaScript debugger, which has the usual stop/breakpoint features that let you step through the JavaScript code one function at a time. While doing this, the debugger provides a wealth of information on the variables and the DOM environment you’re working in. In fact, if anything the wealth is a little too much, and without a search feature it can be hard to find the variable you’re looking for. Still, this is a big improvement. In addition, there’s a console panel under the widget area that provides useful error messages, and you can toggle between that and a panel that lets you actually enter an interactive JavaScript session with your widget… testing different DOM calls, CSS properties, and so on, as part of debugging.
Just as useful is the DOM tree Widgetarium provides. Here, once you navigate down to a particular node, you get 4 panes of info about the node:
- Attributes: The node’s ID and all other attributes (like attached events) visible in the HTML source code
- Value: Only for text nodes, this contains the displayed text
- Style: The node’s CSS style as defined in the project’s style sheet(s), and
- Properties: I invariably find this the most interesting pane. Here you’ll find the node’s offsetParent, its parentElement, and its top and left offset measures (see screenshot).
If you’re developing JavaScript in Firefox,you can get this information in Mozilla’s DOM Inspector, but frankly Widgetarium’s interface is a heckuva lot more useful. What you get in the DOM Inspector is the same kind of thing that Widgetarium’s Debug window provides: Anything and everything, and good luck focusing on what you’re trying to find.
Even better than Widgetarium is the WebKit team’s Web Inspector, which presents the DOM and its nodes and styles in an incredibly compact, useful interface. The only problem with Web Inspector is that it isn’t finished. For now, it provides the Attributes, Value, and Style information you get from the other tools (including the “computed” style that’s so important in debugging), but not the Properties information. Web Inspector has two panes that stand empty as “Not finished” for now: Metrics, and Properties. I can’t wait to get them! Just like Mozilla’s Inspector, WebKit’s highlights the elements you select as you navigate, but Web Inspector has a far superior search interface. If you’re used to the Mozilla DOM Inspector search tool, you’ll be blown away by what you can do in Web Inspector.
Which brings me back to Widgetarium. One of the most glaring weaknesses–which I’ve pointed out to the developer, and he’s working on it–is the inability to search in either the DOM tree or the JavaScript debugger. This means, for example, that each time you want to find a node in the tree you have to manually traverse its parent nodes. Not a problem once, but unfortunately Widgetarium crashes often enough that you find yourself doing this walk way too often.
In my use, one of the things that crashed Widgetarium most reliably was trying to use the powerful “Symbol Browser.” How I wish I could have made use of this little puppy, but alas Widgetarium crashed every time I tried. I know it works with other widgets (the screenshot is from the MacUpdate widget), and I can see it would be very useful. What Symbol Browser does is the sort of thing you can do in TextMate and Eclipse in searching across your code base: Namely, Widgetarium compiles a list of all the functions, variables, properties, and methods used in your code, and you can find instances of whatever you’re looking for across all your files. If, like me, you have multiple, interrelated JavaScript files, this could be a real timesaver! The tool not only shows you the lines of code in a search-list format, but also brings up the full source code in a third pane when you select a given line. (Note: Click on the Symbol Browser thumbnail graphic to see the full screenshot.)
All of the Widgetarium editing screens have four useful features in common:
- A pulldown menu showing all the JavaScript functions (or CSS styles, or HTML elements, etc.) defined in the file, which you can use for quick navigation
- A pulldown showing various options for auto-indent, code-folding, syntax-coloring, line-numbering, and so on.
- A quick “go to line number” search, and
- The ability to split panes–as many times as you could possibly want! There are times I’d like to be able to bring another file into one of the split panes, but that wasn’t possible
The editing environment in Widgetarium is good, but not great. To improve it, my primary suggestion to the developer is to implement a single tabbed window. I find navigating among four or five open source code windows a bit tedious, especially after getting used to tabbed editing in BBEdit, TextMate, Eclipse, etc. Still, I no longer find myself using Widgetarium for some things and BBEdit for others, like I did last year.
There are many more useful features to Widgetarium, which I don’t have time to go into detail about but want to just mention here to whet your appetite (assuming you have one by now):
- Customizable syntax coloring
- Customizable key bindings
- Built-in lint and syntax checkers
- Incremental search option (should be the default, actually)
- Screenshot export options
- Image files viewer
- Top-notch documentation
- Full suite of embedded JavaScript and Dashboard reference material from Apple, the w3c, and elsewhere,
- Built-in extensions for working with audio, speech, AppleScripts, data from Spotlight, the Mac OS X clipboard, the file system, and more.
If it sounds like I’m a Widgetarium fan, you’re right… I am. And if the developer continues to improve this tool, pretty soon I won’t have any quibbles with it at all! At $50, I’m more than glad I shelled out the bucks last summer.
It’s hard to believe I could have so much to say about this simple little widget. As usual, I meant to sit down for an hour and found myself writing for four instead… All without even talking about the widget’s functionality! I guess you’ll have to figure that out for yourself! I hope you find it useful and enjoyable, and please let me know if you think of a good idea for improving it.
But most of all, have some fun building your own Ajax/DHTML widget!****
* Yes, I know that widgets can incorporate many native Mac OS X technologies that web pages can’t–like AppleScript, Core Data, file system calls, Core Image, and so on–and some of them do. But they don’t have to, and most of them don’t.
** And of course, down below, holding the whole structure firmly in place, is prototype.js, which provides the foundation that makes extending and combining JavaScript functions possible.
*** Both libraries were updated to new versions during development, but xCuts 1.0 is behind one or two releases (it uses prototype 1.5.0_pre0 and script.aculo.us so the 1.5.3). I’ll be updating xCuts soon to bring it up to the latest (prototype 1.5.0_rc0 and script.aculo.us 1.6.1).
**** If you want to build for Windows or for a browser other than Safari, check out Yahoo’s free Widget Engine, or Opera’s spanking new
Widgets API, which you can check out in the Opera 9 public beta.