Archive for September, 2010

SelectList 0.3.3

Thursday, September 30th, 2010

A new minor release of selectList is out, fixing a bug with incorrect handling of special characters in item names. Thanks to Sung Pae for reporting it and suggesting a solution.

Moving Windows to the Current Workspace in Gnome/Compiz

Thursday, September 23rd, 2010

On my home workstation, I have a pretty vanilla installation of Ubuntu (10.04), with Gnome as my desktop environment, and Compiz. I use four workspaces, and usually have around 50-60 application windows open and scattered around the workspaces. With this many applications, every once in a while I faced the problem of not being able to quickly find the one particular window that I wanted, because it was lost somewhere on one of the workspaces.

I frequently had this issue with KeePassX, a neat password management application. Often, when I wanted to log in to a website/service/whatever, and needed KeePassX to get the login credentials, I had to first go through all the workspaces to find the KeePassX window and drag it to the workspace where I wanted it. This was annoying.

Annoyance is the mother of invention, so I solved this problem with a shell script that locates the application’s window and brings it to the current workspace. If the script finds that the application is not running, it starts it. It takes two arguments, the first argument is a string that is used to locate the “lost” window — the script looks for a window with a title that contains that string. The second argument is the program to run if no window is found (optionally followed by program arguments).

This is how I use it to find/run KeePassX:

summon.sh 'passwords.kdb - KeePassX' keepassx

The KeePassX window title is "/path/to/passwords.kdb - KeePassX", and I use the "passwords.kdb - KeePassX" part as the title string. Just "KeePassX" might be insufficient, if, for instance, there is a web browser window open with the KeePassX homepage and also has "KeePassX" in the title. The "passwords.kdb - KeePassX" string is distinctive enough.

I have this set up as a launcher in my Gnome panel, so I can simply click the KeePassX icon and have the window pop up on the current workspace:

KeePassX Gnome panel launcher

So in case you’d like to use the script for a similar purpose, here it is:

#!/bin/sh

#
# summon.sh - Brings application window to current workspace or starts the
#             application if it isn't running. Intended for use with Gnome and
#             Compiz.
#
# Usage: summon.sh <window title> <program> [arguments]...
#
#        <window title> is the title (or a substring of it) of the window to be
#        found.
#
#        <program> is the application to run if no window is found. May be
#        followed by arguments.
#

SELF=`basename $0`

