Thursday, September 27, 2007

Canvas Loading Indicator

If any of you have been writing Web 2.0 apps for the iPhone, you will have realized by now that it doesn't allow you to use animated gifs. This is a bit of a problem since you want to provide some sort of feedback to the user if you're waiting for some data to load. Normally you would show something like what you find at http://www.ajaxload.info/. The other thing you might know is that you can use the canvas with the iPhone. Clearly the solution to the problem is to use the canvas to draw your indicator. I looked and couldn't find anyone who had built one. So I wrote one. Turns out, if you keep things simple, its relatively easy to write your basic spinner.
function getLoading(context, bars, center, innerRadius, size, color) {
var animating = true,
    currentOffset = 0;

function makeRGBA(){
    return "rgba(" + [].slice.call(arguments, 0).join(",") + ")";
}
function drawBlock(ctx, barNo){
    ctx.fillStyle = makeRGBA(color.red, color.green, color.blue, (bars+1-barNo)/(bars+1));
    ctx.fillRect(-size.width/2, 0, size.width, size.height);
}
function calculateAngle(barNo){
    return 2 * barNo * Math.PI / bars;
}
function calculatePosition(barNo){
    angle = calculateAngle(barNo);
    return {
        y: (innerRadius * Math.cos(-angle)),
        x: (innerRadius * Math.sin(-angle)),
        angle: angle
    };
}
function draw(ctx, offset) {
    clearFrame(ctx);
    ctx.save();
    ctx.translate(center.x, center.y);
    for(var i = 0; i<bars; i++){
        var curbar = (offset+i) % bars,
            pos = calculatePosition(curbar);
        ctx.save();
        ctx.translate(pos.x, pos.y);
        ctx.rotate(pos.angle);
        drawBlock(context, i);
        ctx.restore();
    }
    ctx.restore();
}
function clearFrame(ctx) {
    ctx.clearRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);
}
function nextAnimation(){
    if (!animating) {
        return;
    };
    currentOffset = (currentOffset + 1) % bars;
    draw(context, currentOffset);
    setTimeout(nextAnimation, 50);
}
nextAnimation(0);
return {
    stop: function (){
        animating = false;
        clearFrame(context);
    },
    start: function (){
        animating = true;
        nextAnimation(0);
    }
};
}
This is a fair chunk of code but its not entirely clear how you use it. Its actually quite simple. Assuming you have a canvas to work with, All you do is call
var controller = getLoading(canvas.getContext("2d"), 9, {x:100, y:100}, 10, {width: 2, height:10}, {red: 0, green: 17, blue: 58});
The getLoading function takes a 2d context, the number of bars that you want your indicator to use, the X and Y coordinates of the center of the indicator, the radius of the inner portion of the indicator the height and width of each of the bars of the indicator and, finally, the color you want to use for the indicator. We ask for the components of the color separately because each spoke of the indicator gets a progressively smaller alpha value. The function returns a simple object with two methods, start() and stop(). The indicator is created spinning. If you wanted a more fancy set of spokes, all you would really have to do is rewrite the drawBlock function. Blogger seems to strip out script tags so there's no real way to provide a test (expect maybe a honkin huge bookmarklet) so if you want to try it out, copy the script and he following:
function (){
  var canvas = document.createElement("canvas");
  canvas.width= 200;
  canvas.height = 200;
  canvas.style.cssText="position:absolute; top:100px; left:100px; background:#transparent; border: 3px solid red";
  document.body.appendChild(canvas);
  var controller = getLoading(canvas.getContext("2d"), 9, {x:100, y:100}, 10, {width: 2, height:10}, {red: 0, green: 17, blue: 58});

  var button1 = document.createElement("input");
  button1.value="stop";
  button1.type="button";
  button1.onclick = function (){
      controller.stop();
  };
  document.body.appendChild(button1);

  var button2 = document.createElement("input");
  button2.value="start";
  button2.type = "button";
  button2.onclick = function (){
      controller.start();
  };
  document.body.appendChild(button2);
})();
in firebug and it should be fine.

Thursday, August 23, 2007

Curried What Now?

With the release of Prototype 1.6.0 release candidate, we now have a new way to do things. We can now write curried functions.

Show of hands, how many of you know what a curried function is? I thought so. I first encountered currying about a year ago via Doug Crockfords site which brought me to Svend Tofte’s article on it. Perhaps its because I’m stark raving mad, but I immediately fell in love with the technique and tried to find ways to use it. 

I've shown the article to others but few have really seen the potential as I have. I think its hard to get your head around it without examples. 

The canonical example

It seems that every description of curried functions starts with something like the following, which Sam Stephenson used to announce the release of Prototype 1.6.0 RC1:

function sum(a, b) {
return a + b;
}
sum(10, 5) // 15
var addTen = sum.curry(10);
addTen(5)  // 15

