Archive for the ‘Post’ category

Peblingesø

posted January 13th, 2010, no comments, tagged

It’s been freezing in Copenhagen so last Sunday I went out to the lakes and took the opportunity to walk across them.

It had been a while since I stood on proper ice!

Blu Dot Real Good Experiment

posted January 10th, 2010, no comments, tagged

Design studio Blue Dot put a few of their chairs on the streets of New York and tracked the people who picked them up back to their apartments through GPS. A lovely concept and a lovely video:

Blu Dot Real Good Experiment from Real Good Chair on Vimeo.

Via Matt Cottam of Tellart, the folks who made the tech.

Good OpEd piece by Jasper Teulings of Greenpeace on the treatment of four activists who crashed an official banquet during the COP15 talks.

Before her arrest, Nora told an interviewer that she was aware of the possible consequences of what she intended to do but Nora assumed, as did we all, that in Denmark the law would play by the law. She would be arrested, charged, released until trial and then, if convicted, perhaps sentenced to a fine or some time in jail.

Nora, charged but not convicted, was held for twenty days in a prison cell. For most of this time she was permitted to receive no letters, books or family visits. From arrest through Christmas and New Year she had no contact with her husband and two young children.

(…)

After the arrest, Greenpeace guaranteed that, if the activists were released, they would voluntarily return to Copenhagen to stand trial. To further facilitate the police investigation, Greenpeace immediately offered its full co-operation to Danish police and provided them with comprehensive details of the activity. A request from Greenpeace asking the Danish police to specify what additional information they required in order to complete their investigation was met with two weeks of silence. While the police claimed their detention was necessary for the investigation, it turns out that the Four were only questioned briefly on their first day in custody and for 15 minutes shortly before their release.

Danish police has acted questionably during COP15, and seeing how this protest has been handled isn’t very encouraging. Restricting peaceful protest is bad for democracy, and unfortunately Denmark has become somewhat more restrictive over the past few months.

Redirecting your OpenID

posted January 4th, 2010, no comments, tagged

Previously, my OpenID login was under http://novemberborn.net/recent. But, with last week’s site update, I’ve added a landing page under http://novemberborn.net and am redirecting /recent to /latest. Albeit an improved setup, this has broken my OpenID logins to various websites which are expecting the login to be http://novemberborn.net/recent.

As it turns out, OpenID follows redirects, so even if I try to login with the /recent URL, the OpenID client is redirected to /latest which does not carry any OpenID information. Some Googling turned up a post from Will Norris entitled Challenges in changing my OpenID. He describes a few heuristics for detecting whether an HTTP request is an OpenID request:

  • The Accept header contains application/xrds+xml.
  • The user agent is identified as openid.
  • The user agent is an empty string, or the User-Agent header is not specified at all.

With these heuristics we can create a simple openid-redirect.php script that will redirect to /latest unless it’s dealing with an OpenID request, in which case it serves up a small HTML document carrying my OpenID information:

<?php
if(stripos($_SERVER["HTTP_ACCEPT"], "application/xrds+xml") !== FALSE ||
  stripos($_SERVER["HTTP_USER_AGENT"], "openid") !== FALSE ||
  empty($_SERVER["HTTP_USER_AGENT"])
){
  header("Content-type: text/html");
  echo '<html><head><link rel="openid.server" href="http://id.11born.net"><link rel="openid2.provider" href="http://id.11born.net"></head></html>';
}else{
  header("Location: /latest", TRUE, 301);
}
?>

Combine it with a simple mod_rewrite rule and we’re all set:

RewriteRule ^recent$ openid-redirect.php [L]

A quick test with curl to confirm everything is working:

mark$ curl -H "Accept: application/xrds+xml" -v novemberborn.net/recent
* About to connect() to novemberborn.net port 80 (#0)
*   Trying 8.17.171.168... connected
* Connected to novemberborn.net (8.17.171.168) port 80 (#0)
> GET /recent HTTP/1.1
> User-Agent: curl/7.19.6 (i386-apple-darwin10.0.0) libcurl/7.19.6 OpenSSL/0.9.8k zlib/1.2.3
> Host: novemberborn.net
> Accept: application/xrds+xml
> 
< HTTP/1.1 200 OK
< Date: Mon, 04 Jan 2010 22:22:17 GMT
< Server: Apache
< Served-By: Joyent
< Vary: Accept-Encoding
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html;charset=UTF-8
< 
* Closing connection #0
<html><head><link rel="openid.server" href="http://id.11born.net"><link rel="openid2.provider" href="http://id.11born.net"></head></html>

Now I can still use http://novemberborn.net/recent as my OpenID login, although I’m moving the various websites I use OpenID with to just http://novemberborn.net.

My year in cities 2009

posted December 30th, 2009, one comment, tagged ,

Cities I’ve visited this year, in which I’ve spent one or more nights. Cities marked with * where visited multiple times on non-consecutive days.

  • Naaldwijk, the Netherlands*
  • Enschede, the Netherlands*
  • Copenhagen, Denmark*
  • London, England*
  • Stockholm, Sweden*
  • Brighton, England*
  • Antwerp, Belgium
  • Amsterdam, the Netherlands*

See my list from 2008.

Geek Meet in Stockholm

posted February 27th, 2009, 5 comments, tagged , ,

I spoke at Geek Meet in Stockholm last night. The slides are now up on Slideshare:

  • Web Typography and sIFR 3 is about the current state of web typography, and a quick run through sIFR 3
  • Homemade Ubicomp is about ubiquitous computing you can do in your kitchen, with examples from PICNIC and the Copenhagen Institute for Interaction Design.

I had a great time, thanks to Robert Nyman and bwin for organizing.

Heads up: Stockholm soon, London next

posted February 24th, 2009, no comments, tagged ,

Life’s quite lovely here in Copenhagen. Last week was filled with gentle snow, mostly enjoyed from behind my office window. It all vanished on Sunday, however, and I strangely miss it

Therefore, albeit not quite, I’m headed up to Stockholm on Thursday to give two talks at Geek Meet. One on Web Typography and sIFR, and one on Homemade Ubicomp. An older version of that talk – which I gave at SHiFT ’08 – is available at Supercollider.

I’ll be in Stockholm until Sunday, so if you want to hang out, ping me. I think Robert Nyman is doing a guided tour through Stockholm on Saturday, so if you don’t know the town either (or think that Robert doesn’t!) perhaps you should join us.

I won’t be back in Copenhagen for long, because I’ll be in London for business for a week and a half from Wednesday on. That means I’ll be there during the weekend as well, anything interesting going on then?

P.S. Regarding sIFR, there’s a lot of interesting things happening with web typography and web typography alternatives. I’ll be speaking about that in Stockholm, and can hopefully do a post on it here afterwards. As you may have noticed, nothing really has happened on sIFR since October. I’m thinking of moving the project onto GitHub, LightHouse and Stack Overflow so more people can be involved. (This, by itself, is too slow a process already, which is why I’m hiding this in a footnote.) Let me know if you’re interested in helping out.

P.P.S. The real me on Twitter is here, if you’re into that kind of thing.

P.P.P.S. Christian Heilmann diggs my favicon. All credit to Nadya Peek.

My year in cities, 2008

posted December 30th, 2008, 6 comments, tagged ,

As per Kottke:

  • Enschede, NL*
  • Naaldwijk, NL*
  • Amsterdam, NL*
  • Copenhagen, DK*
  • Roskilde, DK,
  • Bogø, DK,
  • London, UK*
  • Brighton, UK
  • Lisbon, PT
  • Berlin, DE
  • Lausanne, CH
  • Geneva, CH

One or more nights were spent in each place. Those cities marked with an * were visited multiple times on non-consecutive days. I lived in both Enschede and Copenhagen, but took trips to the other city after moving. Bogø isn’t necessarily a city, but it is a place ;-)

sIFR 2.0.7: Fixed issue with CS4

posted December 1st, 2008, 21 comments

The sifr.fla file included in sIFR 2.0.7 could not be opened by CS4. This has now been resolved, thanks to Andrew Crowe who managed to open it in CS3 and re-save the file in a different format. Many thanks to Andrew, and also Nathan Strong, Todd O’Connor, Chris Peters and Lenny de Rooy for reporting the issue and testing a solution.

You can download the updated file from the sIFR 2.0.7 page.

(I would have done this myself, were it not for the fact that I’m still using Flash 8! Any help in getting CS4 would be much appreciated.)

sIFR 3 without Flash Pro

posted November 7th, 2008, 17 comments, tagged

There are currently two tools available which generate sIFR 3 movies for you, meaning you don’t have to use the Flash IDE to export them yourself. First there’s sIFR Generator, run by Richard Odekerken, which lets you upload a TTF file which is then turned into a sIFR movie. sIFR Generator currently generates movies compatible with r419, so it’s not useful if you want to use the latest sIFR 3 release. Which, of course, you should.

Update, November 25th: sIFR Generator now supports r436.

Also of importance are the legal issues surrounding the uploading of TTF files to a third-party server. Richard writes:

This is a Software-as-a-Service tool. Using this software requires you to upload font files to our website. By uploading the fonts you grant us permission to use the font, on your behalf, to create a sIFR SWF file that contains the font, and make it available to you.

All intellectual property rights and copyrights that apply to the original font, also apply to the generated font file. This file is made available only to the party who uploaded the font file.

We do keep fonts uploaded to sIFRGenerator.com for debug and statistical purposes.

Also available is sIFR Font Maker by CoffeeCup Software. This is a free, open source tool that runs under Windows. It too generates r419-compatible movies, but unlike sIFR Generator there is no notice of this fact. This, along with the lack of direct links to sIFR 3 pages is something CoffeeCup should fix. That said, it does run locally, so that’s a plus over sIFR Generator in terms of uploading TTF files.

I’ve seen a few reports of links not working properly in movies created via sIFR Generator. It wouldn’t surprise me if this issue is present in sIFR Font Maker as well, since they’re quite likely to use the same underlying software. But, if you need to create a font file, and don’t have access to the Flash IDE, and are okay with using r419, these tools are for you.

BarCamp Copenhagen is going down in three weeks, on November 22nd. Hope to see you there.

http://barcamp.org/BarCampCopenhagen

P.S. There’ll be a great pre-BarCamp game, details of which are kept secret.

P.P.S. The team is on the lookout for sponsors, for food, drinks and T-Shirts. More on the BarCamp page.

Over the past few days I’ve been making some improvements to sIFR 3, resulting in r436. A quick overview of the important changes since r372:

  • Now supporting Opera! Playing it safe with Opera 9.61, but there is not much reason to be using older versions.
  • sIFR.prefetch() has been merged with sIFR.activate().
  • sIFR.callbacks() has been renamed to sIFR.replacements.
  • Now using ExternalInterface for Flash – JavaScript communication.
  • Improved whitespace filtering before passing on the content HTML to Flash.
  • Improved CSS Load detection, which is disabled by default, but helps in making sIFR replace elements faster in Safari and Opera.
  • Changed how browser and Flash versions are stored in the sIFR.ua object. Please consult the changelog for r408 for full details.
  • Imitating SWFObject behaviour for inserting Flash movies.

Changes since r419 (which saw close to 12.000 downloads):

  • Made some improvements to decrease the jumpiness on the page caused by the replacements.
  • Dramatically simplified font size calculation, making it more accurate in Internet Explorer, and removing potential issues with IE 8. This also removes the need for specifying line-height: 1em for the elements being replaced.
  • Fixed 2 pixel cut-off from the leading property (caused by Flash).
  • Fixed ratio calculation when leading is specified. The leading is now removed before doing any ratio calculations.
  • Enabled Flash transparency on Linux with Flash 10, Gecko 1.9 and Opera.
  • Font size of nested HTML elements can now be configured in pixels.
  • Merged sIFR-screen.css and sIFR-print.css into sifr.css, using the @media attribute to distinguish the media types.

There’s still a number of issues left to figure out, although so far I haven’t had any reports of these issues impacting users. For example, I still have questions about how browsers handle Flash movies that are outside of the viewport, I’d like to see if CSS Load detection could be improved, and what’s up with cross-domain Flash movies. These issues all need extensive research and browser testing.

As always, you can get the latest release from the nightlies.

Other useful links:

These past few months have seen some interesting new developments. Most important of course is the support for Font Linking in Safari 3.1, as well as the upcoming Firefox 3.1 and Opera 10. Finally it’s becoming possible to embed existing fonts on websites, without going through hacks like sIFR or image generation. That said, the current problem with Font Linking is the required redistribution of original font files to web browsers, forbidden by many font licenses. This leaves many typefaces unavailable for embedding. Furthermore, Chris Wilson of Microsoft expressed that Microsoft (and, by proxy, Internet Explorer) should not support Font Linking in it’s current form. Microsoft does have its own EOT format, which it has proposed to the W3C for standardization, and would solve these issues. Mozilla, Apple and Opera however seem opposed to it, mostly out of fears for DRM. I believe these fears are unfounded, for if EOT is DRM, it’s DRM applied by the licensee, not the licensor. It’s like getting an MP3 from Apple and putting DRM on it before you pass it to a friend, instead of getting DRM from Apple preventing you from passing it to a friend. If we want cross-browser, legal font embedding in the short term, EOT is the way to go.

While we wait for cross-browser font embedding, we’re stuck with the alternative hacks. Some new ones have come up recently. FaceLift now seems to be the best way to use images rather than Flash for displaying the font. It uses server-side image generation through PHP, and cleverly provides a hosted service. In the past week Typeface.js came out, which is a devilishly smart way of actually rendering a typeface using <canvas> or VML, though given the complexity of that problem, Typeface.js probably isn’t ready yet for prime-time.

I’d like to point out that these solutions shouldn’t be seen as much as alternative to sIFR – no matter how eager they are to market themselves as such – but as alternative solutions for the real problem: reliable cross-browser font embedding. We’re merely trying to provide the best hack-that-shouldn’t-be-necessary.

That said, I do think that sIFR 3 provides a better solution in being completely client-side, providing actual selectable text, and supporting a subset of HTML and CSS rendering.

I’ve done a few sIFR presentations and workshops in the past few months, most recently at the <head> web conference. Slides are on Slideshare, however the footnotes have gotten lost in an JSON encoding mishap on their end. Therefore, I’ve put up a PDF with notes (17.3 MB). I’m speaking at DrupalCamp CPH, which takes place November 15th and 16th here in Copenhagen.

If you’re interested in a sIFR workshop at your company, or are looking for a weathered web hacker, please get in touch via Supercollider, my freelance alter-ego.

Now, go try out r436, and report back your findings!

Right!

posted October 23rd, 2008, no comments, tagged ,

Let’s see, where were we?

I went to Mediamatic Social RFID Hackers Camp at PICNIC in Amsterdam. Blogged about it on Supercollider.

Then I went to Lisbon, for SHiFT 08, and gave a talk on Home Made Ubicomp.

Sunday, I’m speaking at <head> on Web Typography with sIFR 3. It’s an online conference, so I’m presenting at home or perhaps with some friends, through my laptop. They’re using Adobe Acrobat Connect, which for some reason does not support PDF documents, nor does it support Keynote files, so I had a lot of fun converting to PowerPoint, opening in crappy software also known as OpenOffice, and patching the presentation to a reasonable level of sucktitude. We’ll see what happens.

On November 16th, I’m physically giving pretty much the same presentation, but with working slides, at DrupalCamp CPH. Henriette would not be amused.

I’m now also in the Danish systems, and started Twittering after not using my account for over two years. Then I reached 42 followers, so what choice did I have? Public for now, if that keeps working mentally.

That’s it then, short update. I suggest you subscribe to the Supercollider blog for more technical articles, as I’m turning Novemberborn into a more personal website. Which may mean that the posting activity may go down even further, we’ll see what happens.

I also blog at Toothless Tiger these days.

By the way, Supercollider is my freelance alter ego. Yes, I’m for hire.

sIFR 2.0 – 2.0.5 failed to detect the Flash 10 player, and therefore falls back to normal HTML text. This had previously been resolved in sIFR 2.0.6, however an issue remained with Safari. There is a second Flash version detection, which had not been fixed, and resulted in transparency support being disabled for Safari browsers with Flash 10 installed.

If you are upgrading from sIFR 2.0.4 or older, you must upgrade the sifr.js JavaScript file and re-export your sIFR Flash movies using the sifr.fla file from sIFR 2.0.7.

If you are upgrading from sIFR 2.0.5 or 2.0.6, you must upgrade the sifr.js JavaScript file.

Update, December 1st, 2008: A new sifr-cs3-and-up.fla file has been added to the download. Use this file if you’re using Flash CS3 or later. Find out more.

Detailed Description

sIFR 2 uses the same Flash detection that was originally used in its precursor, IFR, back in 2004. Unfortunately this detection script only expected single digit Flash versions, so it fails to detect Flash 10. This has been fixed in sIFR 2.0.6. However, I missed a second Flash version detection, which was used to check for transparency support in Safari browsers. Back in the day, Safari did not support transparency with Flash 6, so an explicit check for Flash 7 was added. The second version detection failed to detect Flash 10, disabling transparency support under Safari.

Thanks to Giancarlo Gomez for originally pointing out the problem with the Flash detection, and Marco Della Pina for pointing out the Safari problem.

In memoriam Mark Hoekstra

posted September 21st, 2008, no comments, tagged

Mark Hoekstra, hardwarke hacker and tinkerer extraordinaire, passed away last Friday.

Mark, you will be missed.

For more background, see:

iPhoneDevCamp Copenhagen

posted September 17th, 2008, no comments, tagged , ,

October 3rd and 4th is iPhoneDevCamp Copenhagen, co-organized by yours truly. We’ve got 27 places left, so sign up now!

For more info, see http://iphone.devcamp.dk

Location Identity

posted September 9th, 2008, 7 comments, tagged ,

Over the past 13 days I’ve been in five different countries. It started by taking the train down to Enschede, from Denmark, through Germany. After spending some time in The Netherlands, I boarded a flight for Brighton, in the UK. I got back to Copenhagen yesterday, and spent a few hours in Malmö, Sweden this evening.

I don’t have a very clear feeling anymore of being in a foreign country. The sense of wonder is gone. The excitement of being in Enschede, of being with my family, of being in Brighton or Copenhagen, it doesn’t start more than 24 hours prior to arrival.

(It could, however, be argued that this is because I go to countries I’ve been to before. Still, as a child, being somewhere else was just wonderful. Not so much, no more.)

Neither do I see myself as being primarily Dutch. I’ve begun describing myself as a European Dutchman. Yes, I have Dutch roots, and the more time I spend abroad, the more cultural differences I notice, the more I recognize these roots. Being Dutch is part of my identity. But, also, being European. Especially European, for within Europe, I don’t think in countries anymore. I think in cities, in places, in people.

And even on a global scale, I think in cities, places and people more than I think in terms of countries. It’s just the experience of crossing the border, with extensive questions, that make it clear that you really are in a different country.

(Again, I’ll interject, and state that if I were to go to Seoul, I’d really feel that I’m going to South Korea, and not just Seoul.)

In Europe, most countries have signed the Schengen treaty, which means that there is no border control. And even for the few countries that haven’t, like the UK, showing a simple ID card is enough to get in. No questions asked. Heck, the Danish border control only glanced at my ID card before waiving me through.

The ease of travel, and for me, the experiences of living abroad, with two months in the Bay area, and now moving to Copenhagen, they change how I feel about the world and about travel. They change my Location Identity. I’m Dutch, but I don’t live there. I’m not yet a Copenhagen person, and will never be Danish. Where does that leave me?

For now, European Dutchman will do.

What is your Location Identity?

(For the best introspective thinking, reply late at night after a train ride home from a foreign country, to a somewhat foreign country.)

Autumn’s Coming

posted September 3rd, 2008, no comments

It’s a grey day, and I’m sitting in the Xopus office in The Hague, Netherlands. I’ve been here since last week, having taken the train down from Copenhagen to Enschede for Elmine’s birthday and unconference. A great time was had, with many insights I hope to describe in a blog post soon.

This afternoon I’m headed for Brighton for the dConstruct conference on Friday, and perhaps also Barcamp Brighton. The fantastic Jerome Ribot has graciously made available his couch (I guess) for crashing, and I’m very much looking forward to the trip.

Come next Monday, I’ll be back in Copenhagen for only a few days. I’ll be in Amsterdam Sept 19th – 27th for the Mediamatic Social RFID Hackers Camp @ Picnic 08.

In business news, I’ve joined up with Toothless Tiger, Henriette’s up and coming web freelance agency. I’ll be blogging on the Toothless Tiger blog about businesses and the web.

I’ve also been setting up my own freelance business in Copenhagen, under the moniker Supercollider. That’s high energy, particle accelerated collisions, resulting in new insights and different matter. In other words, if I can collaborate with you, we’re going to make for some creative collisions! I’m moving my web application musings to the Supercollider blog, with the first blog post being about Method Chaining.

I’ve been busy with a few other projects during the summer as well, about which more later. For now, this should break the 41 day blogging lull over here ;-)

Web Fonts, Licensing and EOT

posted July 24th, 2008, 17 comments, tagged ,

Over on the IEBlog, Bill Hill recently posted about Font Embedding on the Web. In it, he discusses how Microsoft has submitted it’s Embedded OpenType (EOT) file format to the W3C for standardization. This could be an important development for web typography, and here’s why.

Web fonts as currently implemented by Safari 3.1, and supposedly supported in Firefox 3.1 and Opera 10, work by linking to the original TrueType Font or OpenType Font files on a web server. For all intents and purposes, this comes down to distributing a font file. In the case of commercial fonts, this is most likely a violation of the license agreement for the font, which means that you, as a web designer, will get your ass sued.

EOT, however, was designed to enable you to use TrueType or OpenType fonts on the web, without getting your ass sued. Quoting from the W3C submission:

Most commercial fonts have an end user license agreement (EULA) which specifies the rights a person who has purchased a license to use the font may enjoy. As the right to freely distribute the font to others is not normally granted, being restricted to installing within a limited set of parameters, there exists a need for a mechanism to allow people who have licensed the font for use with their documents a way to also use the font for their web content without violating the EULA agreement which they have accepted. The EOT file format was developed by Microsoft in cooperation with font creators for just that reason. The technology has been accepted for use by font makers for over 10 years.

EOT respects the flags in font files regarding embedding and can be limited to domains or full URLs. Another important feature is that it is able to compress the font data and lets you pick the glyphs you want to use – resulting in much smaller files than directly linked TrueType or OpenType fonts.

I believe this is a good, practical solution for having real web typography, which performs well and doesn’t violate font licenses. Some steps still need to be taken, obviously. First of which is cross-browser support for the EOT format, which should be in reach if EOT is accepted by the W3C. Another problem with EOT currently is that the software to generate EOT files is specific to Windows. However, given open formats, that’s a problem that can be overcome as well.

As, apparently, with every Microsoft announcement, there’s plenty of Microsoft bashing being done. Most of the bashing revolves around Microsoft pushing another proprietary format, people not being willing to respect licenses on typefaces, and a feeling that commercial fonts aren’t worth using anyway. I’ll let Chris Wilson reply to this:

As I also stated to the WG – I don’t personally even care that much if that system is EOT as it is today; I’d be okay with building a new system if the details of EOT were a sticking point. But I want to use commercial fonts on my web pages, I want that to work interoperably across browsers, and I want to not have to violate my license for the fonts I use (and get sued for it) in order to make that happen. A solution that only works for freeware fonts is not a solution.

(JavaScript) Hackmeetup Oresund, July 22nd

posted July 21st, 2008, no comments

Quick pointer to tomorrow’s hackmeetup in Malmö: http://upcoming.yahoo.com/event/921335/. Yours truly will be doing a very speedy version of my sIFR talk at Singularity, hopefully combined with some weird JavaScript hacks.

Unfortunately, sIFR 2.0.6 did not resolve all issues. Please use sIFR 2.0.7 instead.

sIFR 2 fails to detect the Flash 10 player, and therefore falls back to normal HTML text. This has been resolved in sIFR 2.0.6.

If you are upgrading from sIFR 2.0.4 or older, you must upgrade the sifr.js JavaScript file and re-export your sIFR Flash movies using the sifr.fla file from sIFR 2.0.6.

If you are upgrading from sIFR 2.0.5, you must upgrade the sifr.js JavaScript file.

Detailed Description

