ActionScript: TileList, deleting, scrolling backwards

[Another day, another blog cross-post...]

I have a Flex app which shows users a TiledList of chemical structure depictions. Since it can be a large list, it's lazy-loaded from the server as the user scrolls through the list.

Users can delete items from the list, with undo. In order to do this with reasonable performance, once the app has received confirmation from the server that an item has been deleted, it clears out the single deleted item from its local lazy-list.

To undo the deletion locally, the Flex app fills the correct lazy-list entry with an ItemPendingError; that error gets thrown as soon as the TileList tries to retrieve the item.

All of this works okay when the row containing the undeleted item is already visible. On the other hand, if the user has scrolled away from the row where the undeleted item will reappear, then when (s)he scrolls back the TileList simply empties out that item and all of the successive items in the row. Ugly!

ugly_repaint.png

Workaround

When the item is undeleted, immediately try to retrieve it via getItemAt(itemIndex). Catch the resulting ItemPendingError and register an ItemResponder. When the ItemResponder's result or fault method is called, tell the TileList to invalidateList(). If the undeleted item actually contains a value, the TileList will repaint correctly -- no more unsightly gaps.

import mx.collections.errors.ItemPendingError;
import mx.collections.ItemResponder;
[...]
try {
    structures.getItemAt(offset);
} catch (e:ItemPendingError) {
    e.addResponder(
    new ItemResponder(
        function(result:Object, token:Object = null):void {
            tilelist.invalidateList();
        },
        function(error:Object, token:Object = null):void {
            tilelist.invalidateList();
        }));
}

Developing Django apps with zc.buildout

(Another cross-post from Bottled Text) Found this excellent introduction via Looking for quotes about Buildout. Developing Django apps with zc.buildout:

"... Finally, Buildout generated a bin/python interpreter."
So buildout makes it easy to create isolated Python environments, just like virtualenv, but perhaps with a different goal: isolated development environments vs. isolated deployment environments? Not sure; gotta read more...

Lang.NET 2009 at Ted Leung on the Air

[Cross-posted from Bottled Text] Gratis ago ad Ted Leung for posting his summary of Lang.NET 2009. It seems like every time I see one of his summaries, my reading and projects lists grow. Newspeak and Hopscotch More reading here. Ted also links indirectly to the video of Gilad Bracha's talk. Powershell

[Jeffrey Snover] seemed to think that UNIX shells could gain a fair amount of PowerShell’s capabilities by recognizing that pipes ship bytestreams, adopting a data format (like JSON or XML) for those byte streams, and proceeding from there. That might be true technically, but that would be a huge cultural change for that community.
Hm... imagine a /usr/bin_json/ sitting alongside /usr/bin. The first prototypes of the utilities therein could be based on David Beazley's tutorial "A Curious Course on Coroutines and Concurrency". (Start at slide 34 of the presentation slides.) Monads
[Erik Meijer] then used mindless symbol pushing to demonstrate that [IEnumerable and IObservable] were duals of each other, and that they obeyed the rules for monads.
I'm so far behind on terminology... what the heck is a monad? Google suggests it's what Pythonistas would call a list comprehension or a generator expression. Favorite Quote
I suspect that this is the only conference I will go to all year where Macs are the minority.

FF3.1b2, Safari 4b, and html 5 canvas, continued

The previous post described improved support for the HTML 5 canvas. It turns out the latest betas of both Firefox and Safari also support the canvas toDataURL method.

An obvious use for this method is to let users easily copy and paste rendered depictions into other applications.

    function saveToBitmap() {
        var canv = $('#canv');
        var img = $('img#bitmap');
        img.attr('src', canv[0].toDataURL());
    };
toDataURL_result.png

Firefox 3.1b2, Safari 4 Beta and HTML 5 canvas text

This will be useful for depicting chemical structures using HTML5 canvas: both Safari 4 beta and Firefox 3.1b2 support the HTML5 canvas text rendering APIs.

