programming and diy
by philip kelly

Quick lists using localStorage and contentEditable

September 20th 2010

screenshot example of quick lists I've been playing around with some newfangled html5 techniques. Specifically localStorage to easily persist data on a client browser, and contentEditable to allow editing of html on the fly. Hop on over to if you want to go straight to the demo.

I ran across this demo on which shows off editing some elements and saving them with localStorage so you can refresh the browser and your changes are still there. This is pretty neat in itself, but I ran with the concept and created a quick client-side task list.


  • Create, edit, remove, and complete items.
  • Add nested lists.
  • Save multiple lists by keying each one with a URL param (ex:
  • Lists have their name set as the page title. Making for nice bookmark-able lists.
  • Persisting list, even after browser is closed.

Under the hood

Each editable field has the contenteditable attribute set.

<h1 contenteditable="true"></h1>

Each keystroke on an editable field will trigger the save_state javascript helper function, which saves everything in the body element. A better solution would be to separate the markup from the data when saving, however the markup would have to be rebuilt with javascript on page load. Not worth the time to implement for a simple demo. The get_list_name function is another helper that checks the URL for a list name. This keeps each separate list separate in the localStorage.

function save_state() {
  localStorage.setItem(get_list_name(), $('body').html());

When the page is loaded, localStorage is checked to see if data has already been saved, in which case it is used to replace the body's inner html.

if (localStorage.getItem(get_list_name())) {
  var saved_html = localStorage.getItem(get_list_name());

jQuery is used to handle all the application logic. For a more detailed look, check out the lists.js file.

Take me to the demo

Runs great in Safari, Firefox, and Chrome. Unfortunately contentEditable doesn't yet work in Mobile Safari. I haven't tested it in Opera or IE.

How to make a simple eye mask

September 13th 2010

cat wearing eye mask Recently I was confronted with the daunting task of sleeping on an airplane. I don't often fly on planes and even less often am I able to fall asleep in sitting positions. To tackle this problem I wanted all the tools I could muster without breaking my wallet on expensive travel gear. One such tool is the eye mask. This quick tutorial will show you how to make an eye mask using only a wash cloth and bit of elastic string. That's right, we're doing it Macgyver style.


  • Bathroom washcloth - approx. 12" by 12"
  • Elastic string - approx. 20" (non-elastic string can be substituted, although the mask may not fit as snugly)


  1. First fold the washcloth diagonally making a triangle.
  2. Next, form a clove hitch on one end of the elastic string and pass one corner of the triangle through it. Check out the short video below to learn how to make a clove hitch.
  3. Finally, make another clove hitch on the other end of the string, attaching it to the other corner of the wash cloth. You can adjust the length of the elastic by feeding the string through the hitch and pulling out the slack.
  4. Slide the mask over your head with the third point of the triangle pointing down. The mask can be quickly assembled on the go and having a wash cloth on hand while traveling can be quite handy for cleaning up spills.

Is that clove hitch kicking your butt? Check out this video for an alternate tying method.

Now if you'll excuse me, I'm going to take a little cat nap.

IKEA Product availability Notifier

July 22nd 2010

example notifo notification on iPhone Update: Notifo is now defunct unfortunately. An alternative notification service could be used with this guide.

Waiting for IKEA to restock that new KIVIK couch you're dying to park your bum on? You could manually check IKEA's website everyday or you could use a web page change notification tool like femtoo. However, the free version will only check every 12 hours. Sounds like a great opportunity to write a small Ruby scraper that runs every 5 mins. Also, I've been looking for an excuse to test out the API of the notification service, Notifo. With their iPhone app a nifty message will pop right up. Now if I could only replace that alert sound with Emeril's Bam...


  • Unix-like system. I'm gonna assume you know how to use the command line. You don't need to be an expert or anything.
  • Ruby
  • Rubygems
  • Git
  • iPhone (Don't have an iPhone? No worries, you can still check your notifications on


First you need to set up a Notifo account. Head over to and set one up. If you've got an iPhone now's the time to install the Notifo app and link it to your newly created account.

Next install the nokogiri gem, We'll need it to parse IKEA's product availability page.

$ gem install nokogiri

There isn't an official API gem from Notifo yet. Jot has a gem on github which I've forked to fix a bug in which you couldn't set the label attribute for notifications. Create a working directory to clone my github repository to, like "ikea-notifo." cd into that dir and type in the following.

$ git clone git://

Now you're working folder should contain a folder called "notifo."

Fire up your favorite text editor and paste in the below code.

    require 'rubygems'
    require 'nokogiri'
    require 'open-uri'
    require 'notifo/lib/notifo.rb'

    search_url = ""

    while true do
      doc = Nokogiri::HTML(open(search_url))

      if doc.css("div.sc_graph_product").inner_text =~ /out of stock/
        puts "No sofas in stock at #{}, better luck next time..."
        puts 'W00t! Sofa in stock!'
        puts "I'll give you the good news with a nifty notification!"

        user = 'chatche'
        label = 'IKEA'
        title = 'Kivik Sofa'
        message = "Good news! The sofa is in stock. Welcome to WooTown, USA pop. you!"
        uri = search_url

        notifocation =,"put your Notifo api key here"), message, title, uri, label)

Let's break this script down a bit:

  • We've got nokogiri required at the top, the standard library open-uri, and notifo.rb that you cloned from github. Next, the IKEA URL is set.
  • An infinite loop is opened up and the html is fetched.
  • A CSS selector is used to find the html element that might contain the "out of stock" message.
  • If the product is still out of stock, a message is printed to the console and the script waits 5 minutes before checking again.
  • Once the product is in stock the notification is created and script ends.

You'll have to change 3 things in this script:

  1. First change search_url to the out of stock page of your desired IKEA product. You can find it by first going to a product page, like this KIVIK sofa one. In the Buy at your local store box on the right, select the closest IKEA and click Check stock availability. The resulting page should look similar to the below screenshot. ikea product out of stock example

    Once a product is in stock the page will look like this.

    ikea product in stock example
  2. Change user to your Notifo username.

  3. In the line that contains "" paste in your Notifo secret API key. You can find it by logging in to and clicking Settings.

Save this file as "ikea.rb" in the working directory we created earlier. That folder should now contain ikea.rb and the notifo folder.

Now you're ready to rock n roll! Well more like execute some ruby code and wait for a notification. But you can still rock your ass off while you do that.

$ ruby ikea.rb

Once your IKEA product is in stock your phone will let you know. Tap the notification in Notifo to go the IKEA URL and see how many are in stock.

When you shutdown your computer the script is of course going to stop running which is why you should run it on a server. If you don't have access to a server you can just leave your computer on. It only took a few days for them to restock my KIVIK sofa.

Pro tip: Make sure all your Notifo information is correct by testing on an IKEA URL that has an in stock product.