sIFR 2 uses the same Flash detection that was originally used in its precursor, IFR, back in 2004. Unfortunately this detection script only expected single digit Flash versions, so it fails to detect Flash 10. This has been fixed in sIFR 2.0.6. Thanks to Giancarlo Gomez for pointing out the problem with the Flash detection.

Koebenhavn

posted June 29th, 2008, 8 comments, tagged ,

So, I’m in Copenhagen now. It’s been a long while since my last post here, and people have been asking me to update more frequently – especially now that I’m an expat! Luckily I set the bar quite low, so no problem there ;-)

I got here last Tuesday, courtesy of Ton Zijlstra and Elmine Wijnia (thanks!). I’m in a temporary place, until August, so in desperate need of slightly more permanent housing. Going to check out two apartments on Thursday, hopefully that works out.

This past week was spent on Reboot, which again was pretty awesome. Spoke a lot of Danes, who were extremely kind – thanks for making me feel welcome! Next week I’m going to be at Roskilde. If there’d be a Danish integration course for foreign geeks, I’d have passed by now!

On a somewhat more professional level, I’m still figuring out what to do. I’d like to stay flexible though, so freelancing seems like the obvious answer. Consider this my Mark is on the market announcement. Read what I wrote about myself back in February. One of the things I’d like to do is finish sIFR – and given the cost of living in Copenhagen, it’d be great if I could find a (few) sponsors to enable me to do that. If possible, Danish sponsors, since I do need a residence permit in order to stay here. If you’re interested, get in touch.

Besides finishing sIFR, I want to finally write server-side code again. I’ve got some pretty neat ideas for this website, as well as a dedicated sIFR website. More on that later. A new site should also stimulate me to write more, which I really need to do – too many great (or I’d like to think so) ideas go to waste cause I don’t write about them.

In other news, I’ll be in the Netherlands end of August / early September. Then Brighton for dConstruct. I’ll be in Amsterdam around PICNIC to work on RFID stuff with Mediamatic. And, of course, I’ll be in Lisbon for SHIFT in October, followed a week later by Singularity, where I’ll give a presentation about web typography and sIFR. That’s probably in Copenhagen.

I’m going to see where life takes me, and where I can take life. Mark out ;-)

Two LIFT Quotes

posted February 26th, 2008, 2 comments, tagged

A lie is a conscious prevention of reality. A lie is not the opposite of truth; a lie is the opposite of reality.

Quoted from Peter Steignitz, Austrian social-psychologist, in a presentation.

The complexity of the name is part of the story. This is not technology as usual.

Rafi Haladjian about the naming scheme for Nabaztag. Subsequent versions of Nabaztag have /tag added to them, recursively, so the 4th would be Nabaztag/tag/tag/tag.

Unfortunately I didn’t write down any other quotes.

For the Dutchies reading this, Friday the 29th the Internetcreatieven Kennisdag mini-conference takes place in Delft. Speakers are André Weenink, who’ll be talking about design research in branding, Arjan Westerdiep, who’ll be premiering a pixel-art illustration consisting of no less than 1,664,000 pixels, Gert Hans Berghuis of Fabrique Communicatie & Design, and Bob Corporaal and yours truly. We’ll be speaking about happy marriages between Flash and HTML, with of course sIFR being a prime example.

Registration is only € 23,80, so I hope to see you there!

Web Hacker For Hire

posted February 18th, 2008, no comments

For those of you who can’t read: I don’t break into computers, I just build cool stuff.

Well hello! Within a few months I’ll be graduating from Twente University with my Bachelors degree in Computer Science. I’ve decided not to take a Masters, but to venture out into the world. More specifically, I’d like to venture out to Copenhagen. And since I’m not keen on moving to Copenhagen while still working for Xopus, I need a new job!

I just updated the colophon page to explain what kind of person I am, and what I believe in. I’m quoting a part of it here, because it highlights what I want to do next. Of course, you’re encouraged to read the whole thing:

I love the web. I love it for its creative destruction, for bringing people together and empowering them. For unleashing a new generation of creative people no longer simply consuming but interacting and creating. I sincerely believe the web can dramatically change the world – for the better. I want to be a part of this change.

I want to work with smart, creative people who want to change the world for the better.

And really, that’s all it boils down to. If you are a company changing the world, and you’re looking for a sharp web hacker, please get in touch. By the way, if you’re not from Copenhagen, please do get in touch anyway. Just realize I think Copenhagen is really, really great.

Og jo, jeg vil meget gerne lære dansk.

Forgetful Interfaces Workshop/Discussion @ LIFT

posted February 16th, 2008, no comments, tagged

At last week’s most amazing LIFT conference I organized a workshop / discussion on forgetful interfaces. I’ve posted a report from the workshop on my very own LIFT blog, and it’s reposted here as well:

It’s a bit over a week since the workshop on Forgetful Interfaces. I proposed the workshop because I had a feeling that simply storing the data we collect about ourselves may not be such a good idea. To my joy and surprise, all proposed workshops happened, including mine! However, because my thoughts on Forgetful Interfaces were still uncrystallized, I decided to turn the workshop into a discussion. And what a discussion it was. Thanks to all of you who took part, it was truly amazing.

Now, originally I had approached the subject from the perspective of privacy, which turned out to be perhaps the least interesting aspect of forgetful interfaces. We’ve arrived at three different aspects, and there is a fourth one which was introduced to me, outside of the workshop, by Irwin Oostindie. These aspects are privacy, filtering, user experience and security (the latter proposed by Irwin). But, first of all, what is a forgetful interface? In my view, a forgetful interface hides artifacts from view. Here I’ve used the term artifacts rather than data, because forgetful interfaces are about data in a social context. I’m specifically not talking about deleting the artifacts. They’re still there, just harder to get at. Just like how you may forget things yourself, but given enough clues your brain can bring them back.

So, with that out of the way, let’s look at the aspects of forgetful interfaces.

We’ll start with privacy. The publicity of an artifact can be placed on a scale, from intimately private to extremely public. The closer to the public end of the scale an artifact is placed, the more a forgetful interface may be welcomed. Such an interface could, for example, “forget” certain artifacts after a month. Given that more private artifacts are only available to a smaller group of people, there is more social control over these artifacts which lessen the need for forgetfulness. An example was given of pictures from a party where somebody did not, under any circumstances, wished to have her photographs displayed publicly. Violating this agreement would most certainly place the violator outside of the social group of those attending the party.

Filtering is a type of forgetful interface which helps you find relevant stuff, without throwing away anything. Artifacts which never get touched may be moved out of the main interface, perhaps even be compressed and stored externally. The artifacts are still there, however it’s become pretty hard to use them. Robin Hunicke mentioned this is how the New York Times works: some articles can be found online, but you have to pay to retrieve others from storage. Jerome Ribot just published an awesome post about using forgetful interfaces for filtering. I suggest you read it :)

A third aspect of forgetful interfaces is user experience. Pretty much all social sites will send permission requests your way, to join a specific group, or add somebody as a contact. This bombardment of requests is an oft-heard complaint about Facebook. Last.fm and also the LIFT site will show you a list of people who have not yet added you back as a contact. Being confronted with this list time and time again may become a bit… painful. But, let’s face it, if the permission requests aren’t fulfilled within, say, a week or two, it’s probably not going to happen anyway! In order to improve the user experience, why not forget about them? (Yes, this may actually mean deleting the requests, so perhaps this could be called a deleting interface).

Security may also be a deleting interface. In short it’s about automatically hiding artifacts from third parties when they’re no longer of direct use to the primary party. In order to successfully hide artifacts, they’ll have to be destroyed or perhaps be obscured by large amounts of random data. Truth be told, I haven’t given this aspect much thought yet.

Determining which artifacts ought to be forgotten was given some attention during the discussions, but perhaps not in enough detail. It was argued that we’d need to keep track of use patterns of artifacts in high detail, in order to correctly predict which artifacts users remember and which they don’t. Personally this tracking of use patterns scares me, and I think some more simple heuristics may do quite well. Recency of the artifacts and their use, and some information about the social relationships formed around the artifacts, combined with a simple measure of attention spent on the artifacts should provide enough information to determine which artifacts are important and which are not. Artifacts which have been created or accessed recently have a higher chance of being accessed again, and should therefore be preserved. This is the principle on which most computer caches rely. However, this means that the pictures of your newly born child from five years ago run the risk of being forgotten – even though the social relationships around these artifacts are very strong. Here attention comes into play, carefully describing and organizing an artifact indicates it’s more relevant than artifacts which are never organized. Knowledge of social relationships helps those artifacts about your parents from being forgotten, even though you may rarely interact with these artifacts.

Finally, a word about how a forgetful interface should be presented to the user. We arrived at the metaphors of fading and decaying the artifacts. These metaphors are very much visual, and help convey that the artifacts are disappearing or falling apart. Triggering one of the heuristics may reverse the process so the artifact is preserved (for a little while longer).

The discussions I’ve had at LIFT about forgetful interfaces have been very, very interesting. I definitely plan to delve into this further, and I’d like to thank again all of you who shared your wonderful insights and experiences. Now, let’s discuss this some more ;-)

sIFR 3: r372 Zoom Away!

posted January 26th, 2008, 55 comments, tagged

A quick update with regards to the beta. A number of issues have been found concerning page zoom and Firefox. These issues should all be fixed now. Other changes include:

  • Several bugfixes regarding forceSingleLine.
  • The XHTML demo was not built properly by the build script.
  • Added support for .sIFR-root { cursor: pointer; }.
  • Print preview in Firefox/Windows no longer throws errors.
  • The fixFocus caused infinite recursion, but no more!
  • sIFR should no longer cause the page to “jump” when the window is resized, although the sIFR elements may flicker a bit.
  • Text replaced through replaceText is now remembered after moving back to the page through browser history in Firefox.

You can get r372 from the nightlies.

Useful links:

The Kill Switch

posted January 23rd, 2008, 7 comments, tagged

So. The good news is that IE8 will be much, much better than anything before it. The bad news, Microsoft doesn’t dare release it and make it render existing websites, because there’s no way they’re going to render properly. Why? Because of the browser specific hacks put in to make the site render properly in IE6/7.

A “kill switch” is proposed, which will cause websites to render in the new or updated engine, whilst not breaking existing websites relying on IEs quirks.

Fair enough.

Except that, what if insert <meta http-equiv="X-UA-Compatible" content="IE=8" /> into this website? IE6/7 won’t recognize it, so I can’t really rely on the new CSS support in IE8. In fact, conditional comments are going to be needed so I can load the IE6/7 CSS, and the IE8 CSS, depending on the user agent.

Clumsy, but possible.

The really interesting bit is going to be a few years from now, when IE9 is released. Will I have to insert <meta http-equiv="X-UA-Compatible" content="IE=9" />?. What’s IE8 going to do with this switch? And how different will IE8 and IE9 be? Perhaps I’ll need to conditionally load CSS for IE6/7, IE8 and IE9.

And then IE10, IE11, et cetera…

Seems to me this meta switch is quite reasonable, as long as it kills off the old Trident engine, and activates a proper engine which sees continuous improvement. Much like, you know, modern browsers.

January 26th, Jeremy Keith describes the issue much better than I’d have the patience for. Recommended reading

sIFR 3 Beta 2: Fire Cracker

posted December 31st, 2007, 75 comments, tagged

As years end nears, neighbourhood kids are playing with fireworks around the house. A nice homage to the second beta of sIFR 3 I’d like to imagine. Feature wise sIFR 3 is now complete, and I think it’s gotten pretty awesome. If you’re trying to decide between sIFR 2 and sIFR 3, sIFR 3 is definitely the better choice.

What is sIFR?

sIFR is meant to replace short passages of plain browser text with text rendered in your typeface of choice, regardless of whether or not your users have that font installed on their systems. It accomplishes this by using a combination of JavaScript, CSS, and Flash, which renders the font. It degrades gracefully if Flash is not present. sIFR 3 is open source and licensed under the CC-GNU LGPL.

What’s new?

Here are the major changes since the last beta:

  • The Flash movie and the JavaScript must be of the same version.
  • Added ratios, which make the loading of the Flash movies less jumpy.
  • sIFR does not work when opened straight from the local filesystem. You must load sIFR from a web server.
  • sIFR movies no longer steal user input focus.
  • When the Flash movie is opened directly, it shows its revision in a bold, italic and normal font.
  • The Flash movie insertion code is equivalent to SWFObject 2.

  • sIFR now supports window resizing.

  • Page zoom in Firefox 3 and IE7 is supported!
  • A workaround has been added for transparent or opaque Flash movies in Firefox/Windows, whose ancestor is floated.
  • A workaround has been added for the loss of Flash variables in Internet Explorer if the innerHTML of an ancestor of a Flash movie is changed.
  • Hover effects tend to get stuck in Flash, added a workaround.
  • Added a custom context menu for links when the hover effect is used.
  • In Internet Explorer, the sIFR Flash movies are hidden when the page unloads. This works around rendering glitches where the old and new movies are briefly shown at the same time.

  • You can specify the font size in pixels, directly from sIFR.replace().

  • You can specify different font sizes within the Flash movie, and they will be relative to the main font size if you specify them with a percentage unit.
  • Added support for bitmap fonts, by rounding the font size to the nearest power-of-eight value.
  • You can now force the text to be displayed on one line.
  • You can disable the pointer cursor (the ‘hand’) inside sIFR.
  • Multiple fonts are supported.

  • You can replace the text inside the sIFR Flash movie.

  • You can change some of the CSS that was applied to the Flash movie.
  • You can get mouse events from the Flash movie.
  • You can tell sIFR to check if the CSS has been applied to the document. This will speed up initialization in Safari and Opera.

  • Opera and Konqueror support has been disabled, because these browsers do not support communication between JavaScript and Flash code.

And there have been many, many more minor improvements. Really, this version rocks!

How Do I Get It?

You can check out a demo of sIFR 3, or simply the most recent version of sIFR 3 from the nightlies. (Note: there are newer versions available than beta 2.) If you want, you can subscribe to a feed for the nigthly releases. Major releases are also announced through the sIFR 3 Announce mailing list.

How Can I Help Out?

First of all, it’s very important to test sIFR in as many scenarios as possible. The easiest way to help out is just to install sIFR on your web site and report any bugs you may find. You can also join the forum or the sIFR 3 Development mailing list. These are also the places to report bugs. Documentation on the wiki could use a lot of improvement. And, of course, a donation is much appreciated.

Support?

Documentation (although, to be honest, it’s not fantastic) can be found on the wiki. The best place to go for support is the forum or the sIFR 3 Development. Please do not post support questions in response to this blog post.

Concluding

I’d like to extend many, many thanks to everybody who filed bug reports, helped testing, responded on the mailing list, improved documentation, spread the word and made a donation. With the year 2008 on the horizon, let’s make it the year of sIFR 3!

(P.S. If you’re curious about sIFR, I’d be more than happy to come give a presentation at your organization!)

Firefox 3, or, technically, Gecko 1.9, comes with support for full page zoom. It works by scaling CSS pixels when the page is rendered – from the perspective of JavaScript or CSS there is no difference between a page zoomed to 200%, 50% or 100%.

I’ve found two ways of detecting the zoom level. One way to detect zoom level changes relies on the fact that percentage values are not zoomed. A percentage value is relative to the viewport width, and thus unaffected by page zoom. If you insert two elements, one with a position in percentages, and one with the same position in pixels, they’ll move apart when the page is zoomed. Find the ratio between the positions of both elements and you’ve got the zoom level. See test case.

The problem with this solution is that it does not detect the zoom level when the page is first loaded.

Another solution I found relies on Flash. Not so much a problem for sIFR, for which I started this little investigation, but not the best solution either. Flash can be configured not to scale with its container page by setting Stage.scaleMode = "noscale";. The Stage.height value gives the height of the Flash movie as its actually rendered by the browser. In other words, this value is not affected by page zoom. The height assigned to the Flash movie <object>, as it exists in the DOM, however, is affected by the zoom level. Simply dividing Stage.height by <object>.height gives the zoom level, even on page load. See test case.

Of course, these two methods are hacks. It would be great if the browser can be queried for the zoom level.

Addendum: I just tested both approaches in Opera and IE 7. The percentages trick works in Opera, but not in IE. IE’s page zoom is quite a hack anyway, so that’s to be expected. The Flash solution works in all browsers, and is far more precise than the percentages approach. Definitely the way forward I’d say!

Browser Wars!

posted December 17th, 2007, 3 comments, tagged

  1. Let browser vendors innovate as fast as they can
  2. With the end-goal of interoperability through standardization
  3. Increasing pressure on other vendors to interoperate, since there are actually four major vendors now, which are all important to most developers

And thereby a better world can be had.

Case in point: Incredibly useful features that used to be IE only, but are now being standardized and implemented by other vendors, such as XMLHTTPRequest, getBoundingClientRect, getClientRects, innerHTML, contentEditable and support for XML/XSLT. Uhm, that basically explains why Xopus is still very much IE oriented – despite all its problems.

Addendum: Proprietary web development with the purpose of standardization is much better than proprietary, period. The latter is inevitable, so what do we do about it?

sIFR 2.0.5 Compatibility Release

posted December 16th, 2007, 23 comments, tagged

sIFR 2′s links do not work with the new Flash Player 9,0,115,0 if a hover color is specified. Additionally, no elements are replaced in Safari 3 if the content type of the XHTML document is application/xhtml+xml. This has been resolved in sIFR 2.0.5.

If you’re using sIFR 2.0.3 or older, and have a hover color specified for links, or are using XHTML documents with the proper content type, you are advised to upgrade immediately. You must upgrade the sifr.js JavaScript code and re-export the Flash movies.

If you’re using sIFR 2.0.4, and are using XHTML documents with the proper content type, you are advised to upgrade immediately. You must upgrade the sifr.js JavaScript code and re-export the Flash movies.

sIFR 3 is not affected by the Flash player issue. Revision 341 is no longer affected by the Safari XHTML issue.

Download sIFR 2.0.5.

sIFR 2.0.6 has been released.

There has been one other change in Flash 9 possibly affecting sIFR users: it will not be possible to use links in a Flash movie loaded from a different domain than the HTML page. I’ve decided not to fix this issue, as the workaround changes the security settings for the sIFR Flash movies. sIFR 3 is not affected by this issue, as it already depends on the workaround.

Detailed Description

In order to support long URLs in Flash 7 and Internet Explorer, when you click on a link in sIFR an ActionScript function is invoked. This function will then cause the browser to follow the link. The anchor element is modified before being passed to the Flash movie to point at asfunction:launchURL,i, where i is an index for the link. Flash 9,0,115,0 still supports clicking on these links, however if a hover color is specified, the link is resolved using an onRelease handler on the parent element of the text field. This handler then tries to follow the link using getURL(). With the new Flash Player it is no longer possible to call an asfunction through getURL(). The code has been modified so that launchURL is called directly.

There’s a serious regression in Safari 3. In XHTML documents the normal properties like document.location, document.cookie and document.body are no longer available. This has been resolved in the WebKit nigthlies, but not in Safari 3.0.4. sIFR 2 had a check for document.body, which I’ve now removed.

See also:

Last Saturday I participated in the Federating Social Networks workshop organized by the fine peeps of Mediamatic (Lab), most especially Ralph. Reduced to its essence, the goal of FSN is to enable websites (“social networks”) to stream data to each other. Rather than pulling data in via feeds and API calls, websites can push data out over an XMPP stream.

One reason social networks may start federating is that feeds don’t scale. Some examples:

  • Blaine Cook of Twitter, who flew over from SF to participate in the workshop, noted that Twitter is getting hundreds of millions of requests through the RSS feed. Apps like Twitterific are limited to one request per client per minute, which is not enough for them to live up to their potential. For Twitter to be able to push tweets to apps such as Twitterific would save them a lot of server load.

  • Jaiku has been blocked a number of times for hitting sites like Last.fm too hard, just to pull in songs. Since Jaiku tries to aggregate a lot of feeds into a lifestream, it’s hitting sites such as Last.fm, Flickr and Twitter constantly. Reversing the aggregation by having Flickr notify Jaiku when a new photo is posted would a) enable Jaiku to display the photo instantly, and b) save Flickr from unnecessary polling.

Of course, federating will have its effect on the business models of these sites as well. For Twitter, one upside of federating is that there’ll be more development around it. Twitterific will be real-time, and will be able to provide many more features. Jaiku will be able to display tweets as if they were jaikus. This may mean that more people start using Twitter, without having to lose their contacts on Jaiku. But, if Jaiku also starts federating, Twitter will be able to display jaikus as if they were tweets. More people may start to use Jaiku rather than Twitter!

What this really means is that boundaries between services start to dissolve. The services will have to be more open, and without lock-in will have to do a better job than its competition. Which, of course, is only good for the end user. And not opening up will only make the services more vulnerable to being out-opened by upstarts.

The protocols to achieve federation are already here today, and Mediamatic is committed to actually get it working. In fact, Mediamatic is in a rather special position where they run several social networks which could greatly benefit from federating, and have obtained a government grant to build a federating system. With Twitter and SixApart also involved – David Recordon of SixApart also flew over from SF – federated social networks have a pretty good chance at becoming reality.

We live in interesting times!

On DOM Load (and CSS, too)

posted December 6th, 2007, 3 comments, tagged ,

The discussion on document load detection is more than two years old. The accepted solution for IE looks pretty much like this:

document.write('<scr'+'ipt defer src="//:" '
  + 'onreadystatechange="if(this.readyState==\'complete\'){dojo._loadInit();}">'
  + '</scr'+'ipt>'

(As you can see I took this from Dojo)

Unfortunately this solution is broken. It relies on the defer attribute, which causes the script to change its readyState to complete after the document has finished loading. defer, however, doesn’t always work correctly. If the innerHTML of a descendant of the body element is changed by an inline script, the script may not be deferred properly. This bug cannot always be reproduced – I’ve seen it occur only a few times while others report it happening on every second page load. However, I did find a way to force its appearance. The bug can be triggered by alert()ing right after changing the innerHTML. See also a test case.

So, without the defer trick, what else can we use to detect DOM load in IE? Diego Perini found a pretty cool solution. If you call document.documentElement.doScroll('left'); before the document has loaded, an exception is thrown. Call it every couple of milliseconds until there’s no exception, and you know the document is ready. Diego has also done a superb job of testing his solution, ensuring that it works as expected.

With IE covered, let’s have a look at document load in other browsers. Mozilla 0.9.4 introduced a DOMContentLoaded event for the document object. Opera 9 also supports this event. Since about two months WebKit supports it as well, but only in the nightly builds. Konqueror is in the same situation, and will support it starting with version 4.0. For WebKit and Konqueror, which do not yet support the DOMContentLoaded event, you can poll the readyState of the document, as explained by Dean Edwards.

We can say that document load is covered (again). However, it isn’t sufficient if your script relies on the CSS being applied. Opera, WebKit and Konqueror load CSS and JavaScript in parallel (all CSS is loaded before being applied, however). This means that the document may have finished loading, and your JavaScript may have executed, before the CSS is fully loaded and applied. sIFR relies on CSS, which means that document load may be too early. Preferably there’d be a CSSApplied event, but in lieu of that, we can detect CSS application ourselves.

The trick is to insert an empty element into the body, which has a style property set in the external style sheet. If you insert this element on document load, and poll every couple of milliseconds until the style property can be retrieved using the computed style, you know that the external style sheet has been applied.

I’ve implemented the hacks discussed in this post in sIFR r339. See also hacks.pageLoad under sifr.js:

function checkStyle() {
  dummy           = dom.create("div");
  dummy.className = CSS_DUMMY;
  dom.getBody().appendChild(dummy);
  pollStyle();
};

function pollStyle() {
  if(dom.getComputedStyle(dummy, 'marginLeft') == '42px') afterStyle();
  else setTimeout(pollStyle, 10);
};

function afterStyle() {
  if(dummy && dummy.parentNode) dummy.parentNode.removeChild(dummy);
  dummy = null;
  fire(null, true);
};

And the CSS:

.sIFR-dummy { 
  width: 0px;
  height: 0px;
  margin-left: 42px;
  z-index: 0;
}

sIFR 3: Updates

posted October 11th, 2007, 40 comments, tagged

Life’s busy as usual, but it’s been too long since I last wrote about sIFR. Using the wonders of modern technology I’ve been writing and posting this from a train speeding towards Rotterdam, where I’ll be dropping by at the Xopus booth at ECM Plaza. Afterwards I’ll be going to a place-supposedly-unknown on a company holiday with Q42 and Xopus. (How cool is that, eh?! We’re hiring, too) So anyway, that’s just to tell you that I won’t be responding to questions in the next few days.

I’ve been working on sIFR on and off, so it’s moving slowly. That’s not to say nothing is happening, though! Since the last post there have been 40 new revisions. As usual they can be found through the nightlies. I’ve also created a RSS feed for the nightlies. This is the best way to stay up to date on development.

Which Version to Use?

An oft-asked question is which revision should be used. Since sIFR is under constant development, it’s always a good idea to use the most recent nightly. Using the feed you can keep an eye on bug fixes. sIFR is beta software. Even though I would totally recommend it over version 2, it is your responsibility to stay somewhat up to date and not miss out on important bug fixes. As usual, installing a new version means replacing the JavaScript file and re-exporting the Flash movie. It’s also a good idea to look at the changes between the revision you’re currently using and the new revision, you may have to change some other things.

Roadmap

So what’s the plan going forward? I intend to stabilize the current code base and release a second beta. After the beta is out I’ll be improving the speed of the code and writing documentation – this will likely result in some refactoring. Then one or two release candidates and we’re good to go for 3.0. I’m not giving dates because I honestly have no idea when this’ll be happening. You can view a list of known issues.

Documentation

The documentation is running behind on the code, for which I apologize. Right now I’d prefer to write code rather than text, so it’ll stay this way for a while. Of course you’re welcome to chip in on the wiki.

sIFR on Tour

Last Friday I gave two presentations about sIFR. One at Onstuimig in Amersfoort (they’re hiring too!) and one at Info.nl. In the presentations I talked about the history of sIFR and explained how version 3 works. You can read Tom Greuter’s report of my talk at Info.nl at their weblog. I had a great time giving these talks and meeting web people here in Holland, so I’d like to do this more often. Let me know if you’re interested!

Links

Some useful links regarding sIFR 3:

The Craziest DHTML Hack Ever

posted August 28th, 2007, no comments, tagged ,

Over at the Xopus blog, I’m discussing what I spent my summer on: a DHTML caret! Go read it and see the demo!

On Guilds and Certifications

posted August 26th, 2007, 13 comments

Last month Peter-Paul Koch wrote about a Dutch Guild of Front-End Programmers he is starting. Its purpose is to further professionalise the front-end programming discipline within the Netherlands and improve web design education. The primary means of doing this is by forming a certification body which will certify individual front-end programmers in the fields of HTML, CSS, JavaScript and possibly Flash. Clients can hire certified developers, which will then improve the quality of their websites.

I believe this is misguided certification is the wrong way of achieving this goal.

In the software field, certifications are typically provided by technology vendors, such as Microsoft and Sun. These certifications are a proof of your skills as an individual engineer, for a particular technology. As Microsoft puts it:

The practical expertise that is gained through the certification process provides individuals with the kind of know-how that gets recognized—on the job, among peers, and by future employers.

(Emphasis mine)

Key here is future employers. Like a formal CS education, a Microsoft-issued certificate helps you land a job. It’s the employers looking for the certification, not the employers’ clients. The certification process acts as an early filter for potential hires. I believe the same will happen to the Guild’s certification process.

Peter-Paul wrote in his introduction to the Guild:

(…) Dutch government recently decided that all national ministry sites will have to comply with the Web Guidelines before the end of 2010. Although they have no requirement to do so, quite a few Dutch governmental bodies on all levels have decided or will decide in the near future to comply with the Guidelines.

They have one huge problem: finding standards-aware front-end programmers.

During an April meeting in The Hague, with various stakeholders of the Web Guidelines present, I heard this complaint not once but three times—and every time most attendees nodded wisely bud sadly. I saw this problem coming back in September 2004, so I wasn’t particularly surprised. In fact, I started to see a possible solution: certification.

There are some very knowledgeable and standards-aware groups within branches of the Dutch government (…) Now suppose that these people would, quite unofficially, advise government branches who want to implement the Guidelines to work only with certified front-end programmers? That would give a certificate considerable value.

In fact, if this strategy succeeds it might mean that web companies that work for the government will employ only certified web developers in three or four years time. Government sites would be created only by people who actually know the web standards. Wouldn’t that be interesting?

When you read the above citations closely, you’ll note that a) the government branches are expected to hire web companies who employ certified programmers, and b) that Peter-Paul expects an underground pressure on these branches to only work with such companies. As the French might put it, coup d’etat.

Of course, government websites will eventually have to comply with the guidelines. They will have to work with web companies that can build compliant sites, and these sites themselves will have to be certified with regards to the guidelines. Hiring a Guild-certified programmer, or more precisely the company which employs the programmer, is no guarantee. When I asked him about this at last Thursday’s Guild meeting, Peter-Paul admitted as much.

The advantage of obtaining the certificate for individuals is better chances at employement. For the employer there is easier filtering of job applicants and a marginal advantage in project pitches. Certification does not increase the quality of Dutch web design education, nor does it necessarily improve the quality of websites. Unlike vendor driven certification programmes, the programme that Peter-Paul is proposing requires membership of an organisation. And since there is no certification in the use of a vendor’s technologies, but in open standards whose proper use is continually under debate, the objectiveness of the certification is unclear.

I do believe there’s a need for a professional web designer’s organisation. Pressure needs to be excerted, and a helping hand extended, to improve the quality of (Dutch) web design and education systems. For the reasons outlined above, I do not believe certification should be part of this.

As Peter-Paul wrote, last October:

The main problem with any kind of Web development certification (as well as Web education) is that the industry is moving too fast for official institutions (with their unavoidable bureaucracy) to keep up. Suppose that today we’d set up certification criteria that all experts would agree to be good; before they’re actually implemented by an official body they’d already be outdated. Even if the official body would somehow move with lightning speed, our certification criteria would still be outdated within a year or so.

I wonder what made him change his mind.

I had to write a depth-first tree iterator recently – at Xopus of course – and I wondered about the call stack size. Here is the test, and the results:

  • Safari 2.04: 100
  • Firefox 2.0.0.6: 1001
  • Internet Explorer 7: 1789
  • Opera 9.22: 3340

Safari surprises me, it’s just 10% of Firefox! Opera stands out quite clearly as well.

I’m going to be in San Francisco July 21st – 28th, and Vancouver July 28th – August 4th. While in San Francisco I’m speaking at the Ajax Experience. If you’re based in SF or YVR or are attending the conference let me know, cause I’d love to meet up.

sIFR 3 Revision 278 Security Update

posted July 4th, 2007, 89 comments, tagged

Newer versions are available. Find out more.

Yesterday, a security vulnerability was found in sIFR 2 and 3. Malicious websites can trick visitors into running JavaScript code on domains hosting sIFR movies. No exploits are known. If you are using sIFR 3, you are advised to update to revision 278 (or any later revision).

Download sIFR 3r278.

You’ll need to update the sifr.js file and re-export the sIFR movies.

Detailed description

sIFR passes the text it has to render to the Flash movie using Flash variables. Normally these variables are specified using a flashvars parameter, however they can also be passed using the query string. Malicious websites can craft an iframe which points to a sIFR movie on the target domain. An HTML link to some JavaScript code can be passed to the movie through the query string. When a visitor of the malicious website clicks on the link, the code is run on the domain the movie resides. Vulnerable browsers are Firefox, Safari, Opera and Netscape. This specific attack does not work in Internet Explorer. An alternative attack is to load the movie directly or in a popup window, this does work in Internet Explorer.

Revision 278 prevents this attack by not rendering any content that is passed through the query string. Credit goes to Arseny Vesnin for finding the vulnerability.

Other changes

Here are the changes since revision 229:

  • Improvements to the Callback API.
  • Improved ratio calculation and handling.
  • No longer replacing elements that have display: none set.
  • Added a transparency and opaqueness option.
  • Increased frame-rate of sIFR movies.
  • Elements with the .sIFR-ignore class will now be visible.
  • Changed semantics of sIFR.prefetch(). When invoked after sIFR.activate() it’ll pre-fetch the given Flash movies in all browsers.
  • Fix for the sticky hover problem.
  • Fixed problem in Internet Explorer when used in combination with (for instance) SWFObject.
  • Anti-aliasing can be specified in JavaScript.
  • Text-wrapping can now be prevented by setting the preventWrap property for sIFR.replace().
  • sIFR will no longer activate when loaded directly from the filesystem. Load it through a webserver instead.
  • Debug mode is automatically enabled if the sifr-debug.js file is loaded (before sifr.js).

As this release has been rushed due to the security issue, I have not been able to update the documentation.

Download sIFR 3r278.

sIFR 2.0.3 Security Update

posted July 4th, 2007, no comments, tagged

sIFR 2.0.5 has been released. Please upgrade to sIFR 2.0.5.

Yesterday, a security vulnerability was found in sIFR 2 and 3. Malicious websites can trick visitors into running JavaScript code on domains hosting sIFR movies. No exploits are known. If you are currently using sIFR 2, you are advised to update to version 2.0.3.

Download sIFR 2.0.3.

You’ll need to update the sifr.js file and re-export the sIFR movies.

Detailed description

sIFR passes the text it has to render to the Flash movie using Flash variables. Normally these variables are specified using a flashvars parameter, however they can also be passed using the query string. Malicious websites can craft an iframe which points to a sIFR movie on the target domain. An HTML link to some JavaScript code can be passed to the movie through the query string. When a visitor of the malicious website clicks on the link, the code is run on the domain the movie resides. Vulnerable browsers are Firefox, Safari, Opera and Netscape. This specific attack does not work in Internet Explorer. An alternative attack is to load the movie directly or in a popup window, this does work in Internet Explorer.

sIFR 2.0.3 prevents this attack by not rendering any content that is passed through the query string. Credit goes to Arseny Vesnin for finding the vulnerability.

Other changes since version 2.0.2

  • sIFR has been disabled for Safari 1.0.
  • If the innerHTML of an element containing a sIFR movie changes, the sIFR movie will fail to render. Upgrade to sIFR 3 to fix this.

sIFR 2.0.5 has been released. Please upgrade to sIFR 2.0.5.

Download sIFR 2.0.3.

Memory Leaks: Gone!

posted June 22nd, 2007, 19 comments, tagged ,

At Xopus we develop a pretty big (~50k lines) JavaScript application to edit XML documents through a WYSIWYG interface. With such a large application there’s a large risk of memory leaks. Indeed, this is what we’ve been experiencing in Internet Explorer 6. We’ve also seen a decrease in performance as memory usage increased. These leaks, however, do not occur in Internet Explorer 7. And, as of just 10 days ago, they no longer occur in IE6 on Windows XP.

On June 12th, Microsoft released security update MS07-033. Included in this update is the following:

General distribution release (GDR) fixes
A memory leak occurs in Internet Explorer 6 when you view a Web page that uses JScript scripting on a Windows XP-based computer (KB929874)

And indeed, IE 6 on Windows XP no longer leaks memory, nor performance. This is great news for all JavaScript hackers, there’s no need to worry about memory leaks anymore!

The one cave-at is that this fix is only available for Windows XP. For Xopus this means we’ll still have to fix the leaks, since some of our clients are still running Windows 2000. Users who haven’t installed the latest security updates will also experience memory leaks in IE 6. In the end though, Microsoft fixed the memory leak issues in Internet Explorer, and I’m thankful for that.

Plazes Survey Results

posted April 23rd, 2007, 5 comments

For a university project we researched location-based services and how they can improve social interaction. As part of the research we conducted a survey under 23 Plazes users, all contacts of mine. The results are from March 12th, 2007.

In short, Plazes is a web service which tracks the location of their users via software applications those users run on their machines, a mobile phone application and SMS “check-ins”. A location is called a Plaze, a location history a Traze. The software application uses the identity of the network router as an identifier for the Plaze.

Here are the questions asked and the results.

  1. How long have you been using Plazes? (Check one)
    (A) 3 years (39%)
    (B) 2 years (30%)
    (C) 1 year (17%)
    (D) Less than 1 year (13%)

  2. What Plazer services do you use? (Check at least one)
    (*) The Plazer application (91%)
    (*) The Phone application (13%)
    (*) SMS (26%)
    (*) None (8%)

  3. How many Plazes did you discover? (Check one)
    (A) 0 – 10 (39%)
    (B) 11 – 20 (4%)
    (C) 21 – 30 (17%)
    (D) 31 – 40 (0%)
    (E) 41 – 50 (13%)
    (F) 51 or more (26%)

  4. How many contacts do you have? (Check one)
    (A) 0 – 10 (21%)
    (B) 11 – 20 (30%)
    (C) 21 – 30 (13%)
    (D) 31 – 40 (13%)
    (E) 41 – 50 (4%)
    (F) 51 or more (17%)

  5. In what situations do you use Plazes? (Check at least one)
    (*) At conferences and other events (17%)
    (*) At home (8%)
    (*) At work (8%)
    (*) Wherever I am (26%)
    (*) I don’t actually use it (17%)

  6. Is your Traze public? (Check one)
    (*) Yes (69%)
    (*) No (31%)

  7. Let’s say you’re visiting a company, and that visit needs to remain a secret. Would you remember to disable the Plazer application before you go online at that location? (Check one)
    (A) Yes, absolutely (40%)
    (B) Most likely (30%)
    (C) No (30%)

  8. Did you consider how your location information could be of benefit to those with malicious intent? E.g. burglars who’ll know when you’re not at home? (Check one)
    (*) Yes (78%)
    (*) No (22%)

  9. Are you concerned about goverment / police access to and use of your location information? (Check one)
    (*) Yes (57%)
    (*) No (43%)

  10. Plazes has placed its servers in Switzerland because of stricter privacy laws. Do you care? (Check one)
    (*) Yes (69%)
    (*) No (31%)

  11. Have you been able to use Plazes to hook up with people? (Check one)
    (A) No, not at all. (26%)
    (B) Yes, but rarely. (69%)
    (C) Yes, all the time. (4%)

  12. Have you ever been approached through Plazes by someone whose intentions you’d consider questionable? (Check one)
    (A) Yes (9%)
    (B) No, why would this happen? (22%)
    (C) No, but I’m aware this could happen (69%)

  13. Would this (question 12) affect your future use of Plazes? (Check one)
    (A) Yes (13%)
    (B) No (48%)
    (C) Not sure (39%)

  14. When you balance privacy issues with the benefits of using Plazes, do you think it’s a good trade-off? (Check one)
    (*) Yes (79%)
    (*) No (21%)

  15. Would you mind advertising targeted at you because of your current location and/or frequent hangouts? (Check one)
    (A) No, as long as it’s relevant (56%)
    (B) No, I don’t care if it’s relevant either (0%)
    (C) Yes, my location information belongs to me and should not be used for marketing (35%)
    (D) Yes, I dislike all advertising (9%)

  16. If you’d decide to stop using Plazes, would you remove your location history? (Check one)
    (*) Yes (52%)
    (*) No (48%)

sIFR 3: Revision 229

posted February 25th, 2007, 55 comments, tagged

Here’s a quick overview of what’s changed between revision 209 and 229:

  • Automatic ratio approximation.

    The font size does not correspond directly to the height of the Flash movie. The relation between the font size, the number of lines and the height of the Flash movie is called the ratio. By defining ratios you’ll stop the Flash movies from abruptly resizing, or at least you’ll be able to lessen the impact. You can now use sIFR.debug.ratios() instead of sIFR.replace() to find an approximation of the ratios. For more information see Ratios.

  • Fixed leading for single-line text.

    If you had specified leading, and the text would occupy only one line, the Flash movie would still have the leading applied. This is now fixed. (Thanks, Marty Stake!)

  • Fixed stuck hover effect (except for Opera).

    The hover effect for links inside the Flash movie would get stuck if you moved the mouse away too quickly. I’ve implemented a workaround, which works in Firefox, Safari and Internet Explorer. No luck in Opera so far.

  • Fixes the movie in Internet Explorer after it’s HTML has been reset.

    If you change the innerHTML of an element which contains sIFR movies, the text inside the movies would get lost. This is due to a bug in Internet Explorer. With the Flash-to-JavaScript callbacks I’ve been able to implement a workaround. As this problem exists in sIFR 2.0.2, this is yet another reason to switch to sIFR 3!

  • Improved callbacks, callback access.

    Flash-to-JavaScript callbacks have been improved. You can now get a reference to a callback object, which for example will let you change the text inside the movie.

  • Domain control is now the same in Flash as in JS.

    You can now use the same value for sIFR.domains in the document and in Options.as.

  • Hide movies until rendered

    There’s now a sIFR.delayCss option which will hide the Flash movies until they have finished rendering. It is only supported in Firefox and Safari, and it’s a bit of a risky setting. In Internet Explorer and Opera the callbacks don’t work if the Flash movie is hidden, in the future this might be the case for Firefox and Safari as well. Use with care.

  • As a bonus: as of revision 230 the Flash movies will show their revision

    If you open the Flash movie directly it’ll show it’s revision number. This is an easy way to verify the version of your Flash movies.

And to conclude with some cool news: Ethan Marcotte, designer and CSS wizard, will be making a really flashy sIFR demo. Thank you Ethan!

You can download r230 and subsequent versions from the nightlies.

Conferencing

posted February 4th, 2007, one comment

I’ll be in Geneva Tuesday – Sunday morning for LIFT. I’m hoping to go to FOSDEM as well, although Nadya and Marco still need to book the hotel. April 18th it’s off to London for Future of Web Design (thanks, Malarkey!) for which I’ll be needing a place to crash on the Wednesday night.

sIFR 3: Revision 209

posted January 19th, 2007, 24 comments, tagged ,

From time to time I’ll be posting about what’s going on in the sIFR nightlies. Today is such a time, with a lot of interesting changes! I won’t go into too much detail as it’s all documented on the wiki, so here’s the list:

  • Support for pixel fonts through the pixelFont argument. See JavaScript Methods: replace.
  • A way to stop the page jumps by specifying the ratios argument. (More about ratios). See JavaScript Methods: replace.
  • Support for fixed font sizes and sizes relative to the main size, see font-size in Styling.
  • Simplification in the API, you can now pre-fetch by passing the movies to activate() instead of prefetch(). See the How to use.
  • Improved pre-fetch code for Internet Explorer.
  • Laid groundwork to communicate with the Flash movies. Test it!.

Ain’t that cool? You can find r209 under the nightlies.

A Word About That Pre-Fetching Improvement

There were a few reports about sIFR overwriting the class name on the body element in Internet Explorer, but only on the first load. This has been fixed in this revision, and here’s the background for the bug:

It only happened on initial load because the issue was caused by the pre-fetch code, which only runs once per browser session. Pre-fetching was done by document.write()ing an embed element to the document. As it turns out this forced Internet Explorer to create a body element. (It behaves the same for any other element which should be in the body).

Results of this are breaking the *:first-child+html IE 7 CSS hack and overwriting class names on the body. After sIFR sets the class name IE won’t use the class names from the real body element anymore.

The solution is to write a script tag, which does not have the effect of IE creating the body element. Other software, such as Mint suffers from the same problem. I’ve already reported this to Shaun Inman, but in case you keep seeing the problem even with this revision make sure the Mint JavaScript is loaded after sIFR has been activated.

Jot’s Alive!

posted January 11th, 2007, no comments, tagged

I just received word that all Jot wikis will be upgraded to version 2.9 next week. A change close to my heart is the addition of RSS feeds to the To-Do Lists app. Good stuff!

Release Notes here.

sIFR 3b1: The Mo’ Betta Beta

posted December 18th, 2006, 118 comments, tagged ,

sIFR 3 Revision 278 has been released. Please upgrade to sIFR 3 Revision 278.

Just in time for Christmas, I present you a new sIFR release! It’s been a long time coming but with this release I feel sIFR 3 is ready for widespread deployment. If you’re unfamiliar with sIFR, the obligatory introduction paragraph goes like this:

sIFR is meant to replace short passages of plain browser text with text rendered in your typeface of choice, regardless of whether or not your users have that font installed on their systems. It accomplishes this by using a combination of JavaScript, CSS, and Flash, which renders the font. It degrades gracefully if Flash is not present.

Compared to sIFR 2 the new version is radically different. Backwards compatibility has been broken, but that’s okay since deployment is now ridiculously easy. There’s great control over how the text is rendered inside the Flash movie: you can easily use bold and italics together, or use different colors. There’s support for leading, kerning and opacity, filters, blend modes and anti-aliasing.

The biggest changes from the alpha release are:

  • No more Flash 7 support. This led to a simplification of the code base and made sIFR easier to deploy.
  • No more compatibility mode. Supported browsers are Internet Explorer 6+, Firefox 1.0+, Safari 2.0+, Opera 8+ and Konqueror 3.5.6 (due in Januari) .
  • Improved Flash detection.
  • Improved rendering in Internet Explorer.
  • Firefox and Internet Explorer will now replace the elements before the page has finished loading. No need to put JavaScript inside the body tag.
  • Fine-grained tuning of the text position inside the Flash movie.
  • Callback handlers to modify the content being replaced, the CSS, and the end result.
  • Support for text transformation, including capitalize.
  • Domain validation on the client side, useful for example to stop sIFR from running when the page is being translated by Google Translator.
  • Various bug fixes.

Documentation has improved, too. There’s now a simple introduction tutorial and the configuration settings and methods are properly documented. No doubt this still needs work, and that’s exactly why the documentation takes the form of a wiki. If you see improvements, feel free to make them.

As I said before, I really feel this version is fit for widespread use. However, it’s still a beta so please keep track of developments, for example by subscribing to the mailing list. If you’re using sIFR please consider making a donation, especially if you’re a company. Here’s a nice button to click:

sIFR 3 is open source and licensed under the CC-GNU LGPL. Check out the demo and go download it! If you want to keep track of newer developments, check out the nightlies. Anything from revision 199 is new. And finally, many, many thanks to everybody who filed bug reports, helped testing, responded on the mailing list, improved documentation, spread the word and made a donation.

sIFR 3 Revision 278 has been released. Please upgrade to sIFR 3 Revision 278.

Port Scanning Without JavaScript

posted November 30th, 2006, one comment

For the past few months I’ve been following the ha.ckers weblog, which talks about hacking websites and otherwise internet-connected systems. In the most recent two posts (part 1, part 2) a way to do port scanning without JavaScript is discussed. This all harks back to early August, when SPI Dynamics discussed port scanning using JavaScript. An often heard counter measure to this technique is disabling JavaScript, but it turns out that won’t be enough.

The scan itself is quite ingenious. It relies on the way Firefox parses the document, which by default is sequentially. First, the page itself loads from the attacker’s server. The time is noted. Then, an image is loaded with it’s source set to a server in the local network, say 192.168.0.1. Once finished, another image from the attacker’s server is loaded, the time is again noted.

Because Firefox finishes loading this image before loading the next, it’s possible to determine the time it took to load the image from the local network. If this is a couple of seconds, one can assume the image request timed out, and the port is closed. If it’s faster than that, the port is open.

This goes to show that the browser security model as it is today is not good enough. One idea would be to disallow resources from outside the local network to access resources inside the network. It sounds good to me, but then I’m not a security expert :)

To close with a comment from RSnake, the ha.ckers’ author:

I think we’ve proven a our point. JavaScript isn’t the root of the problem here. Sure it enables bad stuff, but HTML and the client technologies alone can obviate the necessity for JavaScript alone. Do I think anyone is going to use this technique in reality? Probably not when JavaScript is so prevalent and cross browser compatible. But it is nice to know you can call people out when they claim they are safe because they turn off JavaScript.

Back at Reboot 8.0 Jyri Engeström introduced us to Jaiku. Jaiku is an online presence service: you can post small notes about what you’re doing, either through the website or by texting or using the mobile application. Together with web feeds you can specify this is all combined into a presence stream. It’s much like Twitter, except prettier, cooler and European. And they have Andy!

Today I signed up, and set about automating the process. First off, there’s JaikuAdium by Peter Rukavina. This will set your Adium status to your current presence. Of course, you’ll still have to go to Jaiku to actually update your presence, so we can use some improvement.