if [ $# -lt 2 ]; then
    cat <<END
Usage: $SELF <window title> <program> [arguments]...

    Brings application window to current workspace or starts the application if
    it isn't running. Intended for use with Gnome and Compiz.

    <window title> is the title (or a substring of it) of the window to be
    found.

    <program> is the application to run if no window is found. May be followed
    by arguments.
END
    exit 1
fi

TITLE_SUBSTR=$1
shift

# Check if the application is running
WINDOW_ID=`wmctrl -l | grep "$TITLE_SUBSTR" | sed -r 's/\s.*//'`

if [ -z $WINDOW_ID ]; then
    # Not running -- start it
    exec $@
else
    # Application is running -- its window is $WINDOW_ID

    # Get the dimensions of a single workspace
    XDPYINFO_OUT=`xdpyinfo | grep 'dimensions:'`
    WORKSPACE_WIDTH=`echo "$XDPYINFO_OUT" | sed -r 's/.*:\s+([0-9]+)x.*/\1/'`
    WORKSPACE_HEIGHT=`echo "$XDPYINFO_OUT" \
        | sed -r 's/.*:\s+[0-9]+x([0-9]+).*/\1/'`

    # Get the X and Y offset of the current workspace
    XPROP_OUT=`xprop -root -notype _NET_DESKTOP_VIEWPORT`
    CURRENT_X=`echo "$XPROP_OUT" | sed -r 's/.*= ([0-9]+),.*/\1/'`
    CURRENT_Y=`echo "$XPROP_OUT" | sed -r 's/.*= [0-9]+,\s*([0-9]+).*/\1/'`

    # Get the coordinates of the top left corner of the window
    XWININFO_OUT=`xwininfo -id "$WINDOW_ID"`
    WINDOW_X=`echo "$XWININFO_OUT" | grep 'Absolute upper-left X' \
        | sed -r 's/.*:\s+([0-9-]+).*/\1/'`
    WINDOW_Y=`echo "$XWININFO_OUT" | grep 'Absolute upper-left Y' \
        | sed -r 's/.*:\s+([0-9-]+).*/\1/'`

    # Calculate the new location of the window
    NEW_WINDOW_X=`echo "($CURRENT_X + ($WINDOW_X)) % $WORKSPACE_WIDTH" | bc`
    NEW_WINDOW_Y=`echo "($CURRENT_Y + ($WINDOW_Y)) % $WORKSPACE_HEIGHT" | bc`

    # Move the window to the new location and raise it
    wmctrl -i -r "$WINDOW_ID" -e 10,"$NEW_WINDOW_X","$NEW_WINDOW_Y",-1,-1
    wmctrl -i -R "$WINDOW_ID"
fi

The script uses several command-line tools: wmctrl, xdpyinfo, xprop, xwininfo, and bc. I’m pretty sure they are available by default in a standard Ubuntu installation (and probably most other Linux distros, for that matter). If, however, any of them happen to be missing in your system, install the appropriate packages first.

ImgZoom 0.2.1

Tuesday, September 21st, 2010

I’ve just released a new version of imgZoom. This is a minor update which brings a few bugfixes and small code improvements.

Thanks to Marc Hoyois for his feedback.

Decrypting the User Agent String in JavaScript

Friday, September 10th, 2010

I test all my jQuery plugin releases in a simple testing environment that I developed. It’s nothing sophisticated, but it allows me to do much of the testing automatically and generates nice reports. The results of the tests are saved in a database, and for each test there is (among other stuff) information about which plugin version was tested, with which jQuery version, and on what browser.

For that purpose, I wanted to extract the browser name and version number from the user agent string that browsers use to reveal their identity to web pages. However, it’s not that simple, since there is no common format of the user agent string and each browser vendor seems to have their own idea of what to put in it.

For example — which version of Opera is this?

Opera/9.80 (X11; Linux i686; U; en) Presto/2.2.15 Version/10.10


Is it 9.80, or 10.10? If you’re curious, it’s 10.10, and this Dev.Opera blog post explains it.

(If you’re even more curious about why this user agent string business is such a mess, I recommend you read this story — apart from being informative, it’s also funny.)

Anyway, for my testing platform I wrote a JavaScript function that extracts the browser name and version (by default just the major and minor release number) from the user agent string. I’m posting it here in case someone finds it useful too. It recognizes the “big five” browsers that have a significant market share (Firefox, Internet Explorer, Opera, Chrome, and Safari), as these are the browsers that I test my plugins on.

Here’s the source code:

/**
 * Extracts the browser name and version number from user agent string.
 *
 * @param userAgent
 *            The user agent string to parse. If not specified, the contents of
 *            navigator.userAgent are parsed.
 * @param elements
 *            How many elements of the version number should be returned. A
 *            value of 0 means the whole version. If not specified, defaults to
 *            2 (major and minor release number).
 * @return A string containing the browser name and version number, or null if
 *         the user agent string is unknown.
 */
function identifyBrowser(userAgent, elements) {
    var regexps = {
            'Chrome': [ /Chrome\/(\S+)/ ],
            'Firefox': [ /Firefox\/(\S+)/ ],
            'MSIE': [ /MSIE (\S+);/ ],
            'Opera': [
                /Opera\/.*?Version\/(\S+)/,     /* Opera 10 */
                /Opera\/(\S+)/                  /* Opera 9 and older */
            ],
            'Safari': [ /Version\/(\S+).*?Safari\// ]
        },
        re, m, browser, version;

    if (userAgent === undefined)
        userAgent = navigator.userAgent;

    if (elements === undefined)
        elements = 2;
    else if (elements === 0)
        elements = 1337;

    for (browser in regexps)
        while (re = regexps[browser].shift())
            if (m = userAgent.match(re)) {
                version = (m[1].match(new RegExp('[^.]+(?:\.[^.]+){0,' + --elements + '}')))[0];
                return browser + ' ' + version;
            }

    return null;
}

A few examples of user agent strings and the returned results:

Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.9) Gecko/20100825 Ubuntu/9.10 (karmic) Firefox/3.6.9 Firefox 3.6 Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.19 Safari/533.4 Chrome 5.0 Opera/9.80 (X11; Linux i686; U; en) Presto/2.2.15 Version/10.10 Opera 10.10 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0) MSIE 8.0

ImgZoom 0.2

Thursday, September 9th, 2010

As promised, I’m releasing imgZoom version 0.2. This is a major update compared to the previous release, as it adds several features that make the plugin an usable alternative to Lightbox (and its numerous clones) — most importantly, the ability to navigate between multiple images (you might have already seen this in the sample gallery that I posted some time ago).

Nonetheless, there are still some features which are obviously missing, like keyboard support and auto-scaling of large images. These are planned for version 0.3, which I hope to develop soon (but, as always, no promises).

The new features are described in the updated documentation. I still need to add some usage examples, though.

As usual, I’m looking forward to your feedback.