Undo in Webapps

28 July 2011

Anyone who's built a web-based application (as opposed to “just” a website) will likely have learned that the Undo functionality that has been built in to desktop apps for decades is simply nonexistent on the Web. Browsers do have some native Undo support for changes in form elements, as well as for commands executed against contentEditable elements, but that's about it. If you have an application that responds to user interaction by, say, modifying the DOM, there is no good way to provide the user with a robust undo history.

What exactly does “robust” mean here?

  • Undo/redo can be triggered by the keyboard shortcuts defined by the system, which users are accustomed to using in desktop apps. Not just when the focus is on a specific element in the document, and when the webapp has been specifically written to intercept the common keyboard bindings on known systems (thereby conflicting with the native undo support).
  • Undo should also work when invoked not through keyboard shortcuts, but from the browser menu bar, context menu, or other means.
  • The undoable (or redoable) action should be given an descriptive label in the browser chrome. For example, in OS X the Undo menu item will often have a more specific name, such as “Undo Typing”, when it is available. (I assume something similar is available on other desktop UIs.)

We've all seen web applications (and in particular rich text editors) that provide custom undo/redo buttons, which are in no way related to the undo/redo history managed by the browser. It's really ugly, but currently there's just no better way. A number of web developers have been calling for a proper Undo API over the last years, but apparently there has not been enough demand to have such an API be included in the HTML5 family of specifications so far. That is unfortunate: if there's any part of web applications that makes them feel severely less powerful than desktop apps it may well be proper undo support (except maybe cross-application drag and drop, but that is already in HTML5, although via the rather cumbersome interface invented in Redmond).

So I'm really glad to see that some progress is being made here in the form of a proposal by Ryosuke Niwa. The draft specification looks rather promising to me at first glance, but I still need to review it in detail. I'm not sure whether it's a good idea to support more than one undo history per page in the form of undo scopes, as I was under the impression that you normally get just one undo history per document in desktop applications. This is probably intended to support applications where you can edit more than one “document” per page at a time, but I can already see it being abused to provide one undo history per instance of a rich text editor, which I think would be counter-intuitive.

Anyway, in the meantime I'd like to encourage everyone interested in webapp development to read the proposal and provide feedback. We really need proper undo support in web applications sooner rather than later.

Building PIL with Freetype2 on Snow Leopard

7 July 2011

I had some trouble getting the venerable Python Imaging Library (PIL) compiled with the Freetype feature on Mac OS X 10.6. Doing a simple pip install or easy_install will build it with just the basic options. But if you need to generate Captchas and the like (with django-simple-captcha, for example) you'll need the Freetype option, too.

In fact, OS X comes with Freetype2 installed (as part of the X11 package), just not in a location the PIL build script knows about. That is simple to fix, however:

$ cd ~/Downloads
$ curl -LO http://effbot.org/downloads/Imaging-1.1.7.tar.gz
$ tar -zxf Imaging-1.1.7.tar.gz
$ cd Imaging-1.1.7
$ echo 'FREETYPE_ROOT = "/usr/X11/lib", "/usr/X11/include"' > setup_site.py
$ python setup.py build

The setup summary printed out at the end of the build should now say “FREETYPE2 support available”, which would mean this procedure worked as designed.

jQuery Ajax Transports

1 July 2011

A few weeks ago I published a jQuery plugin called jquery.iframe-transport.js, which I had written after reading up on the Ajax rewrite introduced with jQuery 1.5. The Ajax rewrite was big news for jQuery, as it enables things like non-XMLHttpRequest transports to be integrated seamlessly, and also because it introduced Deferred/Promise-style programming.

I've long wanted to get rid of the rather heavy-handed jquery.form.js plugin, which I've been using for some time to support traditional HTML file uploads in Ajax-based applications. But the jQuery Ajax subsystem is pretty hard to digest if you're trying to extend it in a non-trivial manner. The documentation and the examples are quite limited at this time, and I had to carefully read through and debug the relevant jQuery code to get my plugin to work.

In this article, I'll explain what this particular plugin does, and why it does that in the way it does. I'm sure there's room for improvement here, and would welcome any feedback guiding me in the right direction.

read on …

Lion Sandboxing

28 June 2011

From a security perspective, it has long felt ridiculous to me that any native application I run can access any data I can access, whether that be read-only or read/write, without my explicit consent. That's basically the decades old Unix model of security based on file permissions and application processes running as the current user. It looks like Lion is going to change that, at least for applications acquired through the Mac App Store. To me, this is by far the most significant part of the Lion feature set that has become public knowledge so far.


So Lion will introduce an application sandboxing feature, where applications basically run in a chroot environment. Applications can't just read and/or write arbitrary files somewhere on the users disk; instead they can (by default) only access files in their own little sandbox directory.

But apparently, the really cool trick about this is that the user can poke holes into the sandbox by using the standard open/save file dialog, drag and drop, or other explicit means of giving an application access to a specific piece of data. Which means that in most cases, users can continue to work with those applications as they have in the past, without having to even be aware of the sandbox that's providing them extra security. That would be huge.

Also, according to the official (and rather brief) description, the sandbox can limit the application's access to other resources, such as the network or the camera. Applications will presumably need to state upfront whether they need such access, and the user will be able to check those requirements before installation.