The explanation is that you can write some function, and call it with some of the arguments defined, and then call it later with the rest of the arguments. That is all well and good but how on earth would you want to use it in a real situation?

Real uses for curry

Curried functions are all about not repeating yourself more than you have to. Don’t think of it as calling some function many times with some of the same arguments. Think of it as creating a specific function out of a more general function. That is probably not any more enlightening so a less trivial example might be illustrative.

Alerting buttons

This is probably not a much more complex example, but I think it starts providing an explanation. What we are going to do is have a page with 5 buttons. When the button is clicked, we are going to alert a number which is randomly assigned to that button (each button will have 1 random number assigned to it). We might right this as follows:

function alertMessageEventHandler(evt){
alert(this._message);
}

$$("#ButtonList button").each(function (button){
button._message = "Your random number is:\n" + Math.round(100 * Math.random());
button.observe("click", alertMessageEventHandler);
});

This is going to work just fine but lets see how we can rewrite it using curry:

function alertMessageEventHandler(msg, evt){
alert("curried" + msg);
}

$$("#ButtonList button").each(function (button){
var message = "Your random number is:\n" + Math.round(100 * Math.random());
button.observe("click", alertMessageEventHandler.curry(message));
});

This is obviously almost as trivial as our sum function above but it gets the point across. 

Validation

While its all well and good to provide trivial example, sometimes its more illustrative if we have a robust example. So lets see what we can come up with. 

Imagine that we want to build a validation system for forms. We know that we want to define, for any given field, a series of tests to which a value is subjected and we want to get back a list of the tests that failed so that we can display error messages. 

Lets start with a simple validation function. It will take the value to be tested and an array of testing objects. It will return an array of failed testing objects (which would be further used to produce the error message). 

function isValid (value, tests) {
return tests.reject(function (aTest){
return aTest.test(value);
});
}

This is a really small function but I think we can all see what it does. We loop through each element in tests and call its "test" function passing in the value to test in. If the test function returns true, then we know that the value passed that test and we boot it out of our array. We can use our new function by creating an array of tests:

function isLessThanSeven(val) {
return val <> 2;
}
function isMultiplOfThree(val) {
return (val % 3) == 0;
}
var test1 = [
{test: isLessThanSeven, errorMessage: "Must be less than 7"},
{test: isGreaterThanTwo, errorMessage: "Must be greater than 2"},
{test: isMultiplOfThree, errorMessage: "Must be a multiple of 3"}
];
console.debug("3:" + isValid(3, test1).length)
console.debug("10:" + isValid(10, test1).length)

This is a rather simple test. In the first test we should pass all the tests (thus have a length of 0) and in the second we should fail 2 test. 

The problem with this sort of thing is that we end up repeating ourselves with all our little test functions. This is where curry comes in. If we get a little more generic we can reuse some code easily:

function isLessThan(comparison, val){
return (val <> comparison );
}
function isMultiple(divisor, val){
return (val % divisor) == 0;
}
var test2 = [
{test: isLessThan.curry(7), errorMessage: "Must be less than 7"},
{test: isGreaterThan.curry(2), errorMessage: "Must be greater than 2"},
{test: isMultiple.curry(3), errorMessage: "Must be a multiple of 3"}
];

console.debug("3:" + isValid(3, test2).length)
console.debug("10:" + isValid(10, test2).length)

We can now reuse our isLessThan and other functions in many places and never repeat ourselves.

Coding

The question that remains is where and when to use curried functions. There are a few things to look for. The first is a function that you are calling frequently with the similar collections of arguments. In this case, set things up so that you curry the function with your common arguments and save the result and call it later (like in the canonical example). The second is somewhat simpler. Look for situations where you are storing some value on an element so that you can use it in an event handler (like in the alerting buttons example). Finally (and this is the hard one) you need to find combinations of functions that are fundamentally similar and consolidate them into functions that you curry as needed. 

The easiest way to write a function for currying is to reverse the order of your arguments. Its counter intuitive but if you think about, we typically put the most important arguments first. Often those are also the ones that are most likely to change between calls. So if we put it at the end then we can more easily use curry. 

In fact if we reorder the arguments of our isValid function, we can curry it and simplify things. 


function isValidCurry (tests, value) {
//we assume that tests is an array of objects which look like:
//{
//    test: function (value){return true;},
//    error: 120
//}
return tests.reject(function (aTest){
   return aTest.test(value);
});
}

var test3 = [
{test: isLessThan.curry(7), errorMessage: "Must be less than 7"},
{test: isGreaterThan.curry(2), errorMessage: "Must be greater than 2"},
{test: isMultiple.curry(3), errorMessage: "Must be a multiple of 3"}
];

var validateTestThree = isValidCurry.curry(test3);
console.debug("3:" + validateTestThree(3).length);
console.debug("10:" + validateTestThree(10).length);

So how do you like your curry?