Archive for the 'ajax' Category

Tag Hierarchies

About four and a half years ago I wrote a little event tracking app that accepts a timestamp and a list of tags, and then provides a pile of ways to report on the data.  Think Twitter, except a couple years earlier, and designed for consumption by software, not people, at least at the individual event level.  The app's been working magically since then, and now that there are a few people using it I realized I needed to support tag hierarchies.

The specific use case in question is aggregate reporting.  Say you track what you eat, and then you want to get a report of how often you eat vegetables.  To this point you've had two choices: retag all the events with 'celery', 'corn', 'carrots', etc. with an extra 'vegetable' tag, or write your report to OR together all the different veggies (remembering to update it every time you eat a new one for the first time).

On the flip side, if you just have a hierarchy of tags, you can drop all those tags underneath the 'vegetable' tag and then report on 'vegetable' directly, which has the implicit meaning of "itself and all it's descendants".  This is obviously much more desirable.

However, overlying a hierarchy is not without problems.  Tags are inherently free-form, and this app is no exception.  As such, the unique key needs to be the tag name by itself, not the combination of the tag name and it's parent.  So the solution I adopted is to expose tags in a flat structure, excepting in the actual hierarchy editor (for which I used the fantastic ExtJS Tree Control), and a slight tweak to the querying language.

Previously, you searched for events using a "tag:celery" style query.  With no hierarchy, that matched exactly the celery tag.  With a hierarchy, the semantics have changed slightly to the celery tag or any of it's descedants.  If you want the old behaviour, you'd use "tag:=celery".

Behind the scenes, I'm using nested sets for storage which makes those descendant queries lightning fast, though I ran into some interesting issues because there is only one storage table for all tags across all users, and each user's tag tree is potentially multi-rooted.  Neither are inherently difficult to deal with, but both were new problems and required a bit of reworking to my treemanager component.

All in all, the process was really painless, and I'm quite pleased at how transparent the overlay of hierarchy ended up being to the general functioning of the system.  In particular, ExtJS was a dream to work with.  It's fast, easy to use, easy to develop with, paired quite nicely with jQuery (what drives the rest of the app), and ended up requiring less than 70 lines of code to create the control, do lazy loading of children, add new nodes to the tree, reorder and rename nodes, and do all the backend calls to update the DB as needed.

jQuery's Autocomplete's Undocumented source Option

Yesterday I replaced an instance of my ComboBox widget with a jQuery Autocomplete.  A sad day it was.  However, I saved a JS file and a few K of download, so it was worth it.  Unfortunately, it's missing a fairly important bit of functionality: the ability to order the list of options.

If you're doing an Ajax-based complete, you can obviously order the options server-side and jQuery will happily spit them out in the same order.  In my case, however, the list of choices is static and fully known in the client.  jQuery only provides options for "url" (the URL to request for options) and "data" (a static array of options).  There's no half-way, where you can supply a function to return for the current term.