There are still rough edges, e.g. it's hard to measure text dimensions and therefore hard to position text properly. Still, it's great to see!

$(document).ready(function() {
    function redraw() {
        var canv = $('#canv');
        var w = canv.width();
        var h = canv.height();
        // Let canvas know how big CSS wants it to be.
        canv.attr("width", w);
        canv.attr("height", h);
        var ctx = canv[0].getContext("2d");

        ctx.clearRect(0, 0, w, h);

        var msg = "Hello, Safari text.";
        var fontSpec = "24pt Verdana";

        // Find out the dimensions of the rendered msg.
        var e = $('<span style="visibility:hidden;font:' + fontSpec + '">' +
                  msg + '</span>');
        $('body').append(e);
        var tw = e.width();
        var th = e.height();
        e.remove();

        ctx.save();
        ctx.translate(w/2, h/2);
            // Indented to highlight transformation state.
            ctx.save();
            ctx.rotate(-45 * Math.PI / 180.0);
            ctx.translate(-tw/2, th/2);
            ctx.font = fontSpec;
            ctx.fillText(msg, 0, 0);
            ctx.restore();
        ctx.restore();
    };
    redraw();
});

Flex: Stopping event propagation from itemRenderers | kahunaburger

Flex: Stopping event propagation from itemRenderers | kahunaburger:
"My problem was that whenever I clicked on one of the menu items (the four icons at the bottom of the right tile), the mouse event would not only execute the action associated with the menu-item, no, the event would propagate up the chain and would result in the current tile being selected as well. Not good - this has to stop. "
Kahunaburger just saved me a lot of effort. The posted solution works for DataGrids as well.

VoodooPad and TextMate

VoodooPad's new HTML-savvy application-specific URLs are a great way to retrace your steps in TextMate.

I keep my worklog in VoodooPad. I do most of my code editing in TextMate. When I'm trying to understand a new chunk of code, I often need to jump around through the code base.

It's hard to keep track of where I've been, so I can back out of a code path once I understand what it does. Until now I've just jotted down pathnames and line numbers in my worklog, so I could manually retrace my steps.

Puzzle Pieces

  • VoodooPad URLs. They let you inject HTML content — including hyperlinks — into an open VoodooPad document.
  • TextMate URLs of the form txmt://open?url=file://pathname&line=lineNum;column=colNum. Open one of these URLs and TextMate will open a window showing the specified line (and column, if specified) of the specified file.
  • Textmate commands and key equivalents. TextMate lets you define custom commands and trigger them with keyboard shortcuts that you specify.

Put these all together and what do you get?

Scenario

  • Select some code of interest in TextMate
  • Type your keyboard shortcut
  • A hyperlink pointing to the selected code is inserted into your VoodooPad document

Later, when you want to get back to that chunk of code, just click on the hyperlink in VoodooPad. TextMate will open the document and jump to the line of code referenced by the hyperlink.

Granted, you're probably going to edit that code someday; and then your bookmarks will break. But this is a handy way to leave a trail of breadcrumbs while you're trying to decipher a new body of code.

Code

Here's a Python script which implements this "Bookmark in VoodooPad" capability in TextMate. You can use it by opening TextMate's Bundle Editor (Bundles -> Bundle Editor -> Show Bundle Editor), creating a New Command using the tool menu at the bottom left of the Bundle Editor window, and pasting it into the resulting Command(s) text area:

#!/usr/bin/env python2.6
import os, datetime, urllib, subprocess

escape = urllib.quote

# What Textmate location are we bookmarking?
path = os.environ["TM_FILEPATH"]
lineNum = os.environ["TM_LINE_NUMBER"]
_tmURLTemplate = "txmt://open?url=file://{path}&line={lineNum}"
tmURL = _tmURLTemplate.format(path=path, lineNum=lineNum)

# Which VoodooPad worklog page should we add it to?
vpPageName = escape(datetime.date.today().strftime("%Y-%m-%d"))

