Archive for category Usability

The importance of safety in everyday things

There is a reason I love building physical objects with wood. It shares many basic engineering principles of building good software. For example, planning to group similar tasks to enable as much batch processing as possible. There is the 80/20 rule as well. You can document the dimensions and cut lists for repeat builds. Even though every step in a woodworking project is pretty much irreversible, the number of “Oops” moments can be drastically reduced over a period of time, with a decent experience.

But see, a real life product differs from a software in a very critical part of the build process – the safety inspection. Even the worst software bug can’t come close to the damage caused by a physical injury from a nasty oversight. When the foldable table-cum-cabinet I was building got almost finished, I suddenly discovered a potential finger-smashing pinch hazard from what I imagined as a cool design. The table part on the right side is foldable, so that also means nearly 29 inches of leg folds back and it is hinged close to the right end of the table, right where a person might keep the right fingers when unfolding the plank. Now if the legs have no retention mechanism, they will just open with a smash as soon as the clearance is available in the unfolding process. So I need to do two things:

1. Prevention of the hazard: I added a small retention clip so that the legs will be in its place until the user is ready to unhook it.
2. Protection in case #1 above is forgotten: Added a barrier next to the hinge at the underside near the right end of the table top. This will ensure the fingers stay out of the way of the pinch zone.

Without photos, none of my words make any sense, so here they are: http://therider.posterous.com/sewing-machine-cabinet

I still have to explain the mechanisms to my friend when I hand it over to her. I will possibly make a printout to ensure anyone using it follows right procedures.

3 Comments

Context-Aware Bash Shell Autocomplete

My work in the IT field involves a lot of repetitive typing in the unix shell, and most of them are basically copy and paste of things like hostnames, directory paths, screen names and so on. Bash shell by default offers a file/directory name expansion, but it can be extended for command specific autocomplete of specific files or data.

For example, all the host names you have ssh’d to before are stored in a file ~/.ssh/known_hosts, so we can leverage that. Similarly available screen names can be seen using “screen -ls” but it is much nicer to have the list available for expansion while invoking a screen.

So here is a project on github to build an extension script which offers as many useful autocomplete features as possible without interfering with the default filename expansion.

Examples:


Context: screen

$ screen -r [TAB][TAB]
photo debug bashautoc
joyd@shield ~/configs $ screen -r ba[TAB]
joyd@shield ~/configs $ screen -r bashautoc

No Comments

Speeding up Pixelpost blog browsing

Pixelpost is a very popular framework for photoblogs due to its simplicity and ease of tweaking. A while back I posted an article on how to enable keyboard navigation on a pixelpost photoblog. It works very well considering it allows mouse free browsing. But the browsing experience can be improved even more.

Lets consider how a photoblog is browsed most of the time. A visitor typically lands on a home page showing the latest image and goes through the previous images sequentially, spending a few seconds on each image. Leveraging this fact, if the “previous” image is downloaded in the background using javascript right after the current image, it will be almost if not completely ready for viewing when the visitor is on the “previous” image page. The same can be done for the “next” image, but in most of the cases that image will already be in the browser cache.

This is a very simple change for a tangible usability benefit. Below are my tweaks:

index.php:

// expose the previous image name through tags, so previous images can be
// pre-downloaded in the background for faster browsing experience
$tpl = ereg_replace("<IMAGE_PREVIOUS_NAME>",$image_previous_name,$tpl);

templates/theworldin35mm/image_template.html:

window.onload = function() {
  ...
  var prevImg = new Image(); // for downloading prev image in background

  img.onload = function(evt) {
    ...
    // after current image load, load previous image in the background
    if('<IMAGE_PREVIOUS_NAME>' != '') {
      prevImg.src = '<SITE_URL>images/<IMAGE_PREVIOUS_NAME>';
    }
  }
  ...
};

To verify that this extra javascript is actually doing its job, look at the Net section in Firebug (in Firefox browser, ofcourse). Clear the cache first. On first page load it will show several items getting downloaded. On left arrow, the previous image page will load, and then you can see that an extra image got downloaded at the bottom. Compare with and without my code. Here is a screenshot:

Testing pre-download of previous image

,

