Wave Notifications on the iPhone

Wave Notifications on the iPhone

I have some news about the Notifiy robot (wave-email-notifications@googlewave.com). It can now send notifications to your iPhone when a wave is updated. The application will be available at the App Store very soon.

I just created a google wave robot that sends email notifications when a wave is updated. To try it out just add: wave-email-notifications@appspot.com to the wave and that’s it. It’s currently in a beta stage so there are no guarantees. Visit the project home page at:

http://wave-email-notifications.googlecode.com/

I found myself needing to implement a tri-state checkbox in javascript. A checkbox has initially 2 states: on or off. From the html perspective if the checkbox is checked then the value is submitted, if it is not then the value is not submitted. From the javascript perspective you can use:

if (checkbox.checked) {
    alert("This checkbox is checked");
}

Some times a true or false is not enough. Other development platforms have tri-state checkboxes that can have either a true, false or null value. The following snippet adds a “greyable” functionality to checkboxes:

Element.addMethods("INPUT", (function() {
    var changeHandler = function(e) {
        if (this.greyed || this.checked) {
            this.setGreyed(!this.greyed);
            if (!this.greyed) {
                this.checked = true;
            }
        }
    };

    function setGreyed(checkbox, greyed) {
        checkbox.greyed = typeof greyed == "undefined" ? true : !!greyed;
        checkbox.setOpacity(checkbox.greyed ? 0.33 : 1);
        if (checkbox.greyed) {
            checkbox.checked = checkbox.greyedState;
        }            
    }

    function setGreyable(element, enable) {
        if (!(element = $(element))) return;
       
        if (!(/checkbox|radiobutton/i.test(element.type))) {
            return;
        }

        if (typeof element.greyedState == "undefined" || enable) {
            element.observe("change", changeHandler);
            element.setGreyState(false);
            element.setGreyed(false);
        } else {
            element.stopObserving("change", changeHandler);
            element.setGreyed(false);
            element.checked = element.greyedState;
            element.greyedState = undefined;
            element.greyed = undefined;
        }
    }

    function setGreyState(element, greyedState) {
        if (!(element = $(element))) return;
       
        if (!(/checkbox|radiobutton/i.test(element.type))) {
            return;
        }

        element.greyedState = typeof greyedState == "undefined" ? true :
            !!greyedState;
    }

    return {
        setGreyed: setGreyed,
        setGreyable: setGreyable,
        setGreyState: setGreyState
    }
})());

This currently requires prototypejs but could be easily modified to not depend on it. To enable it on some element do something like this:

checkbox.setGreyable();

To check if it is greyed or not:

if (checkbox.greyed) {
    alert("This checkbox is greyed");
}

You can also change the state of the checkbox when it is greyed. To set this use the following:

checkbox.setGreyState(true);

The parameter indicates whether the checkbox is checked when grey. Clicking on the checkbox (or changing the state by keyboard) loops through 3 states: checked, not checked and greyed.

If you are constantly running “svn up” or “svn status -u” or maybe “svn log -rBASE:HEAD” then you might find this piece of code useful:

#!/bin/sh  
 
dir="$1"

cd $dir
 
updated=`svn st -u | grep '*'`

if [ "$updated" ]; then
    notify-send SVN "$updated"
fi

What id does is check for updates in subversion and if it finds any then shows a notification using notify-send. Tested on openSUSE 11.1 with subversion 1.6. Add a crontab entry and you are done.

08
Aug

Google Wave Developer Preview at Google I/O 2009

After watching this video many ideas came to my mind! I’ve always wanted to have some sort of a system where you could collaboratively construct stories, movie scripts, etc. When I get my hands on an invitation for wave that’s the first thing I’m going to do. Would make celtx look so old fashioned!

I was in need for a solution to search a very large set of data in a javascript application. I thought of a b+ tree. Looking for a javascript implementation of it, google didn’t help much. So, I just gave up and decided to implement it myself. This is the result: not really a B+ tree but a variation that works better in this case. I call it a B# tree. It depends on the prototype framework but I’m planning on making some changes to it so it would work stand-alone.

var BSharpTree = Class.create( {
    root: null,

    initialize: function(comparator) {
        this.root = $A();
        this.comparator = comparator || function(x) {
            return x
        };
    },

    add: function(text, object, node) {
        var node = node || this.root;

        node.objects = node.objects || $A();
        if (node != this.root) {
            node.objects.push(object);
        }

        if (text.length > 0) {
            var chr = text.charCodeAt(0) - 32;
            var next = node[chr] = chr == 0 ? this.root : node[chr] || $A();
            this.add(text.substring(1), object, next);
        }
    },

    search: function(text, objects) {
        var node = this.root;
        var result = null;

        while (text.length > 0) {
            var chr = text.charCodeAt(0) - 32;
            text = text.substring(1);

            if (char == 0) {
                result = this.search(text, result);
                break;
            } else {
                node = node[chr];
                if (!node) {
                    return $A();
                }
                result = node.objects;
            }
        }

        if (objects) {
            if (node == this.root) {
                return objects;
            } else {
                return this.intersect(result, objects);
            }
        } else {
            if (node == this.root) {
                return $A();
            } else {
                return result;
            }
        }
    },

    intersect: function(a, b) {
        var i = 0;
        var j = 0
        var intersect = $A();

        a = a.sortBy(this.comparator);
        b = b.sortBy(this.comparator);

        while (i < a.length && j < b.length) {
            if (this.comparator(a[i]) == this.comparator(b[j])) {
                intersect.push(a[i]);
                i++;
                j++;
            } else if (this.comparator(a[i]) > this.comparator(b[j])) {
                j++;
            } else {
                i++;
            }
        }

        return intersect;
    }
});