# What text should Voodoopad show for the link?
currLine = os.environ.get("TM_SELECTED_TEXT", os.environ["TM_CURRENT_LINE"])

# How should the HTML be formatted?
_vpHTMLTemplate = '<a style="font:12px helvetica" href="{tmURL}">{currLine}</a>'
vpMarkup = _vpHTMLTemplate.format(tmURL=tmURL, currLine=currLine)

# What URL do we open to inject the HTML into VoodooPad?
_vpURLTemplate = "voodoopad:html={vpMarkup}&page={vpPageName}"
vpURL = _vpURLTemplate.format(vpMarkup=escape(vpMarkup), vpPageName=escape(vpPageName))

subprocess.check_call(["open", vpURL])

Specify a key equivalent such as ⌘-T (Command-T) and you're off to the races.

[Like many posts to this blog, this entry is also posted at http://bottledtext.blogspot.com.]

VoodooPad bookmarklets now support HTML

VoodooPad bookmarklets are very useful. But they let you paste only plaintext from your web browser into your document — until now.

VoodooPad Application URLs

VoodooPad understands application-specific URLs of the form voodoopad:description=encoded_text&page=name_of_VoodooPad_document_page. When you open one of these URLs, VoodooPad inserts the encoded text into the indicated page of the current VP document.

You can open voodoopad: URLs from other applications, such as a web browser. That's what's so cool about VP bookmarklets -- they let you quickly select some text in your web browser and paste it into a VP document, along with other information such as the URL where the text originated.

The only problem is, the description parameter can contain only plain text. So if you've selected part of a web page that contains hyperlinks, those hyperlinks won't come along for the ride.

New Feature

Gus Mueller, VoodooPad's creator, recently released a new build. It adds a new html parameter to VoodooPad URLs. Now you can select part of a web page and transfer it as HTML into your VoodooPad document.

Gus summarized the new feature thus:

The format of the bookmarklet is like so: voodoopad:page=junk&html=<b>hello</b>%20world!

Here's expanded code for a JavaScript bookmarklet which takes advantage of the new parameter. It works in both Safari and Firefox 3.0.x:

javascript: function pad(v) {
    var res=String(v);while(res.length<2) {
        res='0'+res;
    }
    return res;
};
var d=new Date();
var page=(String(d.getFullYear())+'-'+
          pad(d.getMonth()+1)+'-'+ pad(d.getDate()));
function selHTML() {
    var wr=document.createElement('div');
    var c=window.getSelection().getRangeAt(0).cloneContents();
    wr.appendChild(c);
    return wr.innerHTML;
};
location.href='voodoopad:html='+encodeURIComponent('<div style="font:12px helvetica">'+location.href+'<br/>"""<br/>'+selHTML()+'<br/>"""</div><br/>')+'&page='+page;

Worth noting: in this snippet the html content is wrapped up in a div which has an explicit font specification. The bookmarklet is transferring a chunk of HTML, but that chunk doesn't include any of the CSS from the original web page. So if you don't specify a style for the wrapper div, VoodooPad will use its own default style for the current VP page. (I think that's something like Times Roman 10px.)

Mozilla Webdev - Blog Archive - Native JSON in Firefox 3.1

Mozilla Webdev » Blog Archive » Native JSON in Firefox 3.1:

"Pretty easy huh? And here’s how to get a JSON string from an object:

var personString = JSON.stringify(person);"

As a long-time fan of Python's pickle and shelve, I hope JSON.stringify properly handles JavaScript object graphs. And I hope jQuery exposes a similar API.

I think jQuery provides only .param, which is designed as a helper for forms serialization and which does not handle nested objects. For example, in jQuery 1.3.1 this:

var graph = {
    'item': {
        'name': 'bob', 
        value:42
    },
    'index': 1};
$('#msg').html("Graph: " + $.param(graph));

produces this:

Graph: item=%5Bobject+Object%5D&index=1