Enter some HTTP reverse-engineering. Andy already told me they might inadvertently break the code below, so be warned. This tutorial is also for OS X. Here’s what you need to run in Terminal (you can remove the \ and the line break):

curl -b "jaikuuser_XXXX=username;jaikupass_XXXX=XXXX" \
-d "icon=338" -d "message=Hello+world" --url http://jaiku.com/

The jaikuuser_XXX and jaikupass_XXX are the cookies used by Jaiku which contain your login credentials. You’ll have to retrieve these from the browser. Please note that the XXXX is a long string of letters and digits. The presence icon is identified by a number, I’m not sure of the mapping between the icons and their IDs, so you’ll just have to try. 338 is the exclamation-mark-in-a-text-balloon icon. And finally, message is exactly what you think it is. Take care to properly encode it’s contents though, it shouldn’t include whitespace, ampersands and probably some other characters.

Here’s a Ruby script which updates your presence and Adium status at the same time:

#!/usr/bin/ruby

require 'cgi'

`curl -b "jaikuuser_XXXX=username;jaikupass_XXXX=XXXX" 
-d "icon=338" -d "message=#{CGI.escape(ARGV.join(' '))}" --url http://jaiku.com/`

`osascript /Applications/JaikuAdium/JaikuAdium.applescript`

Here I’ve placed the JaikuAdium script in /Applications/JaikuAdium/. Update the login credentials as mentioned above. Then, call the script with the presence message as it’s argument(s).

Enjoy!

Over the past few weeks I’ve been exchanging e-mails with Bobby van der Sluis, discussing sIFR and UFO. Among the things discussed was DOMContentReady, or more specifically my implementation for Internet Explorer, which was based on the Dojo implementation. Dojo uses the following code:

if(dojo.render.html.ie && dojo.render.os.win){
    document.attachEvent("onreadystatechange", function(e){
        if(document.readyState == "complete"){
            dj_load_init();
        }
    });
}

Whereas the “official” code is as follows:

/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
  if (this.readyState == "complete") {
    init(); // call the onload handler
  }
};
/*@end @*/

The difference may seem minor (readyState on a script instead of the document), but Dean Edwards mentioned in a comment on his site that document.readyState wouldn’t work properly. So, time for some test cases! First of all, domcontentloaded.html logs the following:

  • Safari: changes to document.readyState
  • Mozilla/Opera: firing of the DOMContentLoaded event
  • Internet Explorer: both techniques covered above
  • All browsers: the loading of an image and window.onload

Additionally, an external CSS file is loaded which gives the document a red background color. When the DOMContentLoaded event (or browser specific equivalent) fires, some CSS is written to the page which changes the background color to green. More on this later. The CSS file waits 5 seconds before returning data to the browser, the image waits 10 seconds.

It turns out that in Internet Explorer document.readyState fires after the loading of the image, and right before window.onload. In effect, it’s useless. The “official,” script.readyState variant does fire before the image loads, and is indeed the correct version. In the other browsers the official techniques work properly as well, and the background color of the page is green.

Safari & Style at the Opera

Now, what are we using that background color for? For sIFR it is incredibly important that all CSS has been loaded and applied before kicking into gear. It relies completely on the specified styles for the to-be-replaced elements, without it just won’t function properly. As said before, the first test case showed no surprises with the background color: it appears as if the stylesheets are applied in the correct order. In Safari and Opera, however, this turns out to be false. Enter readstyle-domcontentloaded.html. This test case is virtually the same as the one used before, however on the DOMContentLoaded event it logs the computed background color.

In Internet Explorer and Mozilla it reads out a red background color, in Safari however it reads rgba(0, 0, 0, 0) or transparent. There is also a Flash Of Unstyled Content. In Opera 9 the background color also reads as transparent, but without a FOUC. Webkit.org has an article by Dave Hyatt explaining the issue (as well as the FOUC):

(WebKit) will continue parsing a page even after a stylesheet directive has been encountered so that stylesheet and script loads can be parallelized (compared to waiting for the stylesheet to load – Mark Wubben). That way everything can be ready for display much earlier. (…) when a script attempts to access a property that involves having accurate layout/style information(, shipping Safari’s) will go ahead and lay out what it’s got even though it doesn’t have the stylesheet yet. It will also display it. This means you see FOUC whenever a script tries to access properties like scrollHeight or offsetWidth before the stylesheet has loaded. (…) we will (…) give back information that the page might consider to be incorrect, since the stylesheet that supplies the up-to-date information hasn’t loaded yet.

The article also discusses ways of solving this issue in WebKit itself. The suggested method is stalling the JavaScript execution when it tries to access a style dependent property. Unfortunately as of nightly r17760 this has not yet been implemented. This behaviour also highlights a need for something like a FinishedRendering event that would fire when the stylesheets referred to in the HTML have loaded and been applied. Right now the DOMContentLoaded movement relies quite heavily on this specific browser behaviour, if things change in Mozilla or Internet Explorer without implementing either a FinishedRendering event or stalling JavaScript execution — for backwards compatibility probably the better solution — many modern sites and applications will break.

In the mean time, no more DOMContentLoaded in sIFR for Safari and Opera.

The German Spam Ring

posted November 11th, 2006, 9 comments

Over the past few months I’ve been getting comments on this blog from Germans. Now, that’s great, especially since a lot of them are regarding sIFR. Something smells fishy, however. The sites behind these comments aren’t the typical sites you would expect from web developers, they are quite ugly and don’t seem to serve a real purpose. After seeing yet another weird comment today, I decided to dive into this a bit further.

We are dealing with the following domains and originating IP addresses (intentionally not linked):

  • info-aerzte.de, from 212.23.126.1
  • a-katalog.de, from 217.238.100.199
  • 0am.de, from 212.23.126.6
  • softsensive.de, from 84.139.122.75
  • katulago.de, from 84.139.93.56
  • restposten-zentrum.de, from 89.50.175.104, 89.50.175.82
  • online-reisefuehrer.com, from 84.154.108.236
  • afrika-start.de, from 62.159.35.170
  • erfolgs-werkstatt.de, from 84.142.155.180
  • themenrelevanz.de, from 84.178.2.131

IP addresses nicely listed:

  • 62.159.35.170
  • 84.139.122.75
  • 84.139.93.56
  • 84.142.155.180
  • 84.154.108.236
  • 84.178.2.131
  • 89.50.175.104
  • 89.50.175.82
  • 212.23.126.1
  • 212.23.126.6
  • 217.238.100.199

Here are some sample comments:

Wow, never thought that it was so easy, fortunately we don´t have to work with this version….

Great job! Runs absolut fine with Firefox 1.5.0.7 xp sp2 german version.

The IE7 will display a few of my websites also in a different way than the new FF, but who cares….

Did you try to get the FF20? I tried to download it but the server was busy all the time. I am curious if there are new challenges for the web designer…

regards, Sandra

Most notably there are a number of comments with just one or two sentences, ending in a lot of periods. The level of English is lacking, too. (For the real German readers, I mean no offense!) Some commenters even post follow-ups. Considering how the comments fit the context, they are definitely typed by humans.

Looking through the WHOIS information for above domains gives some surprising results. Domains for which a guy named …… is the administrative contact:

  • info-aertze.de
  • 0am.de
  • restposten-zentrum.de

The administrative contact for the domains above contacted me on December 28, informing me his sites were being spammed without his knowledge. I’ve redacted his name at his request.

Domains for which Werner Kaltofen of Neue Medien Muennich is the technical contact:

  • 0am.de
  • restposten-zentrum.de
  • online-reisefuehrer.com
  • erfolgs-werkstatt.de

Domains with kasserver.com as name server:

  • a-katalog.de
  • 0am.de
  • restposten-zentrum.de
  • online-reisefuehrer.com
  • erfolgs-werkstatt.de

Softsensive.de does not appear to be registered, although it still works. The name servers for the other domains reveal nothing interesting, nor does there appear to be a connection the domains I just mentioned and the katulago.de, afrika-start.de and themenrelevanz.de.

There clearly is a connection in the WHOIS data between six of the ten suspected domains. The other domains appear to be connected through the style of the comments linking to them and the type of website found. It is, however, possible that I’m seeing ghosts. In any case, the links from the suspected comments have been removed, and I’m adding the following to my commenting guideline:

Non-contributing comments run the risk of being removed. Especially if the website seem “fishy”. Spammers, beware.

The Summer I Almost Worked at Google

posted October 31st, 2006, no comments, tagged

Congrats to my friends at Jot for being acquired by Google. Wish I could be there to celebrate :)

iTunes 6 Icons

posted September 12th, 2006, no comments

If you dislike the new iTunes 7 icon set, you can replace the icons with the iTunes 6 versions. Download the set, unarchive, and copy and replace to /Applications/iTunes.app/Content/Resources/.

These icons are copyrighted by Apple Computer. Enjoy!

Plazes: Freed from the Network

posted September 11th, 2006, 4 comments

About a month ago I gave a presentation at Web Monday about Plazes. If you’re unfamiliar with Plazes, it’s a web service which uses a small application on your computer to register the network you are connected to. Information about this network can be stored, this forms a Plaze. For example, right now I’m at the University of Twente Campus Network, which is a huge plaze for every computer directly connected to the university’s network. Effectively, everyone who uses plazes and is connected to the university network will be at this plaze.

Since I gave the presentation, Plazes launched a mobile client. It works by finding the ID of the cell tower your phone is connected to, asks for an address if needed, and gives you a list of plazes in the neighbourhood. Up to you to pick the plaze you are actually at. Even though the promise of connecting to Plazes without using a computer is pretty neat, picking a plaze kinda sucks. And thus, my proposal at Web Monday:

There should be a Plazes client which uses RFID tags to identify the Plaze, and connect automatically.

The way this would work is that you would walk into a house or office, and there would be a sticker on the wall with the Plazes logo. Touch it with your RFID-enabled phone (inspired by the address book desk by Timo Arnall) and you’re good to go. This would also lead to the creation of more plazes, on locations where there might not even have been a computer network in the first place. However, as was quickly pointed out, I believe by Gernot, RFID is still far from ubiquitous, and a better solution would be to identify the plaze by a barcode. The hardware (a camera) and software for this is available today. The Plazes APIs support it as well (although not directly).

All there’s left to do is actually implement it.

I was looking at a bug report for sIFR today. It discussed how sIFR 3 crashed Firefox 1.0.0 on Windows. After the better part of the evening, here’s what I found: if you create a non-anonymous method, declare a variable inside it and set a property on the function object with the same name as the variable (and whatever value, I used null), Firefox 1.0.0 will crash after about ten seconds after loading the page. If it doesn’t, close the window and it should crash right away. It seems to be an issue in the JavaScript parser.

Here’s the code, adjusted for readability, but you bet this fits in one line!

(function nonAnonymousMethod() { 
  var aProperty;
}).aProperty = null;

In the case of sIFR, the parseSelector code contained a similar construct. Fixed now in r130.

Full testing details: Firefox 1.0.0 and Firefox 1.0.1 on Windows XP SP2 with the latest updates as of Sept 8, 2006. Firefox 1.0.0 crashes, Firefox 1.0.1 doesn’t. I therefore assume the problem will not occur in any versions later than 1.0.0. Download old Firefox versions.

Back in .nl

posted September 2nd, 2006, no comments

I’m back in the Netherlands. Thank you Jot for this amazing summer!

Introducing To-Do Lists

posted August 16th, 2006, one comment, tagged

(Cross-posted from the Jot blog)

Hi, my name is Mark Wubben and I’m a summer intern here at JotSpot. Since I came here last month I’ve been working on an internal project named “Jotliner”, which has been released today as the To-Do List application! With the app, you can (of course) create your own to-do lists. The real power, though, is in the way you edit your to-do items. And I don’t mean that you can use the mouse to edit items or re-order them, I’m talking keyboard shortcuts. Tons of ‘em. Quickly move items around, select items, indent, outdent, highlight. Or delete, and then undo, followed by a quick redo. Edit, create new items. Cut or copy, and paste. Check them, and move the checked items to the bottom of the section (called “sifting”). And yes, sections, so you can group items together on one to-do list page. There even is a smart page-lock to prevent someone else from adding items to your list as you’re checking off your completed items. (Although you’ll have to be careful not to edit the same list on two different computers or browsers at the same time). And if you don’t like keyboard shortcuts, everything I just mentioned can be done using the mouse as well.

I could continue talking for a while longer, but you really should give it a try yourself. If you already have a JotSpot wiki version 2.7, which is also know as JotSpot 2.0 and was released July 24th, you can follow the link to “Install applications” down at the bottom of the page. (Make sure you have the permissions to install applications, otherwise it won’t work). This will take you to the Wiki Application Gallery. The To-Do List application is at the bottom of this page. Click “Try it now” to install. If you don’t have the 2.7 wiki, sign up first, and then install.

I would like to conclude this post by thanking David Schontzler, who undertook the original Jotliner project and came up with the interaction model. The app wouldn’t have been here today without his great work. As for me, I’m already working on a different project, which I hope to complete before I leave on September 1st. Stay tuned.

JavaScript Threading: Quick Tip

posted August 2nd, 2006, 9 comments, tagged

JavaScript runs in a single thread, which is why a lot of things, including XHR, work with callbacks. But sometimes, sometimes you need to escape the thread [1] and run you code in a new one. One example is event based code, where your stuff should not run while the event is still being propagated. The easiest way to accomplish this is by using setTimeout():

setTimeout(myCode, 0);

Since JavaScript is single-threaded, and the timeout 0 milliseconds, the timer will fire when there is no JavaScript being executed. In theory it’s still possible for other threads to be started, blocking the timer, but this shouldn’t lead to any practical problems.

Happy coding!

[1]: For sake of argument. Technically it’s all the same thread, and execution would depend on queues and callstacks, but I find it easier to think of this as in different threads.

Here’s something that came up at Jot last week. Which event will be fired continuously, if you hold down a key: onkeydown or onkeypress?

The answer is that it depends on both platform and browser. In general, Windows-based browsers fire only onkeydown, except for Firefox/Win which also fires onkeypress. On the Mac and on Linux, only onkeypress is fired, and onkeydown is not fired on the Firefox browser for these platforms. Something to keep in mind when you bind the “iterate over items while holding arrow up or down key” behaviour to your application. See the testcase.

And So It Begins

posted June 28th, 2006, 7 comments, tagged ,

Tomorrow morning, 9am, final exam of the year. Immediately thereafter I’m off to the parents, carrying a suitcase full of clothes and a backpack filled with tech. I’ve only got a few hours to be with my family, because Friday morning the flight to San Francisco awaits me!

Two months of sunny Bay area, working with – if IM impressions serve, and no doubt they will – the great people from Jot. I’m staying at Stanford, with Kurt Peek (brother of Nadya), who happens to be from Twente University as well, and who’s doing physics research (now that’s way too complicated for me!).

Aside from work, I’m looking forward to meeting old contacts in the Bay area, I’m curious whether Jason Speck wears nice pants, how Dunstan is doing, et cetera, et cetera.

See you all on the other side of the pond. And continent.

P.S. If you happen to have my Dutch mobile number, I won’t be using it until I get back in September. My US number is +1 650 669 2654.

Reboot 8.0

posted June 6th, 2006, 9 comments, tagged

Reboot was a blast. Meeting with people from last year, meeting new people. Talking to the Plazes guys, hanging out in wonderful Copenhagen with Marten and Anne and everyone else we had the luck to run into or meet with. Joyent folks, Opera folks. Copenhagen folks. Like last year, this has been the best couple days of I ever took off.

Like last year, I again find myself very interested into tangible computing. Unlike last year, now I know I have to experiment with this myself. Time is another matter, but it can be made or set aside. And it would definitely be cool to be able to demo something at next years Reboot – that’s a pretty good goal, actually.

Reboot, as a conference, isn’t quite about the latest tech hype, it doesn’t teach you how to build websites or any of that jazz. It’s about people, interactions with each other and their environmnent. And I quite like that. See you there next year.

Photos are online.

Flash? Pure Evil

posted May 29th, 2006, 7 comments, tagged

I’ve been working on adding support for a few hours tonight, when I ran into the problem that eval() in ActionScript cannot compute anything. That’s right, the only thing it is good for is for referencing variables! Like we couldn’t do that in a better way already!

Anyhow, I needed eval() so you can configure filters on the client side. Therefore, my own implementation:

private static function eval(str) {
  var as;

  if(str.charAt(0) == '{') { // Ah, we need to create an object
    as = {};
    str = str.substring(1, str.length - 1);
    var $ = str.split(',');
    for(var i = 0; i < $.length; i++) {
      var $1 = $[i].split(':');
      as[$1[0]] = sIFR.eval($1[1]);
    }
  } else if(str.charAt(0) == '"') { // String
    as = str.substring(1, str.length - 1);
  } else if(str == 'true' || str == 'false') { // Boolean
    as = str == 'true';
  } else { // Integer
    as = parseInt(str);
  }

  return as;
}

Eat that, ActionScript!

Oh, and filters seem to be working. Check out the demo and download the latest nightly. Fun!

P.S. I’m going to be in Copenhagen from Wednesday until Sunday for Reboot. If you’re there, say hi!

Over the past few days I’ve fixed a number of bugs in the code and added some more Flash 8 related features. Here’s a quick post on how to stay up to date. An updated version of the demo distributed with the Alpha release is available.

You can download the latest nightly. The nightlies are similar to the alpha distribution, but you’ll have to re-export the Flash movie and you need to compare the configurations (both in JavaScript as in ActionScript) for changes.

Finally, a list of to-do items for the beta (excluding documentation and testing):

  • Use ExternalInterface when possible, instead of fscommand

  • Handle font and window resizing

  • Add a way to change the font style after the movies have rendered

  • (Test?) support for pixel fonts.

  • Background images inside the Flash.

  • Flash 8 filters.

  • IE focus bug with slow page loading, read more.

Have fun!

sIFR 2.0.2 Final Release

posted May 25th, 2006, 30 comments, tagged

sIFR 2.0.5 has been released. Please upgrade to sIFR 2.0.5.

I’ve just released sIFR 2.0.2, which is a maintenance release for the problems caused by the “Eolas patch”. JavaScript changes from version 2.0.1 are:

  • Problems due to the Eolas related modifications to IE should be solved. Thanks to Christian Welzel for testing!
  • A problem where the CSS class name foo-bar would match foo has been solved.
  • Problems where the browser reports an alien user agent (like GoogleBot) have been solved.
  • Improper detection of Flash transparency support in OmniWeb 5.1 have been solved.
  • You should now be able to export sIFR as Flash 8.

CSS (in sIFR-screen.css) changes are: * No more object tab obscuring the text when AdBlock is being used.

There is still no insight into what is causing these problems, so please deploy this patch to ensure everyone can see the headlines. You’ll only have to replace the JavaScript file (make sure not to override your configuration) and add a CSS rule for the AdBlock workaround.

sIFR 2.0.5 has been released. Please upgrade to sIFR 2.0.5.

Download sIFR 2.0.2.

sIFR 3 Alpha: Tasting the 3.0

posted May 22nd, 2006, 111 comments, tagged

The beta is out! Read more.

It’s high time for a release of the new sIFR version, 3.0. With Inman’s Flash Replacement as the original, sIFR 1.0 making it scalabale and sIFR 2.0 bringing it to the masses, this version will really make things rock. Flash 8 has brought a lot of new, cool stuff. Now you can use these features, starting with way better font anti-aliasing and auto-kerning. You’ll also be able to use filters to do shadow effects (in the nightlies).

For those unfamiliar with sIFR, here’s a short description:

sIFR is meant to replace short passages of plain browser text with text rendered in your typeface of choice, regardless of whether or not your users have that font installed on their systems. It accomplishes this by using a combination of javascript, CSS, and Flash.

Furthermore, it degrades gracefully when Flash is not present or the browser not supported.

For sIFR 3, the code has underwent a complete rewrite, making it easier to use and making it work even better. There also is a new way to render the Flash font. No longer are guesses being made as to how big the font should be, the cues are taken from the font size used in the HTML page and the number of lines the text spans. In order to provide better styling you can now use FlashCSS. No matter how many colors or font styles you want to use, now you can.

Additionally, leading is now supported by sIFR. A number of small issues has been fixed, and you can now pre-fetch the movies for Internet Explorer and Safari. This solves a problem where these browsers would request the same sIFR movie again and again until it was cached – instead of waiting for the first request. Huge bandwidth saver here! And for the AdBlock users, using a little bit of CSS the object tab has been hidden. sIFR is not an ad, and you should see the text itself, without obstruction.

Here’s the full feature list:

  • A new font sizing algorithm, although it needs some attention with regards to padding and wrapping in the Flash element.

  • FlashCSS, both configurable in ActionScript and in JavaScript.

  • Support for Flash 8 movies, for real good looking text.

  • Font resizing in Opera 7+.

  • Support for leading

  • Flash movie pre-fetching for Internet Explorer and Safari.

  • Easier configuration.

  • No more object tab obscuring the text when AdBlock is being used.

By the grand tradition of alpha (and open source) releases, this version lacks serious testing, actual documentation and a number of features. None the less, it is functional, as you can see by the demo.

As you are probably anxious to get started, here’s a run-down of deploying sIFR 3. Please note though that this is an Alpha release: if you run a high profile website and you really don’t need the new features, sIFR 2 is best for you. If you like playing around with new tech, please give this version a try. The more real life use, the more improvements can be made.

That’s it for this release. I’ll write more about the beta release in another post. Please give the new Alpha a try and let me know what works and what doesn’t. You can also subscribe to the mailing list. Now, without further ado, to the download!

A Jot Summer

posted May 17th, 2006, 7 comments, tagged

Barring any suprises from the American Consulate, I’ll be doing a summer internship at JotSpot this July and August (Rock!) California, here I come!

Beers with the Dojo Guru

posted May 13th, 2006, 15 comments, tagged ,

Alex’ flight went haywire, so the meeting is off.

Alex Russell is coming to Amsterdam for XTech, so here’s your chance to drink a beer with the guy! Suggestions for locations are welcomed (I hardly know Amsterdam), suggested time is 20:00.

We’re going for dinner at 18:00 on Monday or whereabouts at Café de Jaren, and onwards with the beer from there.

See you there!

P.S. I’m contemplating going to Ajax day, should I?

sIFR and Eolas, Testing the Patching

posted May 11th, 2006, 4 comments, tagged

While working with Microsoft to fix the mentioned problems with sIFR and the Eolas patch (the security updates for Internet Explorer released on April 11th) I’ve also been working on a patch for sIFR itself. But before I get to that, some updates on the problem:

  • Everyone who reported the problem and whom I have asked to verify, is using either Flash MX 2004 or the Flash 8 editor.
  • This might mean that the problem is related to the editor being installed
  • Or only people with the editor take the trouble to contact me.
  • Reinstalling Flash fixes the issue.
  • Using <object> instead of <embed> prevents the issue.

It still is not clear what the exact conditions are for the problem to occur, but most likely it’ll be solved when people update their Flash players… in a few years, that is. Therefore, the patch to sIFR.

This patch, bringing sIFR to version 2.0.2 has the following fixes:

  • Problems due to the Eolas related modifications to IE should be solved. Thanks to Christian Welzel for testing!
  • A problem where the CSS class name foo-bar would match foo has been solved.
  • Problems where the browser reports an alien user agent (like GoogleBot) have been solved.
  • Improper detection of Flash transparency support in OmniWeb 5.1 have been solved.
  • You should now be able to export sIFR as Flash 8.

A test version of the patch is available: Download sIFR 2.0.2 TEST.

Please let me know how this version functions. Nothing should be broken in other browsers, but of course you never know. Once I’ve had enough feedback I’ll officially release the patch.

sIFR 2.0.2 is out now

On sIFR and the Eolas Patch

posted May 2nd, 2006, 9 comments, tagged

After Microsoft rolled out their Eolas patch I got a few reports about sIFR not working correctly. As sIFR is a script which creates the Flash movies there shouldn’t be any issues, but unfortunately that is not the case.

There are two problems, one is easy to fix and is the result of extra steps taken in the deployement of sIFR. The other seems to be a system issue, and as such is harder to fix.

First, the easy problem. Using the sIFR code in the page itself causes the sIFR Flash movies to require activation:

You cannot write script elements inline with the main HTML page to load your control externally. If the script is written inline programmatically, for example with the writeln function, the loaded control will behave as if it was loaded by the HTML document itself and will require activation.

