November 30, 2011

Improved Solari Board

Recently I received some interest in the virtual Solari Board project, which was the incentive I needed to take another look at it. The result is both simplified and improved:

  • Rows load sequentially now, and the end result is a much smoother animation as the number of elements changing at any one time is far fewer than before.
  • Added support for multiple different display element "drums", including letters only, numbers only, and images as well as the full set.
  • The status lights are now set as part of the same rendering process which handles all the other display elements. So no special scripting needed, and they change in concert with their row.
  • Removed a ton of extraneous code and refactored the data source to reflect a more common JSON format.
  • Updated to jQuery 1.7.1
As part of this refactor I removed most of the code associated with the other chart types. I think the right thing to do here is to add them back in a more modular way (require.js?). That'll probably be the next step on this project.

Here's the live demo and project on github.

    [edit - changed github repo]

    November 15, 2011

    An MVC approach to the Legislators App

    Recently, a couple of people pointed out to me that the architecture I was trying to explore in UI-RPC might lend itself to a more MVC approach. Since the Legislators app I created there makes a good "hello world" for testing javascript architectures, I'll use it again for this project.

    Lately the Backbone.js project is getting a lot of traction as a neat way to build an MVC javascript architecture. It provides models, views, collections, and routers (REST URI mappings) and ties them all together with object and event bindings. It also uses Underscore.js, which among other things has a built-in templating function. Since dynamically creating markup was one of the things I need to solve for UI-RPC, it makes sense to give it a test drive here.

    First things first:

    Code: https://github.com/baspete/backbone
    Live: http://dev.basdesign.com/backbone/

    The guts of the app are in legislators.js. The app is surprisingly small, consisting of a Model and View for each individual legislator, a Collection of legislators, and a Model and View for the filter widget.

    In this case, even though the models are the most fundamental objects here, they're not very interesting.

    var Legislator = Backbone.Model.extend({
      initialize: function(){
        console.log("Created Legislator",this.get("legislator"));
      }
    }); 
    

    Models fundamentally exist to hold the data model (duh) for an object. They can be bound to Views, and the magic of backbone is that changes to a Model will automatically update its View.

    Views are responsible for actually making something happen on the screen, and in this case what they mainly do is pass a JSON object to underscore.js for rendering:

    var LegislatorView = Backbone.View.extend({
      template: _.template($('#legislator_template').html()),
      initialize: function(){
        this.render();
      },
      render: function() {
        var l = this.model.toJSON().legislator;
        $("#legislators").append(this.template(l));
        return this;
      }
    });
    

    Models, Views, Collections and Routers in backbone are constructors. In our example, a new legislator is created by instantiating its View and binding it to a Model, like so:

    var view = new LegislatorView({
      model: Legislator
    });
    

    A Collection is a list of things, in our case a View for each legislator:

    var LegislatorList = Backbone.Collection.extend({
      url: sunlightBaseUrl,
      initialize: function(){
        this.fetch({
          success: function(collection) {
            collection.each(function(Legislator) {
              var view = new LegislatorView({
                model: Legislator
              });
            });
           }
        })
      },
      sync: function(method, model, options){  
        options.cache    = true; // sunlightlabs needs this to return jsonp
        options.jsonp    = "jsonp"; // sunlightlabs needs this to return jsonp
        options.dataType = "jsonp";  // by tell backbone.js to use jsonp
        return Backbone.sync(method, model, options);  
      },
      parse: function(response){
        return(response.response.legislators); // just return the array, not the whole object
      }
    });
    

    The first issue I ran into was the same one raised in UI-RPC: how do you bind an action in one view to a different view or collection? I cheated:

    var Filter = Backbone.Model.extend({
      change: function(){
        $("#legislators").empty();
        var params = this.toJSON();
        // sending empty query params breaks 
        for(var i in params){
          if(params[i] === "undefined" || params[i] === ""){
            delete params[i];
          }
        }
        // This is ghetto. How to bind here?
        legislators.url = sunlightBaseUrl + $.param(params);
        legislators.initialize();
      }
    });
    

    "legislators" is an instance of the Legislators collection, and one thing I'm certain of is that a Model shouldn't know anything about instances. Interestingly, when I researched this online I found a bunch of posts talking about using a controller to handle this event mapping. Hmmm... that's the same problem I ran into in UI-RPC.

    At any rate, even though this is an incomplete version of the Legislators app, with only one data source (the sunlight labs API), it's an interesting start. I'll get it to the same level of functionality that UI-RPC is currently, then start exploring the two architectures in parallel.

    New Editor

    Over the years, the progression of editors I've used as my primary tool has gone something like:
    • 1984 - vi
    • 1993 - TextPad
    • 1998 - BBEdit
    • 2008 - TextMate
    The other day I tried Sublime Text 2 and it only took a few minutes to decide I had found my New Very Best Friend.

    Among other great features:
    • Great UI out of the gate. I like the 10,000 ft view.
    • Multi-select. This is so cool:
      • Splat-click on as many things as you want and they're all selected and change together as you type.
      • Select a block of lines (splat-shift-l) and they're edited together.
      • With the cursor on a word, hitting splat-d selects additional occurrences of that word. Or, just hit ctrl-splat-g to select all occurrences of that word.
    • Predictive text (both inline using <tab> or in a command palette accessed by hitting shift-splat-p
    • Lightning-fast file switching/opening. Hit splat-p and start typing the name of a file. As you type, the list narrows, but all those files are actually *open*. It's hard to describe how well this works.
    • vi(m) command mode! Hit escape when editing and all those vi commands you have memorized will work.
    • Smart indenting with alignment marks. The thing that pissed me off most about TextMate was the braindead way it did indenting--if a line was half-selected it wouldn't follow any other lines you were indenting/outdenting. Sublime Text fixes this, and with the addition of the "Alignment" plugin makes it easy to align things like equal signs on multiple lines.
    Sublime Edit has an active development community and a ton of plugins for all the things you'd expect, like zen coding, GitHub integration, etc. One really cool thing about the plugin package management (which works kind of like apt-get) is that the packages are actually checked out using Git, and they're automatically updated every time you start the app.

    I really like this editor.

    November 4, 2011

    Virtual Solari Board

    I've always been intrigued by those split-flap displays you used to see clattering away in train stations and airports. Not only are they fun to watch, but I think they really symbolize the peak of 20th century electro-mechanical design and engineering. The Italian company Solari di Udine made tons of them, so they're often called Solari Boards.

    The principle on which they work is relatively straightforward. Here's a schematic of a single element:


    The display element is advanced by a stepper motor, which in turn receives pulses from a controller (or the controller may be built in) and each pulse advances the display one character. The element may contain letters, numbers, punctuation, pictures, entire words, or any combination thereof. This type of system appeals to me because it translates well into software, so a while ago I wrote a javascript application to create a virtual Solari Board. I'm bringing it up here because it's probably time to take another look at it.

    The wheel can be represented as an array, with the characters on the flaps as elements in that array, like so:

    Drum: function() {
      this.startPosition = [' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9','.',',','?','!','/','\'','+','-','↑','↓'];
    }
    

    Changing the display to a different value is a matter of rotate()ing the array as needed, like so:

    change: function() {
      var container = arguments[0],
        c = arguments[1], // the new character
        index, i, j;
      // get the curent order of the display element's drum
      var values = container.data("order");
      // how many times do we need to increment the drum?
      index = values.indexOf(c);
      // increment the drum
      for(i=0;i<index;i++) {
        sf.chart.splitFlap.show(container, (values[i + 1]));
      }
      // rotate the dom element's stored array to the new order for next time
      container.data("order", values.rotate(index));
      // change the number in the markup
      // container.text(c);
    }
    

    Note the use of jQuery's .data() API for storing the new order of the drum array. I'm a fan of storing state information this way, particularly when it comes to UI elements--it's a perfect use of the feature. Certainly you can get into trouble if you go overboard and try and store non-ui state stuff, but it works well in this case.

    This app is based on the groundwork I did at CMSG for our charting stuff (and the complexity of the javascript reflects that broader context) using $.getJSON() to grab data in the background and load it dynamically. Looking over this project, I really like some of the ideas I used:

    • A single method for rendering multiple chart types (split flap is considered a chart type)
    • A common data format, with specs for time-series, category and other data types
    • A plug-in architecture for different chart types

    I think this project is worth revisiting. There's good work here.

    Live demo: http://dev.basdesign.com/split-flap/activity_tracker.html

    Github repo: https://github.com/baspete/Split-Flap

    [Edit: linked to Solari di Udine]
    [edit - changed github repo]