And Again!

Another update to FlexChart this evening, providing set and series colors (both fills and strokes), an option for including/excluding the legend when exporting a chart to PNG, and a few new examples showcasing the features (including some really ugly developer art).

With the coloring support, the 'Grouped Series' example makes a lot more visual sense, so if you thought WTF yesterday, go look at it again.  ;)

More FlexCharts Goodness

Another batch of changes to FlexChart.

First is grouped series, which is hard to explain, but easy to understand.  Go hit the demo and select "Grouped Series" from the dropdown.  The chart layout has always been possible, but the legend just displayed a flat series list with no awareness of the groupings.  To see the old behaviour, change the 'legendStyle' attribute of the chart tag to "simple", and hit "Update Chart".

Second is direct support for PNG exports of the chart.  A Base64-encoded PNG can be requested via an ExternalInterface callback which you can then do whatever you want with.  The demo app just sends it server side (in a new window) for deserialization with CFIMAGE and immediately sends it back to the client.  Hardly interesting, but definitely illustrative.

Finally, I've abandoned the compile-in-page model, Flex SDK 2.0.1, and a single file architecture completely.  You can drop the binary anywhere you want without any dependency on FlexBuilder, a Flex SDK, or any server component, which is nice.  Of course, if you want to customize, it required FlexBuilder 3.  I think that's the right tradeoff.  I've also started splitting up the engine for easier maintenance, now that there's no need to keep it single file (which there was when it was server-compiled).  I haven't done much, but that'll come.  Definitely make it easier to work with.

Soon to come down the pipe are custom colors for series and sets, support for multiple y-axes, and a grid legend style which is basically a headerless datagrid under the chart with a row per series and a column per x-axis item, all lined up evenly.

FlexChart Update

Long time no blog…  I've updated my FlexChart component slightly, as well as repackaged it for easier consumption.  The new feature is the availability of a 'dataTipFunction' attribute on the root 'chart' element, which will be called to format data tips.  It gets passed an object with various keys about the backing chart item.  Since that's all it gets (rather than a HitData instance), it's not as powerful as the native Flex capabilities, but it gets the job done with pure JS.

The repackaging was to a binary (precompiled) format, rather than requiring the server compilation I'd originally built it with.  The old style is still available where it always has been, but the preferred usage is now either the distribution directory (svn) or the FlexBuilder project itself.  With the new model, you just drop the compiled binaries (yours or mine) into any web-accessible directory and use them, regardless of whether you have LCDS installed to do the compilation.

Checkbox Range Selection (a la GMail)

If any of you use GMail, you'll know that you can shift click the checkboxes on the conversation list to select a range of conversations (i.e. click the second conversation's checkbox and then shift-click the tenth conversation's checkbox). You can also deselect the same way (click the seventh, and then shift-click the fourth). Finally, you can shift-click a second time (third click total) to extend the range. I wanted that functionality in one of my apps, and here it is.

Update: I've repackaged the code as a jQuery plugin based on Dan Switzer's suggestion. I've left the original code in place, just struck it out.

Update: I've added namespacing of the event handler, and support for the meta key (Command on Mac) as well as shift based on Henrik's comments below.

function configureCheckboxesForRangeSelection(spec) {
  var lastCheckbox = null;
  jQuery(function($) { // for Prototype protection
    var $spec = $(spec);
    $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;
    });
  }); // for Prototype protection
};