- source

Using sIFR inline is not advised at all, nor shown in any demos or documentation, so I don’t expect this to be a problem. However, when the sIFR code is evaluated using eval(), IE thinks the code is inline. This causes the need for activation. sIFR is not distributed with eval() evaluation of it’s source code, so if you are using a version which is, please dowload and use the official release.

Testcases for this problem are available.

The other problem is that, for some users, the patch broke sIFR rendering for both the 2.0 version as the sIFR 3 demo. Instead of showing the Flash text, a non-loaded image icon would be shown. I do not know what causes this issue, but I’m in contact with Microsoft about it. I cannot replicate the problem myself, but there have been six reports. If you see this problem, please contact me with the following information about your system: OS, Flash version, used Flash versions in the past, installed IE 7 betas, installed Eolas-patches (aside from the one rolled out in Windows Update).

I have heard that reinstalling the Flash Player fixes the issue. However, I would greatly appreciate it if you could hold off on doing this until Microsoft has more insight in what is causing the problem. Thanks!

Reviewing the Plazes APIs

posted May 1st, 2006, one comment

Plazes is an extremely cool location awareness service. Recently the first version of the Plazes API was released. Together with the Launcher and WhereAmI APIs there now are three public APIs for the service. As a hobby project I’ve updated the RubyForPlazes project, run by Peter Rukavina, to work with the new API. The new version hasn’t officially been announced yet, as there is still some work to do with regards to testing and examples, but it is already in the repository at Peter’s site. During the development of the new Ruby implementation I’ve run across a number of issues with the API design, which are documented in this article.

Please note that the properties referred to below are as used in the Ruby implementation, not necessarily the Plazes APIs.

Plaze? What Plaze?

One problem is that all three APIs return different information about a Plaze, and with different property names for equal values. The data returned by all APIs, with normalized names:

  • key: Identifier for the Plaze
  • name: Name of the Plaze
  • city: City where the Plaze is located
  • country: Country where the Plaze is located
  • latitude: Latitude for the Plaze
  • longitude: Longitude for the Plaze
  • url: URL for the Plaze on Plazes.com

The WhereAmI API doesn’t return more information than this. Actually, it returns even less. The Plaze key is not returned, but it is part of the URL.

The Launcher API returns:

  • timezone: Plaze timezone
  • feed_url: URL to the RSS2 feed for this Plaze

And finally, the Plazes API:

  • street: User entered address for this plaze. Includes street number or other address parts when applicable
  • state: User entered US or Australian state for this plaze
  • zip: User entered ZIP code for this plaze
  • blog_url: User entered URL for this plaze, can be empty

I’d love to see the timezone and feed_url added to the Plazes API. feed_url by itself isn’t enough, as it returns an RSS 2.0 feed while RSS 1.0 and Atom 0.3 (which is deprecated) are available as well. The feed URIs are predictable, though, and the Ruby implementation supports this. And while it is annoying that the property names differ between APIs, it is easy to abstract this away.

Users

User information is slightly different between the three APIs as well. You could say there is a base user, which has:

  • key: Identification key of the user on Plazes.com
  • name: Name of this user.

This is the minimum of user information returned by the plaze.comments and plaze.photos methods of the Plazes API. The base user is an abstraction, though. The mentioned methods return the values as user_key and user_name properties on the general result.

Then there is a normal user, returned by the plaze.people method of the Plazes API and which properties are shared by the other methods described below:

  • thumbnail_url: URL to a thumbnail picture of the user on plazes.com. Always set, can point to a dummy image
  • url: URL to the users profile page on plazes.com
  • blog_url: URL the user entered as her blog, can be empty
  • message_url: URL on plazes.com where internal messages can be sent to that user

user.friends returns:

  • plaze: The current plaze of the friend. Only available if the friend is online.
  • distance: The distance of the friend to your current plaze. Only available if the friend is online.

Note: The actual result from the API contains a normal user, a Plaze and the distance. Since the distance and Plaze are properties of the friend I’ve implemented the result as one object.

Finally, the Launcher API has a plazes.buddies method which returns a normal user and it’s vicinity to your location.

It’d be great if the result from plaze.people is the same as the result from user.friends, especially with the distance. An online property would be handy too, currently this has to be inferred from the presence of other properties and the way the user object is requested. The plazes.buddies method could return the same information and drop the vicinity, as this can be derived from the distance. In fact, since the Launcher API requires a special API key, this is what’s done in the Ruby implementation already. Furthermore, there should be a user.info method which returns this information for a given user key.

Other data types

There are other data types available in the APIs, which have a good definition. I won’t go into detail about these types but you can find them in the Ruby implementation.

Concluding

The Plazes APIs need some clean-up, especially when it comes to plaze and user information. The API results refer to abstract structs, such as the user and plaze struct, instead of extending from them. Extending makes more sense when you are using the API.

It would be useful if the next version of the Plazes API would supersede all three existing APIs. An updated Launcher API would be welcome as well, looking at the recent Plazer apps it can be concluded that this API already exists, but is private.

Pre-fetching Flash

posted April 29th, 2006, 10 comments, tagged ,

One of the advantages of sIFR is that the Flash files are cached on the client, saving bandwith. Except, of course, when they aren’t cached, as was pointed out on the sIFR forum. Luckily a solution was hinted at by Marc van den Dobbelsteen from Webbforce. Requesting the Flash file using a document.write() call fixes the problem. Thus, I set out on a journey through seemingly endless browser reloads and cache emptying…

Diagnosing the problem

When sIFR starts replacing elements it adds Flash movies to the document in a very short time. The browser needs to request these movies, which often have the same source. Testcases show that both Internet Explorer and Safari will stupidly request the same source for each movie, until the first request comes back. Upon reload (when the source is in the cache) no further requests are made.

Here are some log files:

# IE
“GET flash-caching/no-caching.html HTTP/1.1″ 200 511
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

What’s interesting here is the 206 response. It seems IE isn’t happy with requesting the file and asks for it again, but this time it wants a partial file. For the five equal Flash elements on the page, seven requests are made.

# Safari 417.9.2
“GET flash-caching/no-caching.html HTTP/1.1″ 200 511
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

Safari is a bit smarter, but still requests the same file five times.

Fixing the problem

Loading the Flash file once, before other requests are made, stops the user agent from requesting it too many times. This gives us a clue for fixing the problem. In Safari we request it through a script in the head:

new Image().src = 'test.swf';

But alas, this doesn’t work in IE. Here’s the IE code:

var head = document.getElementsByTagName('head')[0];
var node = document.createElement('embed');
head.appendChild(node);
node.setAttribute('src', 'test.swf');

You might want to remove the embed element later on, just to keep the DOM tidy. Also note that to be certain the file has been loaded, you need to load the other Flash movies after onload.

Now we have the following in our logs:

# Safari 417.9.2 Prefetching
“GET flash-caching/caching-safari.html HTTP/1.1″ 200 1014
“GET flash-caching/test.swf HTTP/1.1″ 200 8249

# IE Prefetching
“GET flash-caching/caching-iexplore.html HTTP/1.1″ 200 1057
“GET flash-caching/test.swf HTTP/1.1″ 200 8249
“GET flash-caching/test.swf HTTP/1.1″ 206 8249

That’s quite an improvement! Safari requests the file exactly once, while IE is stubborn and requests it twice.

Implementing the fix in sIFR 3

A slightly more specific version of the code above is used in sIFR 3. You can pre-fetch files using sIFR.prefetch('one.swf', 'two.swf', 'etc.swf'). This works pretty well, except that in Internet Explorer sIFR is allowed to kick in before the document has fully loaded.

A trade-off has to be made. Let’s say that the first time the website is loaded, sIFR is not allowed to kick in before the document is fully loaded. The next time it is, since the files no longer have to be pre-fetched. Determining whether the site is loaded for the first time is done through a session cookie. This brings us to the following situations:

  • The user visits the site for the first time. The Flash movies are pre-fetched and sIFR will only kick in on page load.
  • The user navigates to another page on the site. No pre-fetching takes place, the Flash movies are loaded from cache and sIFR kicks in right away.
  • The user has cookies disabled. The Flash movies are prefetched for every page load, but will come from the cache after the first load. Depending on the load time for the page there are extraneous requests for the first load. sIFR does kick in right away.

Of course the cookie path and the use of the cookie itself can be configured.

This fix makes sIFR respond faster and saves bandwidth. And of course, Internet Explorer and Safari can be are a pain to work with.

This problem is now known as bug 8659 in the OpenDarwin bug tracker.

Yesterday I was hunting down an Internet Explorer memory leak at the office. I started with disabling all events which might leak, and then began to check them one by one. With a stroke of luck the first event I checked caused the biggest leak, which was several megabytes on each page load. The event wasn’t to blame, though.

In fact the real bug was something much harder to grasp than the closure-induced leak. And, since I couldn’t find it described elsewhere, I’ve decided to coin it the Externally Declared Global Variable Leak. It has been confirmed in IE6 and IE7.

Here’s the problem in a nutshell:

  • When you create circular references between window objects
  • And declare global variables (someWindow.globalVariable) on window object A, from window object B (or vice versa)
  • These variables will leak
  • Even if they are not referenced

There are a number of possible solutions:

  • Prevent or clear the circular references
  • Declare the global variables inside the window object
  • Use eval() to declare the variables from another window
  • Remove the variables

I’ve created three testcases, one leaking, another one leaking, and a non leaking one. For fun I’ve added a ~ 20 MB object to the tests, so the leaking will be rather obvious.

As you can see in the tests the leak occurs if the global variable is declared in the main window. Declaring the circular reference variable – which is global as well – in the iframe window has no effect. This leads me to believe the JScript engine thinks the leaking variables are still referenced, because they were declared by another window object.

So, yet another thing to look out for when developing for IE. Happy leak hunting!

Anne, Marten and I are going to be at Reboot 8.0 this year. See you there!

In related news, BarCamp Amsterdam II has been announced. See you there as well!

DHTML as a Straw-man

posted March 29th, 2006, 11 comments, tagged ,

Christian Heilmann of the DOM Scripting Task Force published a long article called From DHTML to DOM Scripting today. In it, he claims that DHTML is awful and outdated and generally very bad, and that DOM Scripting is the second coming of Zarquon.

Okay, that was slightly exaggerated. Yet this line of thinking annoys me. DHTML is, like HTML before it, painted in a bad light, whereas XHTML and DOM Scripting are purportedly the best (and therefore only) way ahead. When you look more closely at the practices so easily associated with XHTML and DOM Scripting you see a different picture. Quoting from the weblog post:

DOM scripting assets:

  1. Progressive Enhancement – check if things are available, then apply those dependent on them
  2. Ease of maintenance – keep the maintenance as easy as possible via dynamic CSS classes and properties at the beginning of the script
  3. Separation of Presentation and Behaviour – add dynamic classes instead of changing the style collection
  4. Separation of Structure and Behaviour – use dynamic event handlers and generated elements instead of onclick and NOSCRIPT
  5. Using modern event handling – more than one onload please
  6. Avoiding clashes with other scripts – avoid global variables and encapsulate functions as methods in an object

That sounds quite reasonable. In fact, I’d say these are all very good programming practices, regardless of being used in “DHTML” or “DOM Scripting” scripts. The problem is that DHTML, and all of it, is being blamed for blatantly ignoring these practices. I consider myself to be a DHTML programmer – heck, I learned most of my stuff at the now dead DHTMLCentral – and yet I grew up to these practices. While I am aware of the type of DHTML hinted at by the DOM Scripters, virtually all of the provided scripts on DHTMLCentral fit the description, it’s not the DHTML I, and many others, know and write.

Seeing DHTML portrayed as evil pains me. It does not deserve to be used as a straw-man for good practices. I believe good practices should stand on their own merit, and the ones the DOM Scripting Task Force attempts to spread are perfectly capable of doing that.

sIFR 3 Feature List

posted March 12th, 2006, 19 comments, tagged ,

As I’ll have to focus on course-work and money-making-work in the next few weeks here is a feature list for sIFR 3. Of course, when something turns out to be too hard to implement, it’ll be removed from the list. Don’t take it for granted!

  • Better and easier font sizing, as explained in Font Sizing with sIFR.
  • FlashCSS: This lets you use multiple colors and different text styles in general. It also aids with link styling. Todd Dominey has a list of supported properties.
  • Controlling kerning, leading and line-height.
  • Font resizing.
  • Better FlashBlock support (actually this’ll be a side-effect of the font resizing).
  • Possibility to ignore specific elements during replacement.
  • Modification of the content passed to sIFR.
  • Pixel fonts.
  • Flash 8 filters.
  • Background images inside the Flash.

sIFR itself will be easier to use, and there’ll be a number of small fixes for edge-cases. Speed improvements are high on the list as well, obviously.

Font Sizing with sIFR

posted March 7th, 2006, 14 comments, tagged ,

Font sizing is the most difficult part of using sIFR. In this post, I’ll discuss how the algorithm in sIFR 2 works, or rather, why it doesn’t work as well as originally intended. Coincidentally, this makes this post an excellent tutorial for font tuning in sIFR 2. We’ll also look at how things will be improved with sIFR 3.

sIFR 2

The algorithm used by sIFR 2 is relatively simple:

  1. Take the width and height of the element you want to replace.
  2. Create a Flash movie which uses these dimensions.
  3. Increase the font size from 6 pixels, pixel by pixel, while rendering the text, until the height of the text is larger than the Flash movie.
  4. Render the font with a size one pixel smaller than obtained in (3).

Unfortunately this doesn’t quite work. First of all: the font used in the Flash movie may be wider or smaller than the font used in the HTML. Let’s say the font is wider, what will happen then is:

  • Because the font is wider, it will wrap to a new line earlier than the HTML font would.
  • This allows for the possibility that the text will reach the allowed height earlier, resulting in a smaller font size (see point 3 above).

A way to deal with this is to set the letter-spacing of the HTML text so the font appears to be wider, having the effect of giving it the same width as the Flash font. This works, assuming that there are no other factors influencing the width and height of the text, such as (unintentionally) using different fonts on different user machines. Default browser settings for CSS properties like font-size or letter-spacing can be of influence as well.

In short, achieving cross-browser and cross-platform consistency of the width and height of the text, while taking into account the width of the font used, can be very, very hard.

sIFR 3

In sIFR 3, things will work a little different. For starters, the font size is no longer determined by the width and height of the text. Instead I’ve figured out a way to derive the font size from CSS. Here is a short version of the rendering algorithm employed by sIFR 3:

  1. Determine font size and the number of lines the text takes up.
  2. Create a Flash movie using the width and height of the text, these can be changed later.
  3. Render the text.
  4. If the text is wider than the allowed width, try wrapping it to the next (or a new) line.
  5. If a single word is causing the text to be too wide, resize the movie.
  6. If the text now requires more lines, resize the movie.

Resizing the movies requires Flash to talk to the JavaScript code in the website. Since we’ve dropped Flash 6 support, this has now become possible.

Specifying the desired font size in Flash is quite easy as well. All it takes is two properties!

.sIFR-hasFlash h1 {
  font-size: 12pt;
  line-height: 1em;
}

Why the line-height? sIFR uses the line-height and the height of the element itself to calculate how many lines the text uses. By using line-height: 1em; we ensure it has the same value as the font size. As Eric Meyer said:

(…) for line-height, em-based values are calculated using the computed font-size of the element itself. I declared the font-size directly, so we know its computed size in pixels.

It also turns out that if you specify line-height in ems, and don’t specify it in the descendent elements, they will have the same line-height:

So that computed value (…) is what’s passed on to the descendent elements. (These) elements will inherit (the same) line-height value. End of story. They don’t change it based on their own font sizes; in fact, they don’t change it at all (sic).

Concluding

sIFR 3 will bring easier font tuning, as well as other features I’ll talk about later. You can see the original font tuning experiments, as well as a demo of the current stable SVN export.

Evolution

posted February 20th, 2006, 15 comments, tagged

sIFR now does multiple colors, configured by CSS, and with better links. And it’s still missing a lot of features, and is probably incredibly buggy, but hey.

http://dev.novemberborn.net/sifr3/svn/test/demo.html

Next step: making it *scalable*.

February 21st (in the wee hours): Now with a hand-tailored word wrap algorithm!

Caught Redhanded: QuickTime Stealing Flash

posted February 8th, 2006, no comments, tagged ,

Sometimes QuickTime may take over handling Flash content. As QuickTime is only compatible with Flash 5, this can pose major problems. Here’s some code which detects if QuickTime is handling Flash:

var qtFlash = false; // Is QuickTime rendering Flash?
var flashPlugin = navigator.plugins['Shockwave Flash'];
for(var i = 0; i < navigator.mimeTypes.length; i++) {
    var mime = navigator.mimeTypes[i];
    if(mime.type == 'application/x-shockwave-flash' 
    && mime.enabledPlugin != flashPlugin) {
        qtFlash = true;
        break;
    }
}

[Update April 28, 2006] If there are multiple Flash versions installed this test won’t work. Instead try this:

var qtFlash = false; // Is QuickTime rendering Flash?
var flashVersion = 7; // Stub for real detection code
for(var i = 0; i < navigator.mimeTypes.length; i++) {
    var mime = navigator.mimeTypes[i];
    if(mime.type == 'application/x-shockwave-flash' 
    && !new RegExp('.*Shockwave\\sFlash\\s' + flashVersion
    + '.*').exec(mime.enabledPlugin.description)) {
        qtFlash = true;
        break;
    }
}

If you want to test this see these Mozilla guidelines and apply them in reverse order.

This workaround doesn’t work in IE, though, which leaves me wondering how to detect QuickTime for that browser. Here’s the Flash detection code for IE in sIFR:

if(this.ieWin) {
  try {
    this.flashVersion = parseFloat(
                new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7')
                .GetVariable('$version').match(/([\d,?]+)/)[1].replace(/,/g, '.')
            );
  } catch(e) {}
}

I haven’t been able to get IE to use QuickTime, so the question is: will IE be able to create a Shockwave Flash object if QuickTime is handling Flash? If not, problem solved. If so, any ideas on detecting QuickTime?

Getting Funky With Scopes and Closures

posted January 22nd, 2006, no comments, tagged

The most powerful thing about the JavaScript programming language are it’s scoping rules. In programming languages, scope is what makes the world go round. It defines the context in which the code executes. The context contains objects which are available to the code. To draw a real-world analogy: Say you’re in your living room, and you want to watch some television. You can, because the television is in your living room. But what if you’re in the kitchen?

Scopes can be chained to each other. This means that if some portion of the code is not available in the current scope, you’ll be able to find it somewhere else. If you’re in the kitchen, and it is chained to the living room via a door (as in my place), you can simply walk to the living room to watch television.

In JavaScript there is a variable scope chain. All objects are stored inside variables (and in JavaScript everything is an object). New scopes can only be created when you define a new function. The newly created scope will be chained to the scope it was created in. And for good measure there is a global scope, available right from the start. In most JavaScript implementations (read: browsers), this is the window object.

As I said, in JavaScript everything is an object. You could view the scope as an object as well: the variables you use are simply properties on this object. Normally you can’t access this scope object, it is implied by the language and everything is handled by the interpreter. There is, however, one exception to this rule. The window object acts as a public scope object. Let’s have a look:

var foo = 'bar';
window.baz = 'thud';
alert(window.foo); // alerts 'bar'
alert(baz); // alerts 'thud'

As you can see the foo variable is accessible by window.foo, just as the window.baz property is accessible by baz.

So how do we find the variables on the scope chain? First the JavaScript interpreter checks if the variable has been declared in the current scope (or: if the property exists on the hidden scope object). If not, it’ll check the chained scope. This process continues until the variable has been found, or until the window object has been reached and the variable hasn’t been declared even on that object. In this latter case an exception will be thrown.

This process makes it possible to use the same variable name in different scopes, effectively masking the other variables. There is one caveat in JavaScript though: unless you declare the variable with the var keyword it will be created in global scope.

By now it’s time for some examples:

var foo = 'bar'; // variable declared in global scope
function a() { // creates a new scope, chained to the global scope
    alert(foo);
}
a(); // alerts 'bar'

function b() { // creates a new scope, chained to the global scope
    foo = 'the quick brown fox'; // in which scope does the `foo` variable exist?
}
alert(foo); // alerts 'bar'
b();
alert(foo); // alerts 'the quick brown fox'

function c() {
    var foo = 'thud';
    alert(foo);
}
alert(foo); // alerts 'the quick brown fox'
c(); // alerts 'thud'
alert(foo); // still alerts 'the quick brown fox'

Of course we can chain more than two scopes together:

function d() { // what scope is this function chained to?
    var foo = 'thud';

    function e() { // creates a new scope, chained to the scope of `d()`
        alert(foo);
    }

    e();
}
d(); // alerts 'thud'

Function e() has access to the scope of function d(). This is called a closure. As I said this is one of the most powerful things of JavaScript. This lets you do some really cool things. Ever wanted to create private methods in JavaScript? Now you can!

function A() {
    function hidden() {
        alert("I'm a private method!");
    }

    this.visible() {
        alert("I'm a public method. I can also invoke `hidden()`.");
        hidden();
    }
}
var a = new A;
a.visible();

Up to you to figure out why this works.

This also lets us create Singleton objects:

var b = new function() {
    this.hello = function() {
        alert('Hello world!');
    }
}
b.hello();

Or functions which have private methods in their scope:

var c = (function() {
    function hidden() {
        alert("I'm a private method!");
    }

    return function() {
        alert("I'm a public method. I can also invoke `hidden()`.");
        hidden();
    }
})();
c();

This latter technique is referred to as Block Scope.

One thing to watch out for with these techniques are memory leaks. You can find more info about those by reading my Event Cache article. In any case, let that not be a discouragement! Closures are cool. Use them.

Sources Are Telling Me…

posted January 17th, 2006, one comment, tagged ,

I’ve made a development subdomain here at Novemberborn to host SVN exports of the sIFR 3 codetree and other development projects.

Current projects:

sIFR 3

The codetree contains the latest version with test cases. If you find bugs, please send a message to the list.

ParseSelector

This is a rewrite of the parseSelector() method used in sIFR 2. This version is extensible and works a lot better. It’s partly based on Dean Edwards cssQuery. Because it’ll be used in sIFR I decided to write my own implementation instead of using cssQuery, which is more advanced than what sIFR needs.

11born-wiki

This is the current code for my wiki server. It’s released under the same license as i2, which as far as I can tell is the MIT license (there is no license notice in the trunk, but it’s in the Rails repository and Rails is licensed under MIT).

Because of this license I’ve removed my stylesheet and the images specific to my site. I rather not have someone duplicate the design because the stylesheet was released as open source.

I’m providing the source because I hope it’ll be useful to you. I’m not, however, going to maintain it or provide support. I simply do not have the time for another open source project. If somebody picks it up (or merges it into i2 itself, even) that’d be great.

Have fun!

TextMeet February 4th, Utrecht

posted January 16th, 2006, 2 comments, tagged ,

If you’re in Holland in early February, Utrecht is going to be the place to be. Marten and me are setting up a TextMeet: the event of the year where you can meet your fellow TextDrivers. And, shocker, the Dean is coming! Jason Hoffman might fly over as well. As I said, the place to be!

So get your agenda and write it down: 1400 hours, Utrecht, Central Station (at the meeting point, or in Dutch: trefpunt). You should probably keep the night free as well, for we’ll be eating taco’s for sure.

Till the 4th!

Quick: A Wiki!

posted January 14th, 2006, 8 comments, tagged ,

I’ve updated the wiki software running wiki.novemberborn.net to a customized version of i2, the app behind the Ruby on Rails wiki. At first glance it’s a lot more responsive, and I hope it won’t go down as much as the previous incarnation.

Currently it’s being run off Webrick, which is set to be replaced by a lean Lighttpd server. Some problems however are preventing this right now. I’ll write about the new app later when these issues are resolved. I’ll also publish the source then.You can find the source in this post.

Anyway, that’s what I’ve been busy with the past week. Before that, during the winter break from university I’ve been working, hence the slow updates here. But with the wiki out of the way, it’s finally time to focus on sIFR 3 :)

Talk to you again soon,

2005 Randoms

posted December 31st, 2005, no comments