11 Comments

Pixelpost mod: Edit image link

As my pixelpost photoblog is growing by the day, it is pretty painful to edit info for images that are a little old. In the admin page, go to the images section, then navigate a couple pages to find the image from the small thumbnail then click edit. Too much work for me. A simple tag expansion should solve this.

So I made this new tag and inserted it right after the Admin link in the footer page (my template is theworldin35mm):

<EDIT_IMAGE_LINK_ITEM>

And then the following piece of code in index.php to ensure expansion of the tag only on a image page and when logged in:

  1. if($image_id && isset($_SESSION["pixelpost_admin"])) {
  2.   $edit_image_link_item = "<li><a href=\"<SITE_URL>admin/index.php?view=images&id=$image_id\" title=
  3.     \"Edit Image\">Edit Image</a></li>";
  4.   $tpl = ereg_replace("<edit_image_link_item>",$edit_image_link_item,$tpl);
  5. } else {
  6.    $tpl = ereg_replace("</edit_image_link_item><edit_image_link_item>","<!– Edit image link only for image page when logged in –>",$tpl);
  7. }

The above code goes right after the following block:

  1. if(!isset($_GET[‘x’])) {
  2.    
  3. }

Image management (editing category/tags) is a breeze now.

No Comments

The importance of keyboard navigation

While browsing online portfolios of a few well known photographers I noticed how desperately those fancy designs needed a very basic feature – keyboard navigation. Thanks to the deluge of digital photos everywhere and the tendency of people to upload a gazillion of them after every party or a trip, we no more have the time or patience to point and click the “next” link. Picasa, smugmug and facebook among a few others have already put this most important feature in place, making it a pleasure to browse images quickly. Too bad flickr did not yet, but it is nothing a greasemonkey userscript can’t fix.

Then I wanted this on my own photoblog as well. I thought about using jQuery but since pixelpost uses prototype, I looked up the syntax and quickly came up with this small piece of code:

  1. Event.observe(document, "keypress", function(e) {
  2.   // 37 for left, 39 for right
  3.   var cur = location.href;
  4.   var prev = $$("#image-nav-prevlink a")[0].href;
  5.   var next = $$("#image-nav-nextlink a")[0].href;
  6.   switch(e.keyCode) {
  7.     case 37:
  8.       if(cur != prev) { location.href = prev; }
  9.       break;
  10.     case 39:
  11.       if(cur != next) { location.href = next; }
  12.       break;
  13.   }
  14. });

10 minutes of effort. 10x better usability.


Update: I made a flickr photostream navigator userscript in a similar fashion but had a critical bug. When an input box is focused, arrow keys should not trigger another page load. The same bug is present in the above code as well. Thanks to my good friend Francesco for catching this early. I fixed them both. The above code after correction looks like:

  1. var PB = { Globals:{} };
  2. PB.Globals.inputHasFocus = false;
  3.  
  4. Event.observe(document, "keydown", function(e) {
  5.   // console.log("any input has focus: " + PB.Globals.inputHasFocus);
  6.   if(PB.Globals.inputHasFocus) { return; }
  7.   // 37 for left, 39 for right
  8.   var cur = location.href;
  9.   var prev = $$("#image-nav-prevlink a")[0].href;
  10.   var next = $$("#image-nav-nextlink a")[0].href;
  11.   switch(e.keyCode) {
  12.     case 37:
  13.       // for some reason the inactive link looks like "…showimage="
  14.       if(cur != prev && prev.match("showimage=[0-9]")) { location.href = prev; }
  15.       break;
  16.     case 39:
  17.       if(cur != next && next.match("showimage=[0-9]")) { location.href = next; }
  18.       break;
  19.   }
  20. });
  21.  
  22. Event.observe(window, "load", function() {
  23.   $$("input.input, textarea").each(function(inp){
  24.     inp.observe(‘focus’, function(e){
  25.       // console.log(e);
  26.       PB.Globals.inputHasFocus = true;
  27.     });
  28.     inp.observe(‘blur’, function(e){
  29.       // console.log(e);
  30.       PB.Globals.inputHasFocus = false;
  31.     });
  32.   });
  33. });

1 Comment