However, after spending a while digging around inside the internals of the widget, I discovered the undocumented "source" option (and it's complement, the "parse" option).  It's a function that will be called to get the options for the current term.  However, it seems to be undocumented, and is pretty tightly bound to the internal implementation of the widget.  Specifically, your return value structure has to be the same as the structure returned by the internal "parse" method.  Note this is NOT the 'parse' option I mentioned above.

If you don't supply data or a URL, the function passed as "source" will be invoked, and it's result will be either passed through the function passed as "parse" (if there is one), or returned directly.  That result must be an array of objects that represent the options.  Each object must have "data", "value", and "result" keys.  The first is an array of row data (passed as "row" to various callbacks), the second is the display value (what would come back from "formatItem"), and the third is the result value (what would have come back from "formatResult").

Mess though this is, it does let you execute arbitrary code to build the option list, including supplying a custom order, which is what I needed.  More specifically, I wanted to order tags that began with the search value before those that contained the search value in the middle.  Here's my code, just for reference.  "tagList" is an array of strings that are available for selection and the "tag" argument is the current value to be autocompleted.

jQuery("selector").autocomplete({
  source: function(tag) {
    var map = {
      first: [],
      internal: []
    }
    for (var i = 0; i < tagList.length; i++) {
      var pos = tagList[i].toLowerCase().indexOf(tag.toLowerCase());
      if (pos >= 0) {
        map[pos == 0 ? "first" : "internal"].push({
          data: [tagList[i]],
          value: tagList[i],
          result: tagList[i]
        });
      }
    }
    return map.first.concat(map.internal);
  },
  matchSubset: false // this is important
})

The FlexChart Manifesto

The current state of web applications is a fragmented mess.  HTML is still the standard approach, but dynamic data loading with Ajax and application embedding with Flash are both incredibly popular, particularly for data-heavy applications.

HTML is great because it's easy to build and well understood by user agents, but you lack drawing capabilities.  SVG addressed parts of that, and if XHTML ever gets adopted in any meaningful way SVG will see a resurgence.  The canvas element has stepped in, but it's not universally supported.

Flash is the other end of the spectrum: you have extreme customizability, but developing applications is significantly more work and requires people with a higher skill level than for HTML apps.  Flex, OpenLazlo, etc. attempt to ease the developer's pain, but it's still not nearly as easy as HTML.  Silverlight (Microsoft's Flash competitor) follows the same vein, though with the addition of binding to Microsoft's technology stack.  I'll only be refering to Flash from here out, because as far as I'm concerned here, they're basically equivalent.

Flash also has the significant downside that it's hard to go half way.  Applications tend to be either all Flash or all HTML.  Static HTML applications can add snippets of Ajax in an incremental fashion where they're best suited without going whole hog.  Of course, if you want to go whole hog with HTML, that can be a lot of work too.

But what does this have to do with FlexChart, you ask?

FlexChart provides a way for dynamic HTML applications to leverage the drawing capabilities of Flash in a nearly completely transparent way.  If you want to render a chart on a page in a static HTML app, you'll probably employ some server-side technology to build an image and embed that image in the HTML with the IMG tag.  But if you want to control the chart's appearance with JavaScript, you're in a bit of a bind, because you have to go server-side to generate a new image and then update the image source with the new URL.

Google Charts provides something of a solution for this problem, because the URL of the image implicitly causes a chart regeneration.  So you don't have to manually go server-side, it happens behind the scenes.  But as anyone who has worked with the API knows, it can be a bit of a bear to figure out all your data ranges, encode your data, lay out your axes and labels, compute bar widths, etc.  Plus, while the number of charts available is pretty good, they're fairly inflexible.  For instance, you can't plot a trend line across a column (vertical bar) chart.

FlexChart provides what I feel is a better solution for many applications.  It uses a simple XML dialect for specifying both chart structure and the data to display.  It leverages the Flex's charting engine to take care of all the chart layout tasks so you don't have to do that.  It's designed to be loaded once and draw multiple charts over the SWF's lifetime by passing it new XML descriptors via JavaScript (though it can also be used statically with descriptors from FlashVars).  Since it's Flash, it also has the ability to provide interaction with the user.  For example, when a user clicks a given data point on your chart, you can detect that with JavaScript and load a new drilled-down chart.  The tradeoff is that it's Flash, and while Flash has good penetration, it's significantly behind PNG support (what Google Charts uses), particularly on mobile devices.

As such, FlexChart addresses a narrow slice of the inability/reluctance to build hybrid HTML/Flash applications by providing a JavaScript-centric API for drawing rich charts.  Note that this API is not just for drawing static charts, you can register JavaScript functions for handling data tip rendering, click handling, and axis labeling.  This allows a very high level of interactivity that I've not seen replicated in any other charting package for HTML/JavaScript applications with similar browser support (or really at all).

Another significant advantage is the technology-neutral XML dialect that FlexChart uses.  There is absolutely nothing FlexChart-specific in the structure of the XML, so any charting engine could theoretically read and render the XML.  I'm currently in the initial stages of a Google Charts-backed implementation that does exactly that.  Yes, that means you can supply FlexChart XML to this new project and get back a Google Charts URL that displays the chart.  Obviously it won't be a complete implementation because of limitations in Google Charts (like the mixed line/bar chart issue), but for supported aspects it will be equivalent.  This will allow an application to render charts either as Flash (with rich interactivity) or as PNG images (for broad support) without having to change the format used to describe the charts.

Note that the XML descriptor language does NOT provide for the full gamut of capabilities that Flex's charting components have to offer.  Nor will it cover all that Google Charts has to offer.  The idea is that it provides a simple way to describe most common chart types, and do it in a rendering-neutral way.  I've also gone to great lengths to hide much of the complexity that Flex (and soon to be Google Charts) foists on chart developers.  For example, Flex forces you to create a huge number of redundant axis references when laying out complicated charts, but with the XML descriptor you can do it once at the top level.  In the other direction, Google Charts forces you to explicitly specify all your axis labels and their positions, but the XML descriptor is such that they can be figured out for you by the implementation.

As I alluded to above, if XHTML actually gains adoption and SVG makes a comeback, that's another very likely candidate.  I've used an SVG-based charting engine that I wrote five of six years ago on several applications (currently using Batik to conver the SVG to PNGs for display), so that would be another potential implementation.  There are several canvas-based charting engines as well, all of which could be potential targets.  Finally, there are preexisting XML-configured Flash-based charting packages (FusionCharts is one example).  Leveraging one of those packages from the unified XML dialect would come for the cost of some XSL templates.  The possibilities are quite promising.

FlexChart itself is only half of the puzzle.  Sure, it's greate to have nice charts in a HTML/JavaScript application, but the bigger picture is just as important.  Having a single way to describe charts across applications and rendering mechanisms is a huge win.  Even more useful is bridging the gap between HTML and Flash with a way to create hybrid applications, instead of people seeing them only as two alternatives.  This is a huge win for all web app developers.

Checkbox Range Selection Update

Just a little update to my checkbox range selection jQuery plugin to allow chaining.  I'd forgotten to return 'this' at the end of the function.  Here's the full source, including the mod:

(function($) {
  $.fn.enableCheckboxRangeSelection = function() {
    var lastCheckbox = null;
    var $spec = this;
    $spec.bind("click", function(e) {
      if (lastCheckbox != null && e.shiftKey) {
        $spec.slice(
          Math.min($spec.index(lastCheckbox), $spec.index(e.target)),
          Math.max($spec.index(lastCheckbox), $spec.index(e.target)) + 1
        ).attr({checked: e.target.checked ? "checked" : ""});
      }
      lastCheckbox = e.target;
    });
    return $spec;
  };
})(jQuery);

You can check the project page as well, for full history and updates.

Prototype and jQuery

Since I discovered it a few years ago, I've been a big Prototype fan.  It's simple, and gets the job done with a minimum of fuss.  It's not without warts, of course.  I still occasionally forget to put 'new' in front of Ajax.Request, and some of the Ruby-like methods share their lineage's arcane naming.  When it was new, it was the best thing around, and while it now has competitors, it's certainly not lagging behind.

At work, however, jQuery has been adopted as the standard (and I've no power to change it).  The lack of the $() function is annoying; several times I've debated adding this function (or one of various similar ones) to our library:

function $(id) {
  return jQuery("#" + id)[0];
}

I haven't, of course, as it's not the jQuery way.  jQuery also lacks any sort of class assistance, so we still use the Prototype class framework for our class-based JS.  That seems to work fairly well, except for the fact that we have to use two frameworks where one could suffice.

jQuery is not without it's benefits, of course.  The plugin architecture is a nice aspect that Prototype didn't really offer an equivalent of.  It means the core stays lighter (good), but if you want additional functionality you're stuck managing files from a bunch of different projects (annoying).  Event handling is a bit more straightforward, in some ways.  "Magically" acting on collections of elements with a single call (i.e. no .each(function(o){…}) garbage) definitely makes for more readable code as well.

Because of this shift at work, I've been porting some of my personal apps over to jQuery as well.  I've actually been using a couple jQuery plugins (both self-written and external) for specific tasks for a while now, but not the core framework.  What I've found, however, is that jQuery can be prone to slow code.  To avoid a huge amount of extra work on the part of the JS interpreter, using temporary variables for jQuery objects is essential.  If you do strictly id-based queries, the degradation isn't huge, but if you do CSS-based queries, it can be significant.  With Prototype's focus on id-based queries (at least until $$() came about in 1.5), that was less of an issue.

This need to query a minimum number of times can provide a fair amount of complexity when you have more than a handful of closures hanging about and/or a dynamic DOM.  You end up doing a lot of state management work because you're, in effect, caching DOM lookups and have to ensure you never have stale cache.

Other than that issue and the lack of an equivalent to document.viewport, porting has been relatively painless.  Still very id heavy, so not leveraging jQuery as much as could be, but most of what I'm doing wouldn't benefit from other selectors.

Which one is better?  Hard to say.  jQuery seems to make you work harder to type less code, while Prototype seems to cost you a few more characters for a bit less density.  With the exception of Prototype's class support, their feature sets are fairly equivalent, especially with jQuery UI now available to "compete" with Scriptaculous.  For the moment, I'm choosing to use jQuery on new stuff, but wishing for Prototype every few minutes.  Until I come up against some sort of significant wall, it'll probably stay that way, just to stick with the same tooling professionally and personally.  And over time it'll probably get better as the Prototype-ness fades from apps.

Ajaxian on Prototype vs JQuery

Ajaxian posted a little blurb on benchmarking Prototype and jQuery today. I've been a Prototype guy for years, but at the office we've gone from all-Prototype to all-jQuery, and performance degredation was one of the things I noticed. I never did any actual benchmarking, just went by feel, but it's interesting to see that my perceptions were well founded.

Whether performance of JS libraries should be a huge determinant in picking one to use is up for grabs. Unless the client-side is doing a hell of a lot of work, these days' computers have plenty of CPU hanging about unused.  However, in the past couple months we've spent a lot of time working around JS performance issues at the office. I can't say that using Prototype instead of jQuery would have eliminated the bottlenecks, but clearly performance matters.

JavaScript. At Last.

Working on gpsracr.com this evening, and whipped open my favorite chunk of JS: Prototype. It just gets better all the time. They've added support for automatically executing text that is returned from an Ajax.Request with an appropriate MIME type (like text/javascript), which means you don't even need result handlers. Just dispatch to your URL with whatever parameters you need, and let CF emit back whatever JS should run as a result of the submission (error, failure, success, whatever). Talk about sweet.

Now that might not sound like a big deal, but it really is. I use Fusebox. So all my links are parameterized with XFAs. But XFAs are server-side. Client-side JS (especially static JS in *.js files) can't really access XFAs very well, so URL building is a pain for remoting endpoints and/or hard context switches, But if you're generating the JS server-side….

The specific use case was a login form. I want to "submit" it asynchronously and either display a "denied" message if there's a failure, or forward to the inside of the app if it succeeds. Ordinarily, you'd have to submit the credentials, get some sort of status back from the server, and then use JS to do something with it. Now I can just submit the credentials and let CF emit the JS that I want to execute as a result. I love it.

Improved ComboBox Demo

After Koen (a coworker) pointed out that my online ComboBox demo was rather user-unfriendly (like how you just had to "know" to type an 'a' in the fields), I threw some instructions around it. It's available at /web/widgets/, as before. There's also a new distro at /web/widgets/widgets-b2231.zip, and the project page has been updated as well.  The actual code is unchanged, only the demo file has been updated.

ComboBox Widget Update

I've been making tweaks to my ComboBox widget (first published in July 2005) in various apps of mine that use it, and figured it was time to publish an update. You can download the archive at /web/widgets/widgets-b2218.zip, play with the online demo (which is just the expanded archive) at /web/widgets/, or visit the project page for most info.  I believe I've fixed every bug that people have brought to my attention, but if I missed something, let me know.

New and notable features (in no particular order):

  • multiple delimited values (defaults to comma delimited)
  • the ESC key will now close the dropdown, if it's open
  • the TAB key will select the current value and move focus off the combobox
  • automatic hiding of SELECT, APPLET, and EMBED elements under the dropdown (for IE)
  • automatic setting of autocomplete="off"
  • changed to update only ever 250ms, rather than on every key press, which greatly reduces needless churn with JS remoting-based dropdowns
  • added "Loading …" message while waiting for dropdown values (for JS remoting-based dropdowns)
  • removal of already-selected items from the dropdown (only useful for multi-select)
  • added wrapping with the arrow keys (pressing 'up' when the first item is selected will move the selection to the last item)
  • support for arbitrary HTML in the dropdown values that will be stripped to create the actual form field value

There are a few known issues:

  • if your dropdown is larger than it's allowed side, scrollbars will appear, but the scrolling won't reset when new items are loaded
  • untested on IE aside from a few spot checks. However, except for the hiding of SELECT/APPLET/EMBED elements (which is fixed), I've not received any IE-specific complaints, so I'd say it's pretty sound.
  • documentation is somewhat lacking.

I'm sure I'm missed some, but those are the high points. As always, it's MIT licensed so you can basically do whatever. If you find it useful (or think it's a piece of garbage), I'd love to hear about it.

Ajax (sigh) Pet Peeve

I've finally given up and added 'ajax' to my vocabulary as a synonym for "JS remoting".  But I've got a bone to pick with people building ajax apps: neat UI trickery has to leave the UI in a consistent state or you're making the UI worse instead of better.

The particular example that is continually annoying is WordPress.   The authors have done a good job of integrating ajax bits to speed up the UI, like when you delete a comment, it just fades out of the table instead of reloading.  But what they don't do is update status information (like the number of pending comments) in other parts of the page to reflect that a comment has been deleted.  So you see a link that says there is one pending comment and click it, only to be presented with a page that says there are zero pending comments.  Argggghhhh.  I'd so much rather wait for a page reload on the comment deletion.
Flex wins hands down in this area, because you can do data bindings for all that stuff, and the Flex compiler will build all the event listeners/observers to make sure everything is always updated and therefore consistent.  Building that framework in JS isn't terribly difficult, but it does depend on having raw data structures client-side, rather than using the server to render HTML for injection.