We’re nearing the end of the year (less than twelve hours in my time zone) and it has become time to write down some final random notes.

  • There’s some interesting things going on with regards to tuning in sIFR 3, see these experiments. Of course there’s more info on the mailing-list.

  • If you hang out at the coolest webhosting company around (a.k.a. TextDrive), there’s a TextMeet being organized next week in Utrecht. See the forum topic for more info.

  • Laurent Haug, whom I met at Reboot last June, is organizing his own conference in Geneva early February. I won’t be able to make it, but check it out: Lift06.

And with that, all the best for 2006!

Back It Up Back It Up

posted December 22nd, 2005, 9 comments

As you are hopefully aware, backups are the only way to make sure you don’t lose your data in case of a crash or theft or an other disaster. There are several ways to back up your data, such as writing it to a tape-recorder stuck away in a fireproof vault.

Luckily there are easier methods as well.

My personal strategy is as follows: I keep one full backup of the PowerBooks hard drive on an external Firewire hard disk. Furthermore, I upload my documents and work (anything I code, basically) to Strongspace.

There are two programs I know of for the Mac which do full disk backups. One is Carbon Copy Cloner (CCC), the other is SuperDuper. I’ve used CCC for a couple of months, and I’ve recently bought a SuperDuper license. Whereas CCC seems to be aimed at users who know what they’re doing, SuperDuper is clearly for the folks who don’t and/or want assurance that the backup is really working. The interface is clear and consise, and the User’s Guide (pdf) is useful even if you don’t use SuperDuper. Please note that the risk of buying the application after reading the guide is significant. You’ve been warned.

My Strongspace backups are done by means of a combination of Ruby, rsync and SFTP. You can download the script here. It will allow you to upload specific directories, and it lets you exclude files and directories on a per directory basis. It will also remove files from Strongspace which no longer are on your disk. You can follow instructions on setting up a scripted backup at Invisible: Back-up. Instructions on my script are in the script itself.

And finally, there’s scheduling. You need to schedule your backups so you won’t forget making them. Please do as I say and not as I do, because I don’t schedule mine. I usually make a full backup before I take the PowerBook with me on a trip longer than the bycicle ride to university. If I’m not travelling for, say ten days, I’ll also make a backup. The Strongspace backups are made at least once a day, usually before I hit the sack. This means the latest stuff will have been backed up, no matter what time I go to sleep.

Luckily I haven’t needed my backups yet, but at least I’m prepared. I hope you will be, too.

sIFR 3: A Look into the Future

posted December 16th, 2005, 62 comments, tagged ,

Some time ago Mike and I asked you what features you wanted to see in sIFR 3. While we haven’t defined the exact list of features for the new version it has become time to look into the future to see where sIFR is heading.

As you probably know Mike is now the CEO of Newsvine, and as such he’s pretty occupied. The result of this is that the actual development of sIFR 3 is resting fully on my shoulders. Since I’m no super hero, I’ve been thinking about how the development process should be approached and how you can help with it.

sIFR Wants You

sIFR is meant to work in as many browsers as possible. Being a fully degradable add-on, it should not break sites when a browser has trouble supporting it. To accomplish this goal a lot of testing is needed. Much more testing than I have machines or time for. To speed up and improve with this testing we’re hoping for the user-base to lend a hand. Yes, sIFR wants you.

A mailing-list has been set up to aid in this process. After every significant iteration of the code you’ll be notified through this list of new tests. The tests will mostly be automated, aside from the occasional human verification. We’re hoping to get confirmations from two or three people for each browser and browser version we are supporting (more about that in a later post).

The list will also be used to discuss the way sIFR works itself. Here you can think of the tuning process or how to provide fallback for Flash transparency. sIFR is not going to be “designed by committee” however. While we greatly value your input, at the end of the day it’s going to be our call.

Time

Just as Mike is busy, so am I. Currently I’m studying Computer Science at the University of Twente and I also have to earn some money. If you have an interest in seeing sIFR developed faster please consider donating (by clicking on the Make a Donation button on the right) or hiring me to work on sIFR.

The Future

The future of sIFR is bright. With the advent and acceptance of newer browsers it has become possible to ease the use of sIFR. The release of the new Flash 8 gives us more power, and also means we’re letting go of Flash 6. Good stuff is on it’s way, and I hope you’re in for a ride. Again, please sign up for the mailing-list and thanks in advance for your support.

Optimizations

posted December 13th, 2005, 10 comments, tagged ,

When optimizing, you have to make sure it actually helps. In this case, not really. You’re lucky to see a one millisecond difference on a thousand function calls (in Firefox 1.5 on Mac, at least).

Update (a few minutes after the fact): I realized that Deans function checks for the existence of a property on a node. This lookup could cost more time, so I updated the test case to make sure the if/else clause looked up two properties on the document node. Differences now are 10 – 20 ms (still not very much as we are talking about a thousand invocations).

Update (December 13th, 11 pm): Updated test with Sjoerds suggestions. More interesting results in the comments.

They Searched, Came, and Left in Utter Confusion

posted December 7th, 2005, no comments, tagged ,

Some search engine referrers I found in my logs:

Looks like I might want to put some ads up when people are coming from search engines…

(And regarding that christmas card thingy… not feeling like it this year. Sorry!)

Nine down, one to go

posted November 24th, 2005, 11 comments

On my way to being a twenty-something.

Paul Graham on “Ajax”

posted November 20th, 2005, no comments, tagged ,

Basically, what “Ajax” means is “Javascript now works.”

- Paul Graham

TextPattern and HTML 4.01

posted November 13th, 2005, 3 comments

Because James Bennett bugged me about using TextPattern to output HTML 4.01 code, here’s how I did it.

First of all, make sure the pages and forms use valid HTML 4.01 markup. Then add the following code to publish.php:

$html = preg_replace("/<(.+)\s?\/>/", "<$1>", $html);

after this line:

$html = ($prefs['allow_page_php_scripting']) ? evalString($html) : $html;

This will change all occurences of <tag/> and <tag /> to <tag>. Note that it does not replace any namespaced attributes or other XHTML specific stuff, but to my knowledge TextPattern does not use this anyway.

As for my reasoning, as I wrote before:

(…) for compatibility, XHTML needs to be sent as HTML. Which means browsers see it as invalid HTML, no matter how valid the XHTML is. Furthermore, you can write just as clean code in HTML as in XHTML, so I don’t see a reason to use XHTML.

Travelogue Slash Crash

posted November 13th, 2005, no comments

I like travel of the passive kind: board a train at 8 in the morning and emerge at the other side of the country two and a half hours later. Same with airplanes: board the plain at 11 am and arrive in Copenhagen somewhere around noon.

Thursday and Friday were spent debugging JavaScript code for Firefox, Safari and Opera at work. While not exactly challenging work, in the sense that you can create cool stuff, it is a fight against several browsers, implementing IE’s XML features (really, it’s competition is only now approaching IE when it comes to XML/XSLT support and other proprietary features such as innerText) and forcing things to just work.

Friday night I had dinner with Anne and we and Sjoerd went to see Crash, which is a really cool movie.

Yesterday I returned to Enschede. No matter how small the Netherlands is, it still takes too long to travel from one side to the other.

Million Dollar Question

posted November 10th, 2005, 3 comments, tagged

What are the two ways HTTP can request an image from a server?

For bonus points: you are not allowed to use cookies.

War and UDP

posted November 6th, 2005, no comments, tagged ,

UDP packets are like soldiers, running across the battle field, trying to reach their destination. Packet loss occurs when they are shot down, but nobody realizes this. The soldiers won’t bide their time until it’s safer to cross, like the mindless packets they are they will try and try again until they have all been killed.

TCP packets, on the other hand, are soldiers led by smart generals. If the field gets too dangerous, they’ll try a less massive approach. When the dust settles they’ll suddenly spring to action and make a run in massive numbers, but when the field gets congested they’ll slow down considerably.

They’ll also slow down when the UDP soldiers are among them, making them all easy targets. Let the UDP squadrons fight the hard battles, TCP fights a smart one.

sIFR 3: A Request for Requests

posted October 28th, 2005, no comments, tagged

As The Seattle Lion pointed out, we’re taking suggestions for sIFR 3. Head over to the request page and let us know what you’d like to see!

Astronaut Suits on the BBC

posted October 28th, 2005, no comments, tagged ,

Last week at BarCamp Paul Mason from Newsnight made a special about Flock. Last night it was broadcasted. I took some pictures, which can be found on Flickr.

Normally a stream would be available for 24 hours after broadcast, but not this time!

Unfortunately, we cannot make the Thursday, 27 October edition of Newsnight available online because of copyright problems.

Crap, I wanted to rip the stream, so now I have no recording. I’m afraid the pictures will have to do…

There’s an article up, too.

No-Tech

posted October 25th, 2005, no comments, tagged ,

In this tech-infested world, the question really is how to balance the tech. Are you better of with less tech?

When I made the presentation for BarCamp, I didn’t make it in S5 but in Keynote. S5 makes you write in HTML, makes you think in two different levels: the markup and the presentation. And you can’t see those at the same time — constantly switching the presentation of your content hurts the way you think.

I didn’t write this post in HTML. I use Markdown: plain text with some syntatic sugar to extract semantics. But the only sugar I’ve used this far is grouping the text in paragraphs (and adding two links after the fact).

When I want to code something I turn on some music, stand up and walk to a whiteboard. Pick up a pen and start sketching stuff. Pause, walk around a bit, drink some water, and improve things. Get your mind in a flow state, design the app. Then, start coding. Throw away parts you don’t need. Get up again if you get stuck, rethink.

Would OmniGraffle work here? Although, to be honest, I haven’t tried, I don’t think it would. It’d be sitting at my desk, typing away and connecting dots on the screen. Quickly drawning some physical lines is easier than doing it in the virtual world. And since these wireframes won’t reflect the actual code, does it matter in what medium I make them? If necessary I can always redo them in the virtual space later.

At uni, we had to make UML charts for Java code with Together. Aside from annoying the crap out of me, because seriously, that app is confusing, did it really make me code better? Not really, since I thought of the abstractions in my head, then put them into Together, and then wrote the code. Sketching on a real whiteboard would’ve been faster.

No-tech is important, don’t let all the shiny tech blind you from that.

Unconference Thougths

posted October 23rd, 2005, 4 comments, tagged ,

BarCamp rocked. Thirty-two hours (and then some) of hanging out with cool people talking and learning and hacking. It’s quite amazing how you can set up an unconference in a months time and have it all working out really well.

I was planning to write a really long post about how cool and great BarCamp is, but you really have to see it to believe it. So, if you can make it to one of the camps, just go there and enjoy.

Photos on Flickr: BarCamp Amsterdam. Slides and links from my (admittedly short) presentation on JavaScript bugs are also available.

BarCamp Amsterdam…

posted October 20th, 2005, no comments, tagged ,

… is really cool. Great people here, even Bobby dropped by for an hour of six. More later :)

Experiment: Function.$case

posted October 19th, 2005, one comment, tagged ,

One of the few things I like about Java is how methods are identified not by their identifier (language collision here, it’d seem) but by their signature. In code:

public static void foo (String bar) {
  // do things
}

public static void foo (String[] bar) {
  // do things
}

These foo() methods are different in that they accept different types of parameters. In dynamic languages this is not possible, but of course you aren’t limited to one type of parameter.

And yet, I was wondering if there was an elegant way to implement someting comparable in JavaScript. Let’s take the following code, and try to make it more elegant:

function say (value) {
   value = value || "";

   if (typeof (value) != "string") {
      if (value.constructor == Array)
         value = value.join("\n");
      else {
         var result = "";
         for(var prop in value)
            result += prop + ": " + value[prop] + "\n";
         return result;
      }
   }

   alert(value);
}

Hmm, some conditions and specific code in their bodies. It’d almost look like a switch statement, but in JavaScript you can only compare values, not use your own conditions. However, Ruby has a switch mechanism which lets you evaluate conditions:

case
when 1 + 1 == 2
  puts "1 + 1 is indeed  2"
when 1 * 1 == 1
  puts "1 * 1 is indeed 1"
else
  puts "Somebody must have disproved mathematics"
end

Quite elegant. Let’s consider this example:

function say (value) {
   value = value || "";

   alert (Function.$case (
      typeof value == "string", // when
         value, // expression

      value.constructor == Array, // when
         function () { // expression
            return value.join("\n");
         },

      function () { // else
         var result = "";
         for(var prop in value)
            result += prop + ": " + value[prop] + "\n";
         return result;
      }
   ));
}

The even attributes (counting from 0) are the conditions. The odd attributes are the expressions, and the last even attribute is the “or else” expression.

I’d say this is (in theory) more elegant, but it looks confusing and requires more code — sounds a bit like Java, actually. And this is what Function.$case() looks like:

Function.$case = function () {
   var condition;
   for (var i = 0, expr = null; i < arguments.length; i+=2, expr = null) {
      condition = arguments[i];
      if (i == arguments.length - 1)
         expr = condition;
      else if (condition === true)
         expr = arguments[i + 1];
      else if (typeof (condition) == "function" && condition () === true)
         expr = arguments[i + 1];

      if (typeof (expr) == "function")
         return expr ();
      else if (expr != null) // To obviate the need for superfluous functions, you can also use a value instead of a function
         return expr;
   }
}

More Site Updates

posted October 14th, 2005, 7 comments

After some hard work (cough) I’ve updated the following:

  • Valid Atom feeds, even for comments
  • Feed to get the 20 most recent comments
  • Comments form without tables
  • and better hints
  • Highlighted the comment posted notification
  • Ensured no XHTML escapes the system. HTML rocks
  • Fixed Last-Modified on comment feeds
  • Full content feeds (with summary)

TextPattern rocks.

Rip

posted October 14th, 2005, one comment, tagged ,

You know those pesky “Copy-Protected” CDs? Unfortunately I’ve got a few of them (found out too late) and they were a pain to rip on the Windows box. Sometimes they’d crash iTunes (or other ripping software), and if it worked, well, they had some small gaps in them.

Not anymore. I have no idea whether it’s iTunes 6, or Tiger, but I can now rip right through them with perfect quality. (Not perfect as in lossless, but perfect as in 160 kbps VBR AAC.) On one disc it even found a bonus track, which I missed out on when I ripped it on Windows.

Also nice is how Tiger shows two discs if the CD contains a data part and an audio part. Perhaps that’s why ripping works so well?

BarCamp Amsterdam

posted October 12th, 2005, 2 comments, tagged ,

Next week I’ll be in Amsterdam for BarCampAmsterdam. Two days of hanging out, hacking, and other cool, interesting stuff. Rock.

I’m thinking of giving a presentation on working with JavaScript in this cross-browser world. If you have ideas, let me know.

Shiny

posted October 11th, 2005, 10 comments

Look! All new, all shiny! Running on a somewhat hacked version of TextPattern, valid HTML 4.0.1 Strict and Atom 1.0. Now with comments. And article-level feeds. Yay!

This site is not guaranteed to work in browsers originating from Redmond. Batteries not included (but stolen from smoke alarms).

Entmoot

posted September 26th, 2005, no comments, tagged ,

We had Entmoot in Utrecht:

The Happy Clog meeting, err, Entmoot, was fun. Even a girl attended,

There are pictures on Flickr!

Adding Events, Recoded

posted September 11th, 2005, no comments, tagged

Peter-Paul Koch recently launched the adddEvent() recoding contest. Of course, I couldn’t say no to such a challenge, so here’s my article explaining my entry. You might want to write the short terminology overview I wrote a while back before diving into this article.

First off, let’s look at the requirements for the event code:

  • must work in all modern major browsers: Mozilla, Opera, Safari and Internet Explorer.
  • must follow DOM standards (which also means adding the same listener only once)
  • must not leak memory
  • must respect the this keyword (scoping must work correctly)

Then there are some niceties we might want:

  • allowing you to set the scope
  • providing a way to modify the event object

With that in mind, I started writing. After three iterations of simplifying, I came to the current solution. Let’s look through it piece by piece.

Oh blimey, that looks like code!

var __DomEvents__ = new function()

I’m creating the necessary functions inside __DomEvents__. I’m using the underscores because the actual addEvent() and removeEvent() functions are exported into the window object and I want a nice little namespace of my own. Also note the new function() part: the methods for the script are defined in an anonymous class, which is immediately instantiated. JavaScript’s solution for Singletons.

var registry = {};

function registryKey(id, type, fn)
{
  return id + "#" + type + "#" + fn;
}

function isInRegistry(key)
{
  return registry[key] != null;
}

This is the code which keeps track of all event listeners. registry is a hash table in which the listeners are stored. registryKey() takes care of creating a unique key to store the listener with. It does this by combining the ID of the event target, the event type, and the string representation of the listener. Finally isInRegistry() is a utility method we can use to see if a key has already been registered.

function invoke(key, evt)
{
   if(!isInRegistry(key))
      return null;

   _evt = __DomEvents__.alterEventObject(evt);
   // If the alteration method returned an object, use that as the event
   // object.
   if(typeof _evt == "object")
      evt = _evt;

   var handler = registry[key];
   var scope = handler.scope;

   // Evade Function#apply()
   scope.__DomEvents_listener__ = handler.listener;
   scope.__DomEvents_listener__(evt);
   scope.__DomEvents_listener__ = null;
}

invoke() will make sure the listener is invoked in the right scope. First — of course — it makes sure the listener is still in the registry. It then calls alterEventObject(), which allows us to modify the event object before it’s sent to the listeners. And at last it’ll invoke the listener!

The next part is a bit tricky. We first load the handler, a small object stored in the registry which contains a reference to the listener and the scope. Then it sets a property, __DomEvents_listener__ (now if that isn’t unique, what else is???), which references the listener. This means that when the listener is invoked via the property its scope will be scope. That’s exactly what we want so we invoke the listener via the property and then set the property back to null. We ain’t leaving no traces, are we now?

var targetIdCount = 0;

function targetId(target)
{
   if(target == document)
      return "__DomEvents_ID_document";
   if(target == window)
      return "__DomEvents_ID_window";

   var id = target.getAttribute("id") || target.uniqueID;

   if(id == null){
      id = "__DomEvents_ID_" + targetIdCount++;
      target.setAttribute("id", id);
   }

   return id;
}

Remember how registryKey() uses the ID of the event target? We need a way to find this ID. For the document and window objects, we’ll use a static ID, for HTML elements we’ll try to derive the ID from the element. Our first attempt is the id attribute. If it doesn’t exist we check for uniqueID. The latter is a property in Internet Explorer which contains a unique id (you didn’t expect that, did you?) which you can use to reference the element (through document.getElementById()). If we still don’t have an ID, we’ll have to set it ourselves. IDs generated by this code are prefixed with __DomEvents_ID_. targetIdCount is used to make sure we assign unique IDs — with each assignment the count is updated. And finally we return the ID.

window.addEvent = function(target, type, listener, scope)
{
   var key = registryKey(targetId(target), type, listener);
   scope = scope || target;

   //    Check if the listener hasn't been registered already.
   if(isInRegistry(key))
      return false;

   // Hey, it's a new listener!
   handler = {
      listener:   listener,
      scope:      scope,
      invoker:    function(evt)
                  {
                     invoke(key, evt);
                  }
   }

   registry[key] = handler;

   if(target.addEventListener){
      target.addEventListener(type, handler.invoker, false);
   } else if(target.attachEvent){
      target.attachEvent("on" + type, handler.invoker);
   } else {
     return false;
   }

   // Reset variables:
   target = listener = scope = null;

   return true;
}

Yes! That’s the code you’ve been waiting for, right? Let’s go through this step by step. First we create the key for this listener. We also check if it’s already been registered. Since isInRegistry() takes a key as parameter, listeners are distinguished by their function, not the scope. Talking about which, the line scope = scope || target; means that the scope will be scope, unless it hasn’t been specified, in which case it’ll be target (and the this keyword refers to the event target the listener was added to, great!).

You can also see how the handler object is created. The invoker property holds the method which will be added as the DOM event listener — it’s just a wrapper for the listener you specify. The handler object is added to the registry and the invoker method is added as the event listener.

There are some key things in this code with regards to memory leaks. By storing the listener and scope in an object separate from the event target,
we can prevent circular references between the target and the listener and the target. And even though the invoker method is a closure, by setting the values of target, listener and scope to null we make sure IE won’t be taking a leak.

By now we are almost finished with the code. We only have to look at a way to remove the listeners:

window.removeEvent = function(target, type, listener, scope)
{
   var key = registryKey(targetId(target), type, listener);

   if(!isInRegistry(key))
      return false;

   var invoker = registry[key].invoker;

   registry[key] = null;

   if(target.removeEventListener){
      target.removeEventListener(type, invoker, false);
   } else if(target.detachEvent) {
      target.detachEvent("on" + type, invoker);
   } else {
      return false;
   }

   return true;
}

Actually I think you are perfectly capable of understanding this method. Onwards to the final cleaning up:

this.alterEventObject = function(){};

The alterEventObject() method is a dummy method for invoke(). Note how it’s set as __DomEvents__.alterEventObject().

Documentation for the public methods

addEvent()

Adds the listener.

Parameters:

  • target: the element to which the listener will be added. document and window are supported as well.

  • string >> type: the type of the event, without the “on” prefix.

  • function >> listener: the listener you want to add.

  • object >> scope: the scope this listener should be invoked in. Default: el.

    Returns:

  • boolean: whether the listener has successfully been added or not.

removeEvent()

Removes the listener.

Parameters:

  • target: the element to which the listener will be added. document and window are supported as well.

  • string >> type: the type of the event, without the “on” prefix.

  • function >> listener: the listener you want to remove.

  • [object >> scope]: the scope this listener should be invoked in. Default: el.

    Returns:

  • boolean: whether the listener has successfully been removed or not.

alterEventObject()

A function which can alter the event object. Override this to use your own.

Parameters:

  • evt: Event object to alter.

    Returns:

  • [object]: the modified event object. You can also modify evt directly.

And… that’s it!

Wow, I just explained the entire code! You can see the required example page and also the source (littered with custom-style documentation, that’ll have to be extracted in the near future). Like most of my other work the code is licensed under the CC-GNU LGPL.

Also I’d like to thank Michaël van Ouwerkerk for testing the script on Windows, and Lief van der Baan for checking the article for spelling and grammar mistakes. Thanks guys!

I hope you enjoyed the article. If you have any questions, you are welcome to contact me.

Upcoming Happy Clog Meeting

posted September 11th, 2005, no comments, tagged ,

Hey y’all, just wanted to let you know that there’s a new Happy Clog meeting coming up! Check the wiki to sign-up. And if the wiki goes down, let me know and I’ll get it running again.

See you the 24th!

Netscape is a Network App, and without…

posted September 7th, 2005, no comments, tagged ,

Hey I’m back, still alive, still kicking. University started for real this week (posting this from the Telematics class). The professor here has a real thing for Netscape, say, if you don’t have Netscape on your PC you can’t get on the internet.

Of course Netscape sucks even more than IE, and I don’t know of any PC without IE… oh well.

By the way, he also mentioned Eudora mail, and I quote:

Some use Internet Explorer (laughter).

I’ll forgive him the browser stuff if he can tell me interesting things about the network though :)

One Hundred Rabbits and an Elephant

posted August 17th, 2005, no comments

After well over eighteen years I left the family to go live in Enschede, which not entirely by accident is home to the university where I’ll be starting in the Computer Science Bachelor Programme. You can find some pictures of the house on a certain photo-sharing site. The pictures are from about a month ago, I’ll try to upload new pictures soon. And congrats to fellow Happy Clog Anne, who’s now a W3C member and (I think) back in Holland and (I think) proud owner of a certain laptop, which (if all goes well) I’ll be next Friday as well. Yay!

And I hope I won’t regret it ;-)

Song

posted August 10th, 2005, no comments

Why don’t you ever want to play?
I’m tired of this piece of string.
You sleep as much as I do now, and you
don’t eat much of anything.

I don’t know who you’re talking to
I made a search through every room,
but all I found was dust that moved
in shadows of the afternoon.

And listen,
about those bitter songs you sing?
They’re not helping anything.
They won’t make you strong.

So, we should open up the house.
Invite the tabby two doors down.
You could ask your sister, if
she doesn’t bring her Basset Hound.
Ask the things you shouldn’t miss:
tape-hiss and the Modern Man,
The Cold War and Card Catalogues,
to come and join us if they can,