It contains a very efficient intersect method. To use it just do something like this:

var bst = new BSharpTree();

bst.add("some string", "my object");
bst.add("some other string", "other object");
bst.add("last string", "last object");

bst.search("string"); // returns ["my object", "other object", "last object"]
bst.search("some string"); // returns ["my object", "other object"]

You can set any object as the second parameter to the BSharpTree#add method but then you might need to pass a comparator function to the BSharpTree constructor. Use it as you may see fit!

After a lot of time of searching what is the best way to synchronously load javascript from javascript I finally found a way to do it that works in almost any browser. The hardest part of it all is not loading and eval’ing the script, but allowing the script to set new variables in the global scope.

if (window.execScript) {
    window.execScript(contents);
} else {
    with (window) {
        window.eval(contents);
    }
}

This is what I’m finally using on my open source modular javascript project. To test it out I also found this website very useful. What I did was to create a test page that loaded a script, declared some global variables in different ways:

var someVariable = true;
someVariableWithoutVar = true;
eval("var someVariableWithEval = true");
eval("someVariableWithEvalWithoutVar = true");

Then loaded the website through browsershots and the results came in a few minutes. The tests passed on a lot of browsers including all versions I could test of Firefox, Opera, MSIE, Safari, with some minor exceptions: Safari 3.2.1 on Windows XP and OS X 10.5, Opera 7.54 on Windows XP, Konqueror 4.2 on Ubuntu 9.04.

The case with Safari 3.2.1 is that it cannot set global variables declared with var inside a eval’ed script. There’s a newer 3.2.3 version that I would like to test on to see if there is any difference. As for Opera, it’s an old version that nobody is probably using anymore. But for Konqueror I really don’t know the source of the error.

Take the test yourself. If you would like to see how the tests were done take a look at the sources here.

Update: The test suite now passes in almost every browser with the exception of some older versions of opera :) .

A long time ago computers started to be able to store “things” in memory. Even though there’s no doubt that today we mostly store “files” in computers, that doesn’t mean that’s the only thing we can store. There’s a real world analogy from some terms we use to store data in our computers. Quoting from wiktionary.org:

file
A collection of papers collated and archived together.
folder/directory
An organizer that papers are kept in, usually with an index tab, to be stored as a single unit in a filing cabinet.
library
A collection of books or other forms of stored information. An individual may refer to his collection of books and other items as his library.
An equiavlent collection of analogous information in a non-printed form, e.g. record library
index
An alphabetical listing of items and their location; for example, the index of a book lists words or expressions and the pages of the book upon which they are to be found.

Some time in the past century the file system was created. Since then a thousand file systems had come to existence. Most of them can store basically two things: folders and files. Well, most of them also store information about these two items like dates and sizes, and permissions, and extended attributes. But are they indexable? Can anybody do a fast search on those attributes? From the btrfs changelog:

Btrfs indexes directories in two ways. The first index allows fast name lookups, and the second is optimized to return inodes in something close to disk order for readdir. The second index is an important part of good performance for full filesystem backups.

But is there any filesystem that creates indexes on anything else? Well many desktop searching applications create their own indexes. You have Spotlight on the mac, beagle, tracker, etc on Linux, google desktop search on windows. They can really get out of sync. If you take your external hard drive from a mac to a Linux machine copy some files and then back again you need to wait to Spotlight to catch up. Sometimes I just want to search for a file and I only remember it was an image I copied yesterday. Browsing through a whole messy external hard drive to find that can take forever.

That’s why I started working on dejumble. I don’t want to organize my files on folders anymore. I just want to find them!

I had this error for a very long time when trying to use extensions in Inkscape 0.46 for Mac OS X:

“The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension”

After many hours of trying to find a solution I got it to work. This is what I did:

sudo easy_install lxml
cd /Applications/Inkscape.app/Contents/Resources/lib
mv libxml2.2.dylib libxml2.2.dylib.old
ln -s /usr/lib/libxml2.dylib

After that, all extensions (those found under the effects menu) started to work.

Update:
The problem seems to be fixed on the latest development version.

Update 2:
Fixed typo. Thanks to Bill Gathen for your comment.

When using ProjectPier in a different language than English you may come to see a bug when browsing through the dashboard. Some translations include html code in their strings (because of a migration from activeCollab?). ProjectPier escapes this strings and the code leaks to the user interface.

For example there is this Spanish translation:

'log add projectmessages' => ''%s' <strong>agregado</strong>',

On a first instance, %s is replaced by some string that may contain html, lets say “A&B”. Then ProjectPier scapes the resulting string and ends up like:

A&amp;B &lt;strong&gt;agregado&lt;/strong&gt;

And the end user-visible result is:

A&B <strong>agregado</strong>

I’ve attached a patch to this post that fixes this. This patch is a temporary solution for the problem, but a real solution would be filter the real content before it is inserted in the translated string. This is for ProjectPier v.0.8.0.3.

Download: projectpier-lang.patch