Of course, I have no idea how well this will work in practice, but it seems like a very elegant approach. It might very well be a huge step forward in terms of application security on mainstream desktop operating systems.

jQuery Event Delegation

22 June 2011

A lot of words have been published about the various methods jQuery provides to bind event handling code to specific elements in the DOM. In the very beginning there was only bind() and its plentiful shortcuts. Starting with the 1.3 release, native support for delegation-based event handling was added. The difference is that traditionally you attach event listeners to individual DOM elements, whereas with delegation you register handlers with a central event listener higher up the DOM hierarchy.

The benefits of the event delegation approach have been described in abundance, so I'll keep it brief: First, your handlers also get called for elements added dynamically after you've set up the event handler. Second, if you have many elements on a page that handle the same events in the same way, not having to attach the handler to each element individually is more efficient.

At first glance, the difference between using live() versus using the newer delegate() function seems rather subtle: the former registers the event handler at the document level (at least by default), while the latter can be used to register with an event listener on any element in the DOM.

So you may think that you should really use live() if you want to handle those events at the document level. However, as I found out the hard way a while ago, that can become a performance bottleneck, especially on browsers with lackluster selector and traversal performance (that is, basically, Internet Explorer). I will try to explain why that is by looking into the mechanics triggered by those innocently looking few-liners.

read on …


2 December 2009

Most programming languages get namespaces (or importing modules or files) wrong.

using System;
using System.IO;

public class HelloWorld {
   public static void Main(string[] args) {
      Console.WriteLine("Hello, World!");

Where did that Console class come from? System? System.IO? Is it somehow implicitly available? Just by looking at the code, without knowing the .NET framework, you can't tell. (Nor can you tell that the System.IO namespace is not used anywhere, so that the corresponding using statement can be safely removed. I think.)

You shouldn't have to ask this question, and you shouldn't have to rely on an IDE or documentation to provide the answer. It should be obvious from just reading or grepping the code. Languages that violate this simple rule include Java, C#, Ruby, and C/C++. I'm sure there are lots more.

UPDATE: Wow, it's been so long since I last hacked on Java code that I forgot how its imports work. Actually, Java imports don't violate the rule unless you use star imports, just like Python.

Turns out the languages I'm currently most interested in get this (mostly) right: Python, Erlang, Go, and (although not a language as such) node.js with its CommonJS-based module system:

var puts = require("sys").puts;
puts("Hello, World!");

Sure, you can put Python into dirty namespace mode by using “star imports”, and you can intentionally mess up the explicit namespacing in node.js by applying the process.mixin() method to pull exports into the global namespace. So please try not to do that.


5 December 2008

Python 3.0 has been been released with some fanfare, here's my take, as someone who writes a lot of Python code, but who has admittedly never actively participated in the evolution of the language or the standard library. For some background, I've been one of the early developers of Trac, and I've also written and continue to maintain a coupe of open-source Python libraries such as Genshi, Babel, and CouchDB-Python.

So, Python 3.0. Technically, it's a great thing. It does away with old warts in the language design, improves consistency and simplicity. The standard library with all of its inconsistently (and sometimes awkwardly) named modules has been reorganized. Strings are unicode by default. Integers have unlimited precision. And quite a bit more.


read on …

View Source

28 August 2008

I have written before about how I'm running my website on custom developed Python code. Back then I said:

The code isn't publicly available at this point, although I do intend to release it when I feel it's ready.

Well, stuff like this never is truly ready, but I'm putting it out there anyway: the basis is a custom little web framework called “Diva”, the project site is here, Subversion repository here. The code for both my blog and my scratchpad site is included as examples.

read on …

The Truth About Unicode In Python

3 July 2008

The unicode support in Python is generally considered to be pretty good. And in comparison to many other languages, it's good indeed.

But compared to what is provided by the International Components for Unicode (ICU) project, there's also a lot missing, including collation, special case conversions, regular expressions, text segmentation, and bidirectional text handling. Not to mention extensive support for locale-specific formatting of dates and numbers and time calculations with different calendars.

Basically what Python does provide out of the box is “only” encoding/decoding, normalization, and some other bits such as simple case conversion and splitting on whitespace. It's the absolute minimum you need to do anything useful with unicode, but often not enough to build truly internationalized applications. (Fortunately, most applications get away without true internationalization.)

In this post, I'm going to talk about a couple of the problems with unicode in Python. Please note that this is not intended as a criticism of Python's unicode support or the people who designed and implemented it. Most of those people probably know a whole lot more about unicode than I do, and the limitations discussed here are the result of a pragmatic approach to implementing unicode support, rather than due to a lack of knowledge.

read on …


15 April 2008

I'm going on vacation for three weeks starting in a couple hours, and I'll have little to no access to the net. So I'll be even less responsive to email and all that than I am anyway.

I would've really liked to make a Genshi 0.5 release before leaving, but unfortunately that didn't work out. Just as we were closing in on the last couple of tickets, Google came out with App Engine, which Genshi currently does not work with due to various restrictions in the hosting environment. And I'd really like the 0.5 release to be usable with App Engine (some progress has been made on a branch), so the release will have to wait until I'm back.