for girly drinks and parlor games.
We’ll pass around the easy lie
of absolutely no regrets,
and later maybe you could try
to let your losses dangle off
the sharp edge of a century,
and talk about the weather, or
how the weather used to be.

And I’ll cater
with all the birds that I can kill.
Let their tiny feathers fill
disappointment.

Lie down;
lick the sorrow from your skin.
Scratch the terror and begin
to believe you’re strong.

All you ever want to do is drink and watch TV,
and frankly that thing doesn’t really interest me.
I swear I’m going to bite you hard and taste your tinny blood
if you don’t stop the self-defeating lies you’ve been repeating
since the day you brought me home.
I know you’re strong.

The WeakerthansPlea From a Cat Named Virtute

Happy Clog

posted August 8th, 2005, no comments, tagged

For the two years or so, a group of Dutch bloggers from the web standards scene have been getting together at meetups, chatting and e-mailing together, organising things together. Things have gotten more organised over time, but I felt that something was missing. Something…tangible. Something we could call ourselves, something that brought us together as group more than before. I set out with a mission and I came back succesful. Ladies and gentlemen, I present to you: Happy Clog!

Faruk said it pretty well, and I’m too lazy to repeat it. As of yesterday I appearantly am a Happy Clog!

Also check Egors post, who has a nice image up. And Sjors’, who came up with the name.

Yeah, I’m just adding links to make it look like I’ve got something to say.

Hacking Linux

posted August 6th, 2005, no comments, tagged

A couple of days ago I installed Ubuntu so I could work on this site on a *NIX environment. Aside from the pain and suffering of getting it running properly (who’da thunk you needed to edit a config file with sudo to configure the refresh rates of your monitor, which you have to get off Amazon, to get a proper display?) I also got some hacking done.

Hacking of the hardware kind, that is.

Because of the really curious audio properties of Linux I had to split the front speaker signal of my 5.1 sound card so it would also drive the rear speakers. And I plugged the out signal of the iPod dock to the input of the sound card, so I could play AAC files with proper browsing through artists and albums.

Sigh, but at least it works.

P.S. I wish uni would hurry up with my registration papers, so I can pick up a proper UNIX machine ;-)

iPod Giveaway #4: Entry #2

posted July 31st, 2005, no comments

I lost it. Damn. It took me a full 0.5 microseconds to realize it was gone. As if it had evaporated in plain sight. I should have known it would happen one day, but I didn’t. And now the pain is unbearable.

It happened in the new high-speed train between Amsterdam and Paris. I had been visiting some relatives back in Holland and was now on my way home. Home, for me, is a small apartment in a large building near a now tiny Eiffel tower. I remember the days when the Eiffel tower seemed to rise above the city, a magnificent display of French pride. Unfortunately those days have been lost.

No, today’s world is different. It’s a world driven by technology, and cost-effectiveness. But yet it’s a world with magic, a world where the unexplainable still happens. Like it did today.

When I entered the train I could feel the magic. I was listening, intently, and looking around, somewhat foolishly. There was a girl at the other end of the cabin, staring straight ahead, not distracted by my entrance, as if she was both deaf and blind. I studied her for a while, fascinated by her silent composure. As the train started to move, I sat down. An old song protruded my mind. A manifest, by some long gone Canadian band. It seemed fitting.

And then, nothing. Poof, the music had disappeared. Slightly startled I searched my bag, thinking that’s where I had put it. Nothing there. I patted my jacket, not there either. It had disappeared, along with the music. And as I looked up, I noticed the cabin was empty.

The girl had disappeared as well.

Open Letter to the DOM Scripting Task Force

posted July 21st, 2005, no comments, tagged ,

Dear DSTF,

I applaud your effort to bring JavaScript into a better light, and teach people how to write scripts following best practices. However, I am worried that your focus on unobtrusiveness and best practices will result in less attention for code quality.

A recent example of this is your post about Image Previews. While the script you link to is certainly unobtrusive, I don’t think it’s a good example of code quality, or for that matter, DOM scripting.

I won’t complain about the coding style of the JavaScript, that’s entirely personal. (Although some more whitespace can’t hurt.) No, one of the problems is that the script, written by one of your members, even, does not use DOM events. For a script linked to from your weblog I find that disturbing. Another, and perhaps worse, issue is that the script leaks memory.

Things like memory leakage can be hard to grasp, but it’s fundamental that DOM scripters understand these problems and work around them. Promoting a script which does not use DOM events is giving the wrong example. By all means, link to a great unobtrusive script, but in that same post also point out what could be improved. Please check the code quality of the scripts you link to: people will try to learn from the code and it’s important they learn it correctly.

If all goes well, you’ll be a huge influence to many new developers. Let’s get the max out of that influence.

Kind regards,

- Mark Wubben

P.S. I realized during writing this letter that the script in question was written by and about by Christian Heilmann, one of your members. Please understand that this post is not meant as an attack on Christian, but as an expression of my worries. Again, I applaud you for your efforts.

iPod Giveaway #4: Entry #1

posted July 17th, 2005, no comments

I lost it. Damn. It took me a full 0.5 microseconds to realize it was gone. As if it had evaporated in plain sight. I should have known it would happen one day, but I didn’t. And now the pain is unbearable.

It was a freezing cold winter day when it came to me. I was huddled inside, close to a warming fire, when suddenly there was a loud knock on the door. Startled I got up, a feeling of dread in my stomach. Not sure what to expect, I grabbed the fire poker. Another loud knock seemed to vibrate the room. With the poker firmly gripped I went to the door, and opened it slowly. There was nothing.

Suddenly angered at my fear I threw open the door and raced out. I could see footsteps in the snow, the distance between them indicating the person, or thing, had run off. Suddenly the cold caught me and I retreated, shivering, inside; bolting the door behind me. As I sat down at the fireplace I realized I was still clenching the poker. I got up to put it back and as I got up, I saw something from the corner of my eye. Something wrapped in gift paper, put inside the house while I was hunting my ghost. What reason there was to lure me out, only in order to present me a gift I did not know. But as I approached the gift, as I moved my hands to open it, I could feel the feelings of fear dissolving. It was a marvellous gift, odd, yet fantastic. A real piece of Art.

That is how it came to me, and it’s been with me ever since.

And now it’s gone.

Time To Get

posted July 14th, 2005, no comments, tagged ,

Time to get NEWD? Hah, we Dutch can do better: 2005july24! Sign up and we’ll see you there!

And yeah, we need a name, but we are working on it!

Spamming Reboot

posted July 10th, 2005, no comments, tagged ,

Scoble can talk what he wants about PR spam, but he’s certainly not afraid of it himself. During the Thursday meetup he opened up some boxes and started giving away… Channel 9 mascottes! See this (slighty munged) one from Jaco Boetter, for example.

And it got worse: the following two days the mascottes were “conveniently” placed next to the t-shirts. Nice one, Robert.

For A While, I Had Two Reboot T-Shirts

posted July 9th, 2005, no comments, tagged ,

When we arrived at the conference venue we were given name tags and we could pick up a yellow Reboot 7.0 t-shirt. There really were loads of them, so many that on Saturday there were still t-shirts left.

So, I picked up another one. It really was too bad though that on Sunday morning I found out that the t-shirt I picked up… was the wrong size. Way too large for me, actually. I did put it in my bag though (of course I did!) and Anne and I headed out for the Sunday brunch meetup.

After brunch a small group headed into town: Ben Cerveny, Nicole Simon, Laurent Haug, Raymond Kristiansen, Martin Röll and one guy who works for the London Times (sorry, it’s been a while…). And also, of course, Doc Searls.

Doc Searls, who didn’t plan to go into town at all. Who was planning to spend the day in his hotel. Who was wearing only a t-shirt, on this cold Scandinavian day.

Well, you can guess… my extra, way too big Reboot t-shirt ended up keeping Doc warm. And, well, he told me I’d get it back, which I haven’t, but I don’t really mind. It was too big for me anway.

picture, another picture

But One Year Ago

posted July 7th, 2005, no comments

One year ago exactly I was in London, hanging out with Eddie Sowden and two of his friends. Now today happened and some stations I passed through that day, one year ago, have been hit.

My thoughts are with the victims and their friends and family. Be strong, and don’t let them scare you.

Books

posted July 1st, 2005, no comments

Something I haven’t written about here is that DHTML Utopia contains my Event Cache technique. I still need to get my hands on a copy, but that’s pretty damn cool!

Also, yesterday evening I officially graduated from high school. Another book closed. The evening was quite fun, my dad took some pictures, I might put them on Flickr. Anne graduated as well, congrats man!

And finally, the subject I talked about earlier will have to be picked up later, due to the complexity of prediciting how fast institutions will handle subscriptions. More about that soon, I hope.

Saturday

posted June 27th, 2005, no comments

I walked into a store today. It was actually quite funny to walk into that store, listening to Steve Wozniak, on my iPod.

Odeo – Pronounced Like Rodeo

posted June 19th, 2005, no comments, tagged

Odeo is the new online podcasting service which has been in the news for most of this year. A couple of days ago it opened it’s beta by means of invites – and before you ask: there’s no way to send out invites yourself. Just subscribe for an invite and hope for the best. Anyway, in this article I’ll provide an overview of the beta. The accompanying screenshots can be found on my Odeo Beta Overview photoset.

Odeo provides a very friendly atmosphere, heavily influenced by Flickr‘s approach of tongue-in-the-cheek language. Just look at the sign-up page, for instance. Or the Terms of Use (cheers to Matthew Bischoff for that). And, to continue on the Flickr angle, they also have tags, profiles and contacts.

The podcasts are centered around channels to which you can subscribe. Actually there’s no talk about podcasts, the individual audio files are “shows”. You can tag channels, and there are also tags for shows, but (possibly due to a bug) I wasn’t allowed to tag shows.

Something else which isn’t possible is finding all shows and channels. I’d say this is the best way to explore Odeo, but unfortunately the closest thing to finding all channels is the Top 40. I definitely hope they fix this soon.

Aside from tags Odeo does a few other things which tie in well with the Web 2.0 spirit of open platforms. Odeo allows you to create a new channel by importing a syndication feed. If you’re a podcaster yourself this is the way to get your stuff on Odeo while preventing lock-in. You can also get a RSS feed for your subscription list, meaning you can use any program you want to download the shows to your listening device.

Speaking of downloading programs, Odeo provides one itself: the Odeo Syncr (more Flickr influences!). The app seems to work reasonably well: the shows are imported in iTunes just fine. However, after you’ve finished downloading you can’t exit the Syncr, it’ll show an error message. Proceed anyway and there’s another error message. Also after downloading the “Sync Now” option doesn’t seem to work anymore. Seems like the app freezes up after the downloads are done, but hey, it’s still a beta, right? (I’m using WinXP Pro SP1, by the way.)

The design of the Syncr itself is pretty interesting. It’s preferences are managed through the Odeo web app itself, not stored inside the Syncr. Also interesting is that it’s open source. In fact it’s built off the wonderful iPodder Lemon open source project.

You can let the Syncr download shows by adding them to your queue, though you can also download them via a direct link. The Syncr then downloads the shows from this queue. When you subscribe to a channel the first show of that channel is added to your queue. If you want to download all shows in the channel you have to add each show to the queue manually. A “add all shows in this channel to the queue” would be nice.

After each show has been downloaded it is added to your media player of choice. Well, right now only iTunes and Windows Media Player are supported, but at least you can choose. Additionally you can use the “auto” preference, which (I think) picks the default player for your system. There are some more settings, see this screenshot for more info.

The Syncr creates a new playlist which contains the shows downloaded from Odeo. From there on you should be able to do some interesting things using the Smart Playlists in iTunes, but unfortunately the metadata provided with the shows is lacking. For example I downloaded a few shows without artist / album metadata in the MP3 files, nor with a descriptive title. It’s very hard to figure out what’s what in this way, while Odeo could use the metadata from the creator’s account to populate these fields. It would also be very nice if they would embed the channel name in the file. That way it’d be easier to group channels (such as channels with free music downloads) together in a playlist.

And that concludes this overview of Odeo. Unfortunately the Create part of the side is not yet open, so I haven’t been able to review that. I’ll leave you with this pseudo-easter egg and hopefully I can write more about Odeo later (yeah, that’s a hint, you Odeo guys!).

Later

posted June 18th, 2005, no comments

So I apologize for the short post of last Thursday, and I’ll try to make it up with this one. Highschool has been a weird mixture of things to me: on the one hand I’d like to know how things work, on the other hand I don’t want to do the maths on these things. It’s really cool how electricity and batteries work, but answering questions on it is not. Couple that with the other stuff I’ve been interested in (think hacking, tagging, “social software”) and it becomes a strange mix.

But it’s over now. And interestingly enough I find that now I’m hacking JavaScript for a living (well, scratch that, it’s a summer gig) there’s less need to work on my own stuff. I suddenly understand the reasons why DC2 never happened a lot better.

Anyway, there’s some other things I’d like to talk about in this post. MyBlogLog, which I talked about earlier, has started an affiliate program. So hopefully tomorrow I’ll be dissecting it yet again, and I’m going to add affiliate links too. Let’s see how much money I’ll rake from that! (Well, looking at my stats now, there’s been 27 hits on that article in the past week, even though it holds the #2 position on Google, so most likely it isn’t even going cover the monthly hosting costs of this site).

Another thing I’d like to dissect is Odeo, which just launched and to which I’ve been invited. No idea if it’s specific or if it’s because I signed up for the invitation. In any case now I can finally speak about Dunstan Orchard who’s doing the UI stuff for the Odeo.

Next Friday I’ll be heading to Enschede to look for a room at the University of Twente, where I’ll be doing CS next year. Anyone who want’s to grab a beer (or has a room I just have to check out) let me know.

Also, next Sunday there’s a JavaScript meetup in Amsterdam, organised by Peter-Paul Koch and Bobby van der Sluis. It looks like some of my colleagues from Q42 are tagging as long as well. Rock!

Have a nice Saturday!

Bye bye

posted June 16th, 2005, no comments

Thus ended six years at highschool: marks between sevens, eights and nines, average eight. More later (as usual).

Update: Later is now!

Mindboggling

posted June 13th, 2005, no comments

Oh boy, was that great. I’ve met a lot of cool and interesting people, and learned a lot about cool and interesting stuff. Today I slept in till 11:30, then I spent the afternoon reading stuff and uploading photos. You can find them here and here.

Unforunately I’ve run out of time today to add more descriptions / notes to the photos, or write more here for that matter. But I’m taking notes about stories I’d like to tell, and I’ll give that a shot in the next few weeks.

In the meantime I’d like to give a big shout out to everyone who made Reboot possible. It’s been the best thing of the year!

Hi from Admiral Gjeddes Gaard, Copenhagen!

posted June 9th, 2005, no comments, tagged

Hi from Admiral Gjeddes Gaard in Copenhagen… I’m using one guys PowerBook now, which is really nice, though it requires some getting used to (even worse, it’s got a German kezboard!). Anyway we had a really nice evening here, and a good trip too. Tomorrow’s the real thing, I’ll try to hijack a laptop then as well. Bye!

Event Cache Follow-up

posted June 3rd, 2005, no comments, tagged

Finally, here’s a short follow-up to the original Event Cache article. Let’s start with the best news: the script is now known to work in Internet Explorer, Safari and Mozilla.

Implementing

Something which had to be read through the lines in the original article was how to actually implement the script. Therefore, a little tutorial.

First we take Scott Andrew LePera’s addEvent code. The addEvent function [1] is as follows:

function addEvent(obj, evType, fn, useCapture){
    if (obj.addEventListener){
        obj.addEventListener(evType, fn, useCapture);
        return true;
    } else if (obj.attachEvent){
        var r = obj.attachEvent("on"+evType, fn);
        return r;
    } else {
        return false;
    }
}

This doesn’t take care of IE/Mac and other legacy browsers, so you might want to use this method by Simon Willison to get around that.

Anyway, onwards with adding the event to the cache! Here’s the modified version of Scott Andrew’s code which does exactly that:

function addEvent(obj, evType, fn, useCapture){
    var result;
    if (obj.addEventListener){
        obj.addEventListener(evType, fn, useCapture);
        result = true;
    } else if (obj.attachEvent){
        var r = obj.attachEvent("on"+evType, fn);
        result = r;
    } else {
        return false;
    }

    EventCache.add(obj, evType, fn, useCapture);
    return result;
}

We also need to register the onunload handler so we can flush the cache:

addEvent(window, "unload", EventCache.flush);

And that’s it!

Sam Schillace pointed out that the code above was wrong… the method would return before Event Cache kicked in. Big Oops! Thanks for pointing that out, Sam!

(Edited June 3, 2005).

Event Cache, but without the cache

Ramon Leon pointed out to me the other day that it’d be easier to add an unload event which removes the event handler for each event, instead of caching the event and setting an unload event to flush the cache.

Well, yeah, that approach sounds good to me. It doesn’t mean, however, that you’re no longer using a cache: the unload event still contains references to the characteristics of the original event, you just can’t access those references. It’s a minor difference though, I’d say choose which method suits your needs best.

Footnotes

[1]: The original code has an alert to warn you that the event wasn’t set. I figured it’d be more useful if the function would simply return false, so it wouldn’t scare off visitors :)

Kryptonite

posted June 3rd, 2005, no comments

After two weeks, seven finals, hundreds of items piled up in Bloglines and a broken wiki, I return to the world of language wars and license wars and whatever more. As I’m writing this I have my local copy of Novemberborn up, behind the text editor, and I see the bright yellow of the Reboot banner (to the right, link). Oh yeah, less than six days to go! Rock! Supposedly I’m going to get interviewed by Nicole Simon of the preboot podcast, so we’ll see how that goes.

Yesterday I started at the new job, I’m a colleague of Anne now, if you’re inclinced to know. I get to do cool JavaScript work, with what a great team (it’s good to work with other folks who do JavaScript, instead of developing on your own). It also happened to be the first day something “in real life” told me I should “blog” more, well, this one’s for you, Lon!

Speaking of the “blog”, I’ll be getting a PowerBook later this month, and wil be spending the weekends on hacking away at this site. I’ve got some cool stuff lined up, and if the Ruby on Rails framework is as fast to develop in we should definately see some results before the summer ends. In any case, I’ve already read the pickaxe (great book, even if you don’t care about programming you should read it, it’s that much fun), and although I’ve written not even a few hundred lines of code, it’s the most beautiful language ever.

Now if you’ll excuse me, I have a wiki to repair.

– Mark

Somebody Shoot Me

posted May 18th, 2005, no comments

As a kid I always used to hate these things, and I still think they’re a bit silly now that I’ve grown up (a bit). But I suppose somebody is interested in my musical habits (yes, that’s you Robert Nyman and Andrew Krespanisand also Arthur Steiner and Marko Dugonjic) so I’ll play along!

Total Volume

20,0 GB, with some more music on an external harddrive. I really need to sort things out, though, there’s loads of stuff I never listen to!

Last CD Bought

RazorLightUp All Night

Song Playing Right Now

Phantom PlanetSomething Is Wrong

Five Songs I Listen to A Lot, Or That Mean A Lot to Me

Ouch, this is such a hard question. I’ll just give you five songs I think are really, really great. Wish I could add more, though!

Five People to Whom I’m Passing the Baton

I clearly wasn’t paying attention when I wrote this, but Joen and Andrew have already received it. Too bad, can’t go and pass it to somebody else now, can I?

Who’s Tracking This?!?

It’d be really cool if somebody can write a script which visualizes how this meme has spread, what the most favorite songs are, etc. To this purpose I’ve written a small Musical Baton Profile (link goes to XHTML page).

*.total-volume
Total Volume (number GB/MB/KB)
*.last-bought
Latest CD you bought
ul.favs, ol.favs
All list items will be a favorite.
ul.passing-to, ol.passing-to
All list items will be the person to whom you’re passing the baton.
a[rel="artist"]
Link to artist page, preferably on AudioScrobbler.
a[rel="song"]
Link to song page, preferably on AudioScrobbler.
a[rel="passing-to"]
Link to the person you’re passing the baton to. If possible, enhance with XFN
a[rel="musicalbatton"]
Link to a page which has musical baton content on it.
a[rel="via"]
Link to a a[rel="musicalbatton"] page from which you received the baton.

And finally, tag your post with !

An Observation About Ajax

posted May 15th, 2005, no comments, tagged ,

Two things:

  1. Hardcore DHTML hackers hate it
  2. Everybody else loves it
  3. Some people confuse it with soccer (Thanks, Anne!)

Why? Because we hardcore DHTML hackers have been doing this stuff for years, but it never caught on. Combine that with a lot of hype, and you get negative reactions to the term. Until Google put the principle to good use in Gmail. That is, the hardcore DHTML hackers it hired finally got to design a system which used it.

The attention which has been given to Ajax in the past months has largely affected… designers. UI designers, such as Duntan Orchard and Derek Powazek. But also in the back end: Ajax got included in RubyOnRails a month after the original Adaptive Parth article. These are people who haven’t been hanging around in the DHTML communities of yore [1].

The question is, why? I don’t really know [2], but I have the feeling it’s because the DHTML hackers never got to design the systems, and as such couldn’t show their knowledge and ideas. XMLHttpRequest has been here since IE 5.0, after all.

Footnotes

[1]: Bold claim, I know.

[2]: Hey, I was eleven back in 1998!

Photos

posted May 8th, 2005, no comments

Hey I got back from France last Friday. We went home a day earlier because it was raining and it was really cold. Today I had some time to sift through the 263 photos I took, and 141 have made it into Flickr (that is, the photos with my parents and sister on it are private, in total there’s a 172 pictures in Flickr). You can check them out by viewing Vosges 2005, Vosges 2005: The Candy Shop and Vosges 2005: Colmar.

This is the first time I used Flickr for lots of photos, and I have to say it completely rocks. And yes, I’m a pro member now.

Holidays

posted April 29th, 2005, no comments

Well, it’s that time of the year again, at least for me. I have two holidays planned for the next two months, one is for certain, one highly likely.

The Vesges

On Sunday I’ll be leaving for a one-week holiday in the Vesges, France, with the family. Going to do some nice walks, read some books, and revise for my economics and Dutch exams I’ll have at the end of May. I’ll take some pictures to share when I get back.

Reboot7

I just found out about this conference in Copenhagen, Denmark. The description on the site reads:

reboot is the european meetup for the practical visionaries who are building tomorrow one little step at a time, using new models for creation and organization – in a world where the only entry barrier is passion. reboot is two days in june filled with inspiration, perspective, good conversations and interesting people.

Damn right that’s going to be cool! Some of the cool folks who are going to speak / host conversations are Douglas Bowman, Cory Doctorow, Jason Fried, David Heinemeier Hansson, Matt Webb and David Weinberger. To cut to the chase: I’m thinking of going.

I already managed to get Anne to tag along, but we are hoping somebody is willing to put us up for a few days. We still are students, and speaking for myself I’ll be facing some hefty expenses in the coming months (laptop, but also I’m going to move to uni). Anyway, just asking. We’ll be in Copenhagen from June 9 to June 12. Let us know!

Actually, since I’ll be away next week, let Anne know. Thanks!

sIFR 2.0

posted April 28th, 2005, no comments, tagged

sIFR 2.0 is out! This has been my biggest project in the past months. I’ve spent hours upon hours tweaking and tuning the code to make it as easy to use as possible and to make it work in virtually all browsers out there. It’s been a great experience, and I’ve learned a lot more about providing support, promoting an open source project and (unfortunately) cross-browser issues. Heck, we even got Slashdotted!

During the development I worked closely together with Mike, who is a great guy to work with, he’s smart, pragmatic and he’s showed a lot of patience when we were debugging sIFR via IM. As far as I can tell there’s only one bad thing about him, which is that he’s a bit groggy in the mornings. Since I’m nine hours ahead of him, you can guess what time of the day we usually discussed things…

I’d like to thank everyone who I recruted to test various incarnations of the code, in various browsers. I don’t remember all your names, but you know who you are! In special, though, I’d like to thank Joen Asmussen for his article on sIFR, Andrew Hume for How and when to use sIFR and Stephanie Sullivan, who has been a great help improving the documentation.

And of course a huge thanks to everyone else who used and/or promoted sIFR. Thanks!

Now that we’ve reached the end of the 2.0 cycle, perhaps it’s a good idea to look into the future. I expect sIFR to become a bigger influence in webdesign in the coming months and I hope it’ll accelerate the design of a system which will allow designers to load fonts via CSS. In any case, we’ll make sure sIFR continues to work in future browser versions.

Again, thanks for this wonderful experience.

Implementing :target in IE

posted April 28th, 2005, no comments, tagged

Mark, I’ve got a challenge for you! Try to make some JavaScript that emulates the behavior of the :target psuedo-class in IE.

No, I’m not trying to be rude, this would honestly be something very cool, and to be honest (still), my JavaScript skills are not that good.

That’s the challenge I recieved this morning from The Wolf (not The Real Wolf though). Well, I thought it was doable, so I accepted:

That should be possible… challenge accepted!

And indeed, after about 30 minutes of hacking it’s done: Emulate the CSS :target Pseudo-Class in IE. The demo only works in Internet Explorer, you can view source there to see the code.

I hope you can use it!

Oh, one more thing:

Great to hear it! If you can do it, everyone owes you a beer or something… :)

You all owe me one!

More Seriously: Why CSS Doesn’t Define Behaviour

posted April 25th, 2005, no comments, tagged , ,

When I wrote my short tutorial on how to use the DOM for :hover I tried to point out something else. It was between the lines, and the post was written in a hurry, so I suppose it didn’t caught much attention.

Basically what the JavaScript did was look for events so it could set a different class on the elements. Effectively, mouseover set the state of the element to “hover”, while mouseout set it to “none”. In other words: I wrote code to manage the state of the element, and the style of the element in that specific state is controlled via it’s class.

Thus, a.hover becomes the equivalent of a:hover.

This turns out to be key in understanding why CSS doesn’t define behaviour. The pseudo-classes are a special kind of classes, which aren’t set by some JavaScript code, but by hidden, low-level code in the browser. The browser manages state, detects you have placed the mouse pointer over the element, and styles the element following the rules in the applied pseudo-class [1].

Unfortunately Internet Explorer doesn’t support this behaviour for elements other than a. We need to write high level JavaScript code to manage the state. And since we can’t use :hover to style these elements, we need to use our own class.

This doesn’t mean, however, that CSS is behavioural. Nor does it mean that we should shy away from using :hover, because there are browsers out there which aren’t capable of managing the state of elements internally [2].

Footnotes

[1]: Of course this not only applies to :hover, but also to :active, :link, :visited and :focus.

[2]: Or, more likely, aren’t capable of exposing this state.

The Myth of Object Detection

posted April 22nd, 2005, no comments, tagged

Via Jeffry Zeldman’s alter ego Apartness’ blog come the following practices from Tanya Rabourn regarding web standards (emphasize mine):

In order to anticipate future browser compatibility we require conformance to the following W3C standards:

HTML

Validate to either the W3C’s XHTML 1.0 transitional or strict doctype http://www.w3.org/TR/xhtml1/

CSS

Validate to the W3C’s CSS 2.1 or 1.0 http://www.w3.org/TR/CSS21/ http://www.w3.org/TR/CSS1

Javascript

Javascript will never use browser detect but instead object detection to test for browser support of properties, arrays and methods

I think we all know that things aren’t black and white, just how it is in this case. Object detection surely is a good idea: if you want to fetch an element which has a certain ID, you need document.getElementById, so you might as well want to make sure that method is supported before you run your script. Unfortunately browsers have other bugs than “simply” not supporting DOM methods.

Take Safari, for instance. It won’t repaint the document if you append a new node to it. To work around this you need to use a hack which adds an empty string to the innerHTML of the node you just appended to. Quite obviously you don’t want to use this hack in other browsers: you need to use browser detection to determine if the hack is necessary. And to add to the confusion: this bug has been solved in Safari 1.3, so you might as well want to make sure not to use this hack in that browser.

By the way, did I mention innerHTML is not supposed to work in XHTML documents? You need to find a way to detect that as well…

To conclude: it’s a good idea to use object detection, but sometimes it just won’t cut it. And you don’t want to leave out awesome scripts because it uses browser detection, do you?

How to use the DOM for :hover

posted April 22nd, 2005, no comments, tagged

Yup, Jeremy Keith is completely right when he says that :hover is behavioural. That’s why I’m going to explain here how you should do :hover via the DOM. That’s right… I’m going to tell you!

Okay, so first we grab all the links on the page. We’ll do this on onload. I’m actually going to cheat a bit with the events, but I hope you’ll forgive me:

window.onload = function(){
    var links = document.getElementsByTagName("a");
    for(var i = 0; i < links.length; i++){
        links[i].onmouseover = function(){
            this.className += "hover"; // sorry, no support for multiple classNames here
        };

        links[i].onmouseout = function(){
            this.className = this.className.replace(/\bhover\b/, "");
        };
    };
    links = null; // look how I evaded memory leaks here!
};

And the CSS:

a.hover {
    text-decoration: none;
    font-weight: bold;
}

Ahhh.. that’s so much better. Now we have seperated style and behaviour completely. Time for some well deserved rest now.

You didn’t think I was serious about this, did you?

JavaScript Terminology

posted April 20th, 2005, no comments, tagged ,

This is an introductory tutorial to the sIFR Explained series, covering basic JavaScript (and programming) concepts — some knowledge of JavaScript, like knowing how to define functions, is required though. It’ll be updated from time to time when more background information is necessary. Here we go.

Anonymous Functions

An anonymous function is a function without a name. This is an anonymous function [1]:

function(msg){ alert(msg); }("hello world");

What is happening here is that the function is defined: function(msg){ alert(msg); } and executed immediately: ("hello world").

This isn’t an anonymous function:

function foo(msg){ alert(msg); };
foo("hello world");

Nor is this:

var foo = function(msg){ alert(msg); };
foo("hello world");

In general anonymous functions are used to execute code which only needs to be executed once. This is the way it is used in sIFR. A side effect which occurs in the sIFR code is the creation of closures.

Scope, the scope chain and closures

But before we get into closures, something about scope. Every JavaScript expression is surrounded in a scope. The scope contains what is available to the expression. When you define a new function, the body of this function exists in a new scope. For example:

function foo(){
    var msg = "hello world";
    alert(msg);
};

foo();

Here alert(msg); works, because msg is defined in the same scope as in which alert is called.

Deep down in the JavaScript interpreter the scope is represented as a call object. This object contains all the variables which have been defined in the scope that the call object represents. When a new scope is created, it’s call object is linked to the call object of the scope it was created in. This creates a scope chain.

This phenomenon is best explained through an example. In JavaScript the window object is the global scope. This is the highest scope in the scope chain. If we create a new function in the global scope the scope of this function will be chained to the window object:

var msg = "hello world";

function foo(){
    alert(msg);
};

foo();

Now, if you call foo, it’ll alert hello world! But how does it know the value of msg, which wasn’t defined in the function body foo? It turns out that if the JavaScript interpreter can’t find a variable in it’s current scope, it’ll go up the scope chain and searches the parent scope until it finds the variable. In our case it finds msg in global scope.

You can also create a scope chain by nesting functions:

function foo(){
    var msg = "hello world";
    function bar(){
        alert(msg);
    };
    bar();
};

foo();

Now, if you call foo, it’ll define bar and execute it immediately afterwards. And as expected it’ll alert hello world.

When a nested function has access to the scope of it’s parent function, it is called a closure. sIFR relies quite heavily on closures.

At ths point you might want to read Variable scoping gotchas from Scott Andrew for more information about defining variables in JavaScript.

Footnotes

[1]: I’m not really sure why you’d want to write an anonymous function to alert `”hello world”`, but it’s just an example!

sIFR Explained

posted April 17th, 2005, no comments, tagged ,

This is a series about how the JavaScript code in sIFR works. What tricks were used, what are the powerful concepts, and what are the browser hacks. It’s an interesting read, in which you’ll dive into some complicated JavaScript code.

Before we get started though, you might want to read the terminology tutorial which will give you insight in the wonders of closures, scopes and all that fancy stuff.

Enjoy!

Event Cache

posted April 13th, 2005, no comments, tagged

Event Cache is a small script which can store the events you have set on a page and remove them on unload. This way it can prevent memory leakage. Please read DHTML leaks like a sieve by Joel Webber for more information. Also, here’s a quote Jim Ley, who explains:

The Internet Explorer web browser [...] has a fault in its garbage collection system that prevents it from garbage collecting ECMAScript and some host objects if those host objects form part of a “circular” reference. The host objects in question are any DOM Nodes (including the document object and its descendants) and ActiveX objects. If a circular reference is formed including one or more of them, then none of the objects involved will be freed until the browser is closed down, and the memory that they consume will be unavailable to the system until that happens.

So, how can we solve this? If we make sure no references are left when the page unloads, there won’t be a problem. In the comments of Joel Webber’s article Dylan Schiemann mentioned that Alex Russell of NetWindows had written a solution for this problem. So I dived into the NetWindows code to look for it.

It turns out that what the NetWindows code does is caching all events set for the affected browsers and detaching the events on unload. Based on this principle I wrote EventCache.

Please also read the follow-up article.

Usage

The idea is that you hook EventCache into your normal event code. You simply add the information you need to set the event to the EventCache using the add method.

EventCache has the following properties and methods:

listEvents
An array whose items are arrays which contain the information in the following order: node, sEventName, fHandler, bCapture. See add for more information.
add(node, sEventName, fHandler, bCapture)
Add new information to listEvents.
node
A reference to the node on which the event has been set.
sEventName

The name of the event.

Please note: this name must not be prefixed with on for any browser but Internet Explorer. The actual event you use must be prefixed with on. For example, if your event is onclick, sEventName must be click. If you use a custom event you can’t use foo, but you have to use onfoo. Of course sEventName must be foo.

fHandler
A reference to the function which handles the event.
bCapture
A boolean which determines whether the event is triggered in capture mode or not. Does not apply to Internet Explorer.
flush()
Remove all cached events.

Please note: You have to make sure that EventCache.flush is fired on unload!

Examples

I’ve made two testcases. One with memory leaking, and one which uses Event Cache. You can also try the Event Cache testcase with feedback

As you can see these examples don’t use browser detection to see if cleaning the references is really necessary. That’s something for your own implementation.

Other Methods

There are some other ways to solve this problem. One would be to never keep references at all, but closures are too handy to do this. Another solution is to iterate over all elements in a document and remove specific events. This is the solution that Aaron Boodman proposed.

The downside with this method though is that it only works for events which use the so-called DOM 0 event model: element.onclick = function(){};. If you use element.attachEvent() or element.addEventListener() you need to remember the specific values you used to set the event, which means you have to cache it.

Considerations

The cache keeps references to the element on which you’ve set an event and the event handler. This means that it consumes memory itself. It also takes time to remove the events on unload.

Download and Licensing

EventCache is released under the CC-GNU LGPL. You can download version 1.0.

And Finally

If you have any questions about this article or the code, please contact me.

Secure Gmail

posted March 8th, 2005, no comments, tagged ,

A Greasemonkey extension which ensures Gmail uses the secure connection.

September 16th: Due to a bug in Deer Park beta 1, the script no longer worked. Aaron Boodman of Youngpup fame fixed it… and I’m pretty excited about that (hey, the guy behind Greasemonkey fixes my one-line script, by adding two lines, great!). Anyway, hit the download link below to catch the latest version.

June 23th: Updated script because Gmail has changed it’s domain.

Download.

MyBlogLog Dissected

posted March 4th, 2005, no comments

Via Merlin of 43 Folders I found out about MyBlogLog, a service which tracks outbound links from your site. It does this by detecting clicks on the body, after which it determines if the click originated from a link. If this is the case it then sends the URI, link text and a timestamp of when the click occured to a server by constructing a new Image and setting its src property.

Okay, so I’ve already explained how it worked in the introducing paragraph… so much for dissecting! Well, there are a few comments I’d like to make about the JavaScript, including tips to make the code invisible to the visitor and some ramblings about privacy. Read on!

4th March 2005: I sent a link with this article to the MyBlogLog people and they replied to me within 20 minutes. They’re going to take my tips into account, so this article will probably be outdated in a couple of days. But don’t worry, I’ll write a follow-up ;-)

19th June, 2005: I checked the JavaScript again today, and the only thing which has been changed is that the methods and variables are now all prefixed with cs. No follow-up necessary, I’d say…

The Code

You can find the code at here. The original code was wrapped in a comment, but I’ve removed it here so you can see the code from the browser.

I have to say here that the sign-up process was really fast. Just fill in the form at the front page and you’re ready to go.

I’m afraid I don’t like the code that much, it does its work, but there are lots of things which could be improved. For example, the script is loaded externally: this means there is no need to wrap it in a HTML comment.

Another issue I have with it is that its functions and a some variables are in global scope. This is a bad idea if you want to write a script for people to use in their own websites. Variables like url and trackUrl are too likely to interfer with other scripts. It’s interesting to see though that a different variable, cs_url_trker, and some of the functions, are prefixed with cs_. The variable which holds the ID is prefixed with mbl though. This prefixing makes the variables less likely to interfer with other scripts — the question remains why the prefixes are different.

Some other parts of the code are illogical. Take this code, for example:

if(tg.innerHTML){ text = tg.innerHTML; } //most browsers
else if(tg.innerText){ text = tg.innerText; } //ie only
else if(tg.text){ text = tg.text; } //mozilla only
else{}

innerHTML is indeed supported by most browsers, including IE and Mozilla. Which means that the innerText and text properties will never be used.

And what’s up with that extra else{}? Is it meant to provide a fallback for browsers which don’t support innerHTML in true XHTML pages? I hope that’s the cause, but this empty else condition is used elsewhere in the code as well:

if(document.body){
  document.body.onclick = cs_det_oc;
}else if(document){
  document.onclick = cs_det_oc;
}else{}

Keeping The Function Alive

I did found an interesting hack in the code, namely the pause() function:

function pause(numberMillis) {
  var now = new Date();
  var exitTime = now.getTime() + numberMillis;
  while(true){
    now = new Date();
    if(now.getTime() > exitTime){ return; }
  }
}

pause() is called in this piece of code:

function cs_track_oc(text, url){
try{ var now = new Date(); trackURL = cs_url_trker + '?t=2&u=' + url + '&te=' + text + '&i=' + mblID + '&now=' + now.valueOf(); var x = new Image(); x.src = trackURL; pause(1000); }catch(err){ } }

Appearantly this is used to make sure that data is indeed send to the server. From what I understand from the code the request to load a new image stops if the function returns before the request is made. This “timeout” makes sure the request is made.

It would’ve been nice to document this, though. My first thought was that this was an alternative to setTimeout()

Improving the Script

The issues I pointed out above can be easily fixed. And, while that’s being fixed, we can also make the script invisible to the visitor. Here’s how.

By wrapping the code in an anonymous function you create a closure. This means that the functions and variables which are now defined in global scope will exist in the scope of that closure. And, even better, by using block scope you can completely hide the code from the visitor.

Well, almost. The events are set using document.body.onclick or document.onclick, which is still visible to the outside world. If you’d use a DOM1 way of setting the event, though, this becomes a non-issue. The added benefit here is that another script will not “accidentally” hijack (or overwrite) the onclick event. Now the code is completely hidden from the visitor.

Well, almost. The script tag still exists. If the site which uses MyBlogLog really wants to hide it, it could point the script tag to a URI on the site which redirects to the MyBlogLog script.

Privacy Concerns

Scripts like these raise various questions. Do you want the sites you visit to be able to tell which links you click? Are sites even allowed to track this? I don’t know the answer to the second question, but there are ways to prevent this from happening.

For this specific script you can disallow images from third party domains. A possible workaround for this is that the site owner runs this script and the tracker on his own server. Therefore the only real solution is to disable JavaScript.

But There Are More!

There are a few other tricks people can use to get to know more about you amd your browsing habits. One it to use the CSS visited pages disclosure. A more advanced monitoring system is Stephen Cote’s IMNMotion Behavior Monitor.

In view of these other techniques MyBlogLogs is just one of many. I’m not sure if this specific method creates privacy concerns either: the page you visit knows where you came from, why shouldn’t the page you left know where you went to?

Where to Replace

posted December 30th, 2004, no comments, tagged

I got a question today on where you should put the replacement code for sIFR. Here’s that e-mail, with reply:


cheekygeek wrote:

Mark, I’ve asked this on a couple blogs, but forgot which ones so don’t know if I got an answer.

The replacement code for sIFR can go at the bottom of the page OR in the sifr.js file. Is there a rendering-speed improvement to putting it one place or the other? I wonder if you could list the most important factors in getting the sIFR headlines to render the fastest (small/few font.swfs being used, etc.)

Also, does it matter how you call the sifr.js file (speedwise?)

Thanks!

Hey,

The explanation was covered in an earlier version I believe, but it works like this: if you put the replacement code in the JavaScript file, it’ll be executed on onload or when you call sIFR(). If you put the replacement code in the body, it’ll be executed immediately.

Effectively, this means that you could put the replacement code in the JavaScript and call sIFR() in the body. It won’t make any difference.

The reason there are two methods to replace elements in sIFR is that the onload event is only fired when the page has fully loaded — including all images and other external files. This can take much longer than the actual rendering of the page, so replacing inside the body will make sIFR look faster.

There is a downside to this, however. Elements which haven’t been rendered correctly (imagine an image without width and height attributes) can affect the layout, and in effect (no pun intended!) this can affect the rendering of replaced elements (as these are based on the dimensions of the original elements).

Also, in Safari and in Gecko browsers in XML mode the page isn’t rendered at the moment you would normally replace the elements in the body. For these browsers the replacement is postponed until onload.

Take care,

- Mark

Named Arguments

posted December 30th, 2004, no comments, tagged ,

For sIFR RC3 I wrote a handy little piece of code which allows users to use named arguments in their JavaScript code. For example, if you have a function foo which takes five arguments, and you only need to set the first and the last, this can be quite cumbersome:

function foo(one, two, three, four, five){
    // do something
};

// let's call foo() with only the first and last argument
foo("hello", null, null, null, "world");

sIFR takes thirtheen arguments, a lot of which are optional. As you can hopefully understand using named arguments is lots easier.

Interlude – For the True JavaScript Hacker

If you’re really interested in how all this all works, and how you can implement it in your own scripts… please be patient. I still have to write about the inner workings of my named arguments implementation. Sorry!

Syntax & Placement

In order for named arguments to work you need to pass a special construct to the function. The syntax of this construct is like this:

named({
    name : value,
    name : value
})

named({ is the beginning of the construct. name : value, is the syntax you need for each argument. name is the name of the argument and value is of course the value. You end the construct with }).

Due to a bug in Internet Explorer and Opera you have to be careful not to end the last argument with a comma. There will be a JavaScript error and the code will break.

By convention you pass this construct to the function as the last argument.

Example

Let’s look at the function foo from earlier in this article. Using named arguments the function call will look like this:

foo(named({one : "hello", five : "world"}));

Much easier, eh?

Here’s an example where we pass some other arguments to the function before the construct:

foo("the", "ocean", named({three : "breathes", five : "salty"}));

Here we passed on the arguments one, two, three and five to the function.

Applying it to sIFR

sIFR takes the following arguments (in this order): sSelector, sFlashSrc, sColor, sLinkColor, sHoverColor, sBgColor, nPaddingTop, nPaddingRight, nPaddingBottom, nPaddingLeft, sFlashVars, sCase, sWmode

You can find out what these arguments do in the readme.txt in the download. The names of the arguments above are the same as those which you use in the named arguments construct. For example:

sIFR.replaceElement("h1", "./vandenkeere.swf", named({sColor : "#000", sCase : "upper"}));

Here we passed on the arguments sSelector, sFlashSrc, sColor and sCase to the sIFR.replaceElement function.

Usage Ideas

There are more ways named arguments can simplify your (sIFR) life. You could also store the construct in a variable and use it as a template:

if(typeof sIFR == "function"){
    var template = named({sFlashSrc : "./vandenkeere.swf", sColor : "#000", sCase : "upper"});
    sIFR.replaceElement("h1", template);
    sIFR.replaceElement("h2", template);
};

No doubt more things are possible, but I’m leaving that to your imagination.

Download and Licensing

Just as sIFR the named arguments code is licensed under the CC-GNU LGPL. You can also download the source.

And Finally

If you have any questions about this article or the code, please contact me.

Named Arguments

posted December 30th, 2004, no comments, tagged ,

For now, please see the explanation for sIFR users.

Version Update – Konqueror Crazyness

As of today (Jan. 4, 2005) Named Arguments is at version 1.0.1. A bug was discovered where browsers based on an old Apple WebKit / Konqueror version (WebKit v85) would throw a JavaScript error:

Anonymous function hack: eating identifier named_Arguments

Anonymous function hack: eating identifier named_extract

This was caused by naming an anonymous function:

named.Arguments = function named_Arguments(oArgs){

Removing the name and the script worked again:

named.Arguments = function(oArgs){

I searched for the error message in Google and found what looks like the source code for the Konqueror JavaScript engine… and I quote:

00524       // Lookup for keyword failed, means this is an identifier
00525       // Apply anonymous-function hack below (eat the identifier)
00526       if (eatNextIdentifier) {
00527         eatNextIdentifier = false;
00528 #ifdef KJS_VERBOSE
00529         UString debugstr(buffer16, pos16); fprintf(stderr,"Anonymous function hack: eating identifier %s\n",debugstr.ascii());
00530 #endif

Also note that I had to apply a workaround earlier because the Konqueror JavaScript engine doesn’t deal very well with constructors for classes:

named.Arguments.prototype.constructor = named.Arguments;

Things like this make we want to throw the computer out of the window – luckily Mike found the bug, so I had no reason to throw my computer out of the window ;-)

2005

posted December 23rd, 2004, no comments

The more lucky people among you have recieved one of these in real life, but I can’t send everyone a card, so here’s the digital equivalent:

Closeup of two Christmas cards next to each other

Have awesome holidays, and all the best for the new year!

- Mark

The One Man Company With Three Employees

posted December 17th, 2004, no comments

I’ve known Mr. Jones for the largest part of my life. Actually ever since I moved here. Now that he is dead, I can’t help but think about him. I don’t know why, sure, I liked the guy, but that’s it. yet I constantly feel the urge to write the story of his life, as I know it.

You have to know that Mr. Jones was a workaholic. He worked hard. Really hard. Of course there is nothing wrong with workaholics, or at least I don’t think there’s anything wrong with anybody, save some psychopaths, but, you know, workaholics work really hard. That sucks.

Before I proceed, though, I need to tell you something important about Mr. Jones. Physically he was a workaholic, but not in his head. Well, I suppose this isn’t strictly true. He was schizophrenic. And the Mr. Joneses didn’t know about the other themselves.

Mr. Jones ran his own company. He was specialized in advertising, or so everyone thought. I’ve never seen an ad by him, not even for his own firm. And, as with all companies which don’t seem to have any business, and yet stick around, gossip eventually, and then continually, spread about Mr. Jones Advertising.

One day it would be a maffia front, the other day it was CIA. What was there to spy upon in such a calm rural village nobody knew, though. From time to time teenage boys would break in, in an attempt to impress their girlfriends, though nothing interesting was ever found. This, of course, amounted to the gossip.

The really interesting thing about the company I discovered only months after Mr. Jones had died. The firm had paid taxes for three employees: Mr. Jones, director; Mr. Jones, secretary and Mr. Jones, creative mind. I also managed to track down a client of the company.

This client told me a curious story about Mr. Jones Advertising. He had once hired the firm. While there, on his first visit, he saw the secretary first, bitching about not being able to find the director. Then he would leave the room, only to return five minutes later, complaining the secretary couldn’t be found and excusing for the delay. He then welcomed Mr. Client and introduced himself as Mr. Jones, owner.

The client, completely dumbfounded, asked if Mr. Jones had any twin brothers. “No,” Mr. Jones answered, “my twin brothers are long dead!”. Of course this only confused the client even more. He told me he thought Mr. Jones was crazy, and he pitied him. In an attempt to help Mr. Jones he repeatedly hired him to do some ads, but he never published them. Occasionally he would check back on Mr. Jones. He was the good samaritan in Mr. Jones’ life.

After hearing of his death, the client decided to attend his funeral. I never saw the client there, but I did see two people with a strong resemblance to Mr. Jones. Or perhaps I did not, when I looked again to take a better look, they had vanished.