(function($) {
  $.fn.enableCheckboxRangeSelection = function() {
    var lastCheckbox = null;
    var $spec = this;
    $spec.unbind("click.checkboxrange");
    $spec.bind("click.checkboxrange", function(e) {
      if (lastCheckbox != null && (e.shiftKey || e.metaKey)) {
        $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;
    });
  };
})(jQuery);

It requires jQuery to be available (I used 1.2.1 and 1.2.3), and is safe to use with Prototype also in-scope (regardless of which owns the $ function). Call that function passing in a jQuery expression (NOT a jQuery object) that describes the checkboxes you want to be range-selectable:

configureCheckboxesForRangeSelection("input.image-checkbox");
$("input.image-checkbox").enableCheckboxRangeSelection();

Thanks to Matt Wood (a coworker) for the slice/index suggestion. My initial implementation had used an each with a conditional and a status variable – definitely less elegant.  Check the project page for any additional updates.

Report/Query DSLs Update

I've posted a simple demo app for both DSLs that you can play with. It's included in the distribution as index.cfm, so you'll get it if you pull down the source from SVN. I've also created a readme.txt file in the distribution with the text of my intro blog post.

I also made a very minor (though backwards incompatible) update to my Query DSL implementation. There is a convertToSimpleCriterion method, designed to be overridden by subclasses if necessary. The initial implementation contained the logic for processing negated terms (those prefixed with '-' or '!'), and for splitting terms into predicate/value pairs. Since that's part of the DSL implementation's job, that needed to b done by the internal methods, so the behaviour would be inherited by any subclasses. I've changed the convertToSimpleCriterion method to be passed all three parts individually, so the parsing can live inside the core of the implementation.

Report/Query DSLs

I use a pair of stacked DSLs (Domain Specific Languages) for searching, reporting, and goal management in a couple applications. A discussion at work (with Joshua and Koen) provided the solution for the final piece I wanted to implement before releasing them. The first layer is for parsing query strings, and the latter for using query strings to build up report structures and such.

For the impatient, code is available at https://ssl.barneyb.com/svn/barneyb/reporting_dsl/trunk/. You want to read the docs in querydsl.cfc and reportdsl.cfc.

The Query DSL is quite simple:

  • spaces delimit terms
  • terms are ANDed together
  • terms can be quoted if they contain spaces
  • terms can contain a prefix (predicate) separated from the value by a colon
  • a leading dash (or exclamation point) can be used to negate a term
  • the OR keyword (caps mandatory) can be used to create alternatives
  • parentheses can be used to group terms

Here's some examples:

  • cat type:pet
  • cat -manx
  • cat OR dog
  • cat (-manx type:"house pet") OR dog

The last item is not possible with Google Search constructs, and is the only construct available that Google doesn't provide. Which isn't to suggest that the DSL supports the gamut of features Google supports.

The result of using the DSL parser is a graph of 'criterion' instances. For example 'andcriterion', 'orcriterion', or 'simplecriterion'. The second example above would return an 'andcriterion' instance with it's left side pointing to a 'simplecriterion' instance for "cat", and the right side pointing to another (negated) 'simplecriterion' instance for "manx". More complex expression result in more complex graphs, but they graph is always singly rooted.

The Report DSL is a bit more complex, and leverages the Query DSL. It is line oriented, with a single-character command specifier at the beginning of each line. A document contains one or more "condition" lines (prefixed with a '+' or an '&') which are comprised of a value and a query string. A document may also have one or more "globals" lines (prefixed with a '+') that only contain a query string, and which is added to each condition's query string (to reduce duplication). Here's a sample document:

+ tag:dining -tag:home
? McDonalds : tag:mcdonalds
? Jack in the Box : tag:"jack in the box"
? Cheesecake Factory : tag:"cheesecake factory"
& Fast Food : tag:"fast food"
& Total : *

There is no real difference between the '?' and '&' lines (referred to as conditions and aggregates respectively), the two variants are there to provide a built-in differentiator. Here I'm using them as their names imply: the conditions are raw conditions, and the aggregates aggregate the conditions together. If this report document were used to generate a chart, you might see the conditions as column series and the aggregates as line series.

The result of parsing a document is a structure with 'coreCriterion', 'conditions' and 'aggregates' keys, all of which are optional if the document doesn't specify anything to fill them. The former holds a criterion graph (since it's just a query string). The latter two are arrays of structs, where each struct contains 'value', 'query', and 'criterion' keys. The first two are simply the two halves of the corresponding line of the document, and the 'criterion' key holds the parsed query string.

So what is this good for? By itself, not much, but with a method for converting between a criterion graph and a SQL expression, the Query DSL will give you an easy way to build very granular search functionality on your site. Once you have that, leveraging the Report DSL allows creation of fairly complex reports with a minimum of fuss (do convert each condition's criterion to SQL, run your query, and feed it into a chart series). Of course, the output needn't be a chart, but that's a good example.

What's most important here is that both of these DSLs are for users to use, not coders. I.e. strings/documents are created by users to customize the behaviour of your application, allowing them to build very specific reports very easily. And the definitions for those reports are simple strings, which makes persisting them a breeze.

With a little more creativity, you can get other behaviour. I alluded to goal management up top. If you were to take the report document above, remove the aggregates (the '&' lines), and replace the values (restaurant names) with integers, according to how desirable they are, you'd have a way to rank given dining adventures. Write yourslef a method for converting the parsed document into a SQL CASE..END statement (leveraging your criterion-to-SQL method, of course), you have a really easy way to assign a "points" value to the items you're querying. Wrap that with some sort of structure for managing a points-per-time-period structure, and you have a very flexible and easy to use goal tracking system.

Like so much else, I don't know how useful this is for others. The Query DSL probably is, but the Report DSL might not be. But it's made my life much easier, so worth the effort I've put into it. Code is only available via Subversion (browser or SVN client) at the URL above. All paths are relative, so you can svn:externals (use a revision number!) it into your existing package-space and reference the CFCs natively. There are docs and more examples in the files as well.

My Blog, Or Is It?

I wanted to thank everyone who read, and especially commented on, my My Blog, My Rules post a couple weeks ago.  It's nice to know that people care, and even better to know that they appreciate what I've done.  And apologies to Charlie for the firestorm.

So is this my blog?  Or is it everyone's site, and I'm the only one that can directly start new conversations?  It's public and the comment form is available to all, so the latter seems an apt description.  Weblog implies a log of something on the web, not necessarily any sort of community foundation, but that's really what a blog is.  I don't know how I feel about that.

I want an engaged audience, I want vibrant discussions, but I don't want an obligation.  But it seems unfair to want that pairing, because what's in it for that audience?

Thanks for being part of that audience, even the silent ones.

Programming Creativity

Creativity is an interesting characteristic, because it means so many things.  Bach was creative.  So was Picasso.  Da Vinci.  Shakespeare.  Thompson and Ritchie?

Is computer programming a creative endeavour?  My instinct says yes.

Merriam-Webster defines "creative" as "having the quality of something created rather than imitated".  So by definition, everything that is created is the result of a creative act.  But that is an overly sterile perspective, I think.  If I said the word "the", I would have created sound, but it surely wasn't creative.

Creativity, to me, means that you can create appealing things out of basic building blocks.  Painters use colors, composers use notes.  Everyone has access to the same colors and notes, but not everyone can create the same paintings or compositions.  Moreover, the appreciation of the whole should be at least partially divorced from those basic blocks.  When you look at a painting, you don't say "well, right here it's red X, but over here it's red Y".  Similarly, music can be appreciated without knowing what specific notes are played, you can transpose a piece up or down as long as you maintain the relationships.

I don't claim that that's necessarily a better definition.  It's certainly a more subjective one, but that's intentional.  Creativity is more than just the ability to create.  The characteristics of what's created weigh in.

So back to programming.  It certainly has the characteristic that complete programs are created out of basic building blocks (your language's syntactic constructs).  But is the result appealing, and can it be appreciated distinct from the basic blocks?  Here I'm a bit hard pressed to find an answer.  The user of a program certainly doesn't appreciate the programming, just the behaviour of the running program.  Is there any way to "view" a program outside it's syntactic constructs?  UML modeling (or your choice of comparable things) is one way, but that's not really the program anymore, it's a distinct and incomplete representation of the program.  Record the live symphony on a handheld voice recorder and play it back – not quite the same.

There is a visual micro-perspective on code.  Someone with no language familiarity (or programming familiarity, really) can certainly distinguish between chaotically formatted code and well formatted code (consistent whitespace, capitalization, etc.).  But that's not really the beauty of the program, that trait belongs to an arbitrary collection of characters, regardless of whether it's a program or not.

Now contrast my definition with that of an assembly line worker.  The worker certainly uses basic building blocks to create an aggregate that can be appreciated distinct from the individual pieces.  But he doesn't get to make the decisions for where stuff goes and how it fits together.  So is he really creating?  No, just building.

So I think creativity also requires some aspect of free will (within the constraints imposed by the medium, of course).  This would manifest itself by the ability to intentionally create non-appealing results, but choosing not to unless using it as a form of artistic expression.  Which brings up another aspect of creativity: expression.  When you write a program, are you expressing something beyond the program itself?

Programming definitely has the free will aspect, just ask anyone who has ever had to debug a mess of spaghetti code.  But I'm not so sure about the expression.  But I think that goes back to the ability to view a program outside it's syntactic constructs.  Is it possible to appreciate the creativity of a program directly, or is some sort of intermediate form required?  If you can't appreciate the creativity of a program, does that mean it's not creative, or just that it can't be proven created?

Music…

I have musical talent of a very mediocre degree, but I love listening.  Popular, rock, choral, new age, instrumental, anything that's interesting.  But what makes it interesting?  What about music evokes feelings?  Why are some collections of noises beautiful while some are appalling, even if they're made up of the same constituents?  A very perplexing question.

The basics of music are easy to explain: it's all simple rhythm counting and note counting.  Arithmetic at it's simplest.  But computer's can't compose music with any sort of competence.  If provided with examples, they can produce new "compositions" that are somehow derived from the examples, but it's not really new.  It shouldn't be that hard to follow the rules and make something original that isn't just noise, but computers can't do it.  I don't claim that I can do it either, but entities of my class (i.e. other people) can.

One thing that's interesting to me about music is how differently Heather and I experience it.  She knows the the lyrics to every song on the radio (except for KUFO, the local rock station), every Christmas song, and a host of others.  On the flip side, I have a relatively static collection of digital music on my workstation at the office, some of it dating back 10 years, and there are a pile of songs I can't sing along with, because I don't know the words.

Heather sings in a community choir as well as a church choir, and with above average competence.  She's not going to be touring the world and making millions of dollars, but for a stay at home mom with a pair of kids under four, it's hard to find fault.  She can't read music, she can only copy what she hears others sing.  She can't pick stuff out on a piano.  She can't count a rhythm.  All of those, you'd think, would be prerequisites for performing music as an adult.

I've got exactly the complementary skillset, though with generally less proficiency.  I can read music.  I can't play the piano but I can get a song across with one.  I can count a rhythm.  I understand the basics of music theory.

The conclusion I've reached is that it's all due to my ears (and/or my brain's aural processing centers).  I have great difficulty listening to spoken words.  I get by in daily life by augmenting speech with primitive lip reading.  This is why I rarely look you in the eye when I'm listening – too busy looking at your mouth.  I hate using the phone, because there aren't any lips to see.  As you might imagine, this probably contributes to me not knowing the lyrics of a wide array of songs I'm intimately familiar with.

But the non-verbal aspects of music (including the 'shape" of sung parts) ……. Heaven lives there.  The expressiveness the four weapons of volume, tempo, rhythm, and harmony can assault your ears with is breathtaking.  If you've got Incubus' Warning available to you, give it a listen, paying close attention to the baseline.  There's a little riff in there (just before the first chorus, I believe) that I could listen to over and over again.

To hear a piece of music slowly build it's richness, growing more and more full, voices weaving together in ever more intricate acrobatics, until it they finally all align themselves and the full force of the melody is heard…..  What I would give to be able to create something that evocative.

Tokamak is Offline

I've had a Linux workstation (named tokamak) as my primary computer for the past several years, and until this past year, work was exclusively Linux as well.  Tonight, for the first time in literally years, I took tokamak offline.  It'll come back on, rest assured, but it's off, and it's weird.

I got a new Thinkpad last month, and set up all my development environments on it using VMWare to virtualize Linux servers (as that's what all my production boxes are).  I knew it'd work before I did it, but I'm amazed at how smoothly it goes.  I use the host OS for everything, and just map shared drives, so all my code is executed strictly in a Linux environment.  I.e. my only Eclipse install is in Windows, and my only Apache/CF/MySQL instances are Linux.  Just use Apache to assemble my url-space out of the '/mnt/hgfs/workspace' directory (a link to my Eclipse workspace), and done.  I can have any set of servers online at a time, and manage all the deployed code on them from a single Eclipse workspace, even running the exact same copy of a given project on multiple VMs (for different CF versions, for example) concurrently.