Emil’s Chronicle - The journal of Emil A Eklund

Going to Google

September 13th, 2006

A few months ago I accepted a job offer from Google. Starting next week I’ll be working for them.

For the last couple of months I’ve been busy finalizing my work at Basset, completing the projects I’ve been involved in and handing over my responsibilities. While Basset has been treating me well during the five or so years I’ve been working there I felt it was time to move on.

Google has some of the best DHTML people and brightest programmers, a chance to work with some of them is very exciting.

Without Erik’s encouragement I doubt it would have happened, thank you!

I can’t wait to get started!

SpellingCow followup

September 5th, 2006

Just got the following message from Craig Nuttall, the creator of SpellingCow, as a comment to my LiteSpellChecker post:

Hello All. Right now I am spell checking this message as I type. I am the creator of SpellingCow ( http://www.spellingcow.com ). I would like to thank Emil for his hard work as he inspired my creation. SpellingCow operates completely different in many ways from Emil’s checker, but has the same idea of making the textarea transparent and showing a markup div in the background.

Emil was gracious enough to share his code with the world, benefiting me and who knows how many others. I wish I could open source all of my code, but I just cannot unobfuscate it - though I’d gladly give Emil a peek =] I have open sourced the communication method I’ve developed which allows SpellingCow to be installed with a single line of code on any website or run as a favelet. It’s called COWS Ajax ( http://cows-ajax.sourceforge.net ).

I would like to give some pointers for you guys beyond this though, just to contribute a little more. First of all, Emil’s demo works well b/c the textarea he is using is so large that it masks a lot of problems. The PRE whitespace style type is not recognized by IE and that creates a lot of problems. Also in firefox, this cause long lines to not wrap and completely blows out the display. The fix for this is to drop the PRE whitespace style type entirely. For special cases where you need to render something like consecutive spaces, then wrap just the spaces with the PRE whitespace type. That solves the problem for FF. But IE has a further problem in that in strips leading whitespace on spans. But if you use createTextNode to insert it into the DOM tree directly, that will get you the desired effect.

So… I wish I could make the unobfuscated and commented source available but I can’t. However, the above paragraphs represents MANY hours of tinkering and I think will be quite valuable to someone wanting to OSS what Emil started. Between that and COWS Ajax, I hope you all feel that I am “giving back” at least something ;-)

Thanks again Emil… you rock!

Now that it has been settled that it’s not a cheap ripoff but rather a service inspired (and partly based on) my work it feels a lot better.

His other claims are true too, quite a few of the bugs and limitations found in my implementation has been resolved, and his description of the conceptual changes required for that could prove quite valuable.

Thanks and sorry about my accusations.


September 4th, 2006

Browsed slashdot earlier today, and saw a post about SpellingCow, a remotely hosted ajax spell checker.
My initial reaction was “cool, someone picked up on my ajax spell checker idea and made something useful out of it”.

However as soon as I tried it I noticed a remarkable resemblance to the LiteSpellChecker component I wrote, as a proof of concept, about a year ago.

Screenshot of my LiteSpellChecker

Screenshot of SpellingCow

That menu looks a bit too similar for it to be a coincidence so I decided to dig a bit deeper.
Perhaps they’d actually based it on my implementation and fixed some of the bugs in it, that would have been nice. As both my spell checker implementations are available under the MIT license there’s nothing preventing anyone from using my code as a base or starting point.

Their javascript file starts out with the following comment and all code is obfuscated.

 * © 2004-2006 SpellingCow Software - All rights reserved.  This is not free software.

Not satisfied with that I started to de-obfuscate the code. Quite soon I encountered segments that looked awfully familiar:

Style declaration

webfx-spellchecker-menu {
	border: 1px solid;
	border-color: threedlightshadow threeddarkshadow threeddarkshadow threedlightshadow;
	position: absolute;

.webfx-spellchecker-menu .inner {
	border: 1px solid;
	border-color: threedhighlight threedshadow threedshadow threedhighlight;
	background: threedface;
	padding: 2px;

.webfx-spellchecker-menu a {
	display: block;
	font: menu;
	color: menutext;
	padding: 1px 5ex 1px 3ex;
	text-decoration: none;

.webfx-spellchecker-menu a:hover {
	background: highlight;
	color: highlighttext;

.webfx-spellchecker-menu .separator {
	border-top: 1px solid threedshadow;
	border-bottom: 1px solid threedhighlight;
	overflow: hidden;
	margin: 2px;

webFXSpellCheckHandler.invalidWordBg =
'url(http://me.eae.net/stuff/spellchecker/images/redline.png) repeat-x bottom';
	border: 1px solid ;
	border-color: threedlightshadow threeddarkshadow threeddarkshadow threedlightshadow;

this.g('.sc_ayt_menu .inner','
	border: 1px solid ;
	border-color: threedhighlight threedshadow threedshadow threedhighlight;
	background: threedface;
	padding: 2px;

this.g('.sc_ayt_menu a','
	display: block;
	font: menu;
	color: menutext;
	padding: 1px 5ex 1px 3ex;
	text-decoration: none;

this.g('.sc_ayt_menu a:hover','
	background: highlight;
	color: highlighttext;

this.g('.sc_ayt_menu .separator','
	border-top: 1px solid threedshadow;
	border-bottom: 1px solid threedhighlight;
	overflow: hidden;
	margin: 2px;
	padding: 0px;

this.g('#spellingcow_div .red_span',
	az + "background: url(" + ayty + "images/redline.png) repeat-x bottom;

The declaration for the menu is pretty much exactly the same and even though SpellingCow marks misspelled words with a yellow background there’s still a style declaration using reline.png, precisely matching the one I use.

Menu Creation

webFXSpellCheckHandler._init = function() {
	var menu, inner, item;

	menu = document.createElement('div');
	menu.id = 'webfxSpellCheckMenu';
	menu.className = 'webfx-spellchecker-menu';
	menu.style.display = 'none';

	inner = document.createElement('div');
	inner.className = 'inner';

	item = document.createElement('div');
	item.className = 'separator';

	item = document.createElement('a');
	item.href = 'javascript:webFXSpellCheckHandler._ignoreWord();'

function aytv(){
	this.ad = document.createElement('div');
	this.ad.className = 'sc_ayt_menu';
	this.ad.style.display = 'none';

	var et = document.createElement('div');
	et.className = 'inner';

	var bb = document.createElement('div');
	bb.className = 'separator';

	bb = document.createElement('a');
	if (aytaa) { bb.href = 'javascript:aytk.ignore_word();' }
	else { bb.href = 'javascript:oSpell.ignore_word();' }

Code for constructing the popup menu on initilization. The SpellingCow implementation does it exatly the same way as mine, it even uses the same class and method names.

Displaying suggestions

webFXSpellCheckHandler._showSuggestionsMenu = function(e, el, word, instance) {
	var menu, len, item, sep, frame, aSuggestions, doc, x, y, o;

	if (!webFXSpellCheckHandler.words[word]) { return; }

	menu = document.getElementById('webfxSpellCheckMenu');
	len = menu.firstChild.childNodes.length;
	while (len > 2) { menu.firstChild.removeChild(menu.firstChild.firstChild); len--; }
	sep = menu.firstChild.firstChild;

	aSuggestions = webFXSpellCheckHandler.words[word][1];
	len = aSuggestions.length;
	if (len > 10) { len = 10; }
	for (i = 0; i < len; i++) {
		item = document.createElement('a');
		item.href = 'javascript:webFXSpellCheckHandler._replaceWord(' + instance + ', "' + aSuggestions[i] + '");'
		menu.firstChild.insertBefore(item, sep);
	if (len == 0) {
		item = document.createElement('a');
		item.href = 'javascript:void(0);'
		item.appendChild(document.createTextNode('No suggestions'));
		menu.firstChild.insertBefore(item, sep);

	var n;
	for (n = 0; n < webFXSpellCheckHandler.instances.length; n++) {
		if (webFXSpellCheckHandler.instances[n].doc == el.ownerDocument) {
			frame = webFXSpellCheckHandler.instances[n].el;
			doc   = webFXSpellCheckHandler.instances[n].doc;
	}	}

	x = 0; y = 0;
	for (o = frame; o; o = o.offsetParent) {
		x += (o.offsetLeft - o.scrollLeft);
		y += (o.offsetTop - o.scrollTop);

	if (document.all) {
		menu.style.left = x + (e.pageX || e.clientX) + 'px';
		menu.style.top  = y + (e.pageY || e.clientY) + (el.offsetHeight/2) + 'px';
	else {
		menu.style.left = x + ((e.pageX || e.clientX) - document.body.scrollLeft) + 'px';
		menu.style.top  = y + ((e.pageY || e.clientY) - document.body.scrollTop) + (el.offsetHeight/2) + 'px';
	menu.style.display = 'block';

	webFXSpellCheckHandler.activeWord = word;
aytv.prototype.show_menu = function(e,ak) {
	var bb, bi, co = document.getElementById(ak), fv = this.ad.firstChild.childNodes.length;

	while (fv > 2) { this.ad.firstChild.removeChild(this.ad.firstChild.firstChild); fv--; }

	var cs = co.innerHTML;
	bi = aytk.m[cs].ce.split(', ');
	fv = bi.length;

	if (fv > 10) { fv = 10; }
	for (var i = fv - 1; i >=0; i--) {
		bb = document.createElement('a');
		if (aytaa) { bb.href = 'javascript:aytk.replace_word("' + ak + '", "'+bi[i]+'");' }
		else { bb.href = 'javascript:oSpell.replace_word("' + ak + '", "' + bi[i] + '");' }

	if (aytk.m[cs].ce == '' ) {
		bb = document.createElement('a');
		bb.href = 'javascript:void(0);';
		bb.appendChild(document.createTextNode('- No suggestions -'));

	var db = 0, da = 0;

	if ((document.documentElement) && (document.documentElement.scrollTop)) {
		db = e.clientX + document.documentElement.scrollLeft;
		da = e.clientY + document.documentElement.scrollTop;
	else if (document.body) {
		db = e.clientX + document.body.scrollLeft;
		da = e.clientY + document.body.scrollTop;
	else {
		db = e.pageX;
		da = e.pageY;

	if (document.all) {
		this.ad.style.left = db + 'px';
		this.ad.style.top = da + (co.offsetHeight/2) + 'px';
	else {
		this.ad.style.left = db + 'px';
		this.ad.style.top = da + (co.offsetHeight/2) + 'px';
	this.ad.style.display = 'block';

Code for populating the menu, the data it’s populated with is accessed i bit differently but the menu is cleared, populated and positioned in the same way. Even the ‘No suggestions’ label is the same.

Get Selection Code

WebFXLiteSpellChecker.prototype._getSelection = function() {
	if (document.all) {
		var sr, r, offset;
		sr = document.selection.createRange();
		r = sr.duplicate();
		r.setEndPoint('EndToEnd', sr);
		this._start = r.text.length - sr.text.length;
		this._end   = this._start + sr.text.length;
	else {
		this._start = this.elText.selectionStart;
		this._end   = this.elText.selectionEnd;
aytm.prototype.ch = function(return_is_selected) {
	if (document.all){
		var cn, es, offset;
		cn = document.selection.createRange();
		es = cn.duplicate();
		var gv = es.text.replace(/rn/g,'n'), fu = cn.text.replace(/rn/g,'n');
		if (return_is_selected) { return fu.length; }
		else { return gv.length - fu.length; }
	else {
		if (return_is_selected) { return (this.a.selectionEnd - this.a.selectionStart); }
		else { return this.a.selectionStart; }

Code for determining selection. The original method determines the start and end position while the SpellingCow one returns either the length of the selection or the start position, depending on the parameter.
Although it’s not an exact match there’s a striking resemblance, the SpellingCow one even has the same unused offset variable that I forgot to remove while testing it.

Node Updating

	i = 0;
	startNode = endNode = null;
	for (node = this.elCont.firstChild; node; node = node.nextSibling) {
		if (node.nodeType == 1) {
			str = (node.firstChild)?node.firstChild.nodeValue:'n';
		else { str = node.nodeValue; }
		n = str.length;

		if ((startNode == null) && (i + n >= startPos)) {
			startNode = node;
			word = str.substr(0, startPos - i);
		if (i + n >= endPos) {
			endNode = node.nextSibling;
			word += str.substr(endPos - i, n - (endPos - i));

		i += n;
	var ar = this.r.firstChild, bl = 0, fi = '';

	for (i = 0; ar; ar = ar.nextSibling ){
		if (ar.nodeType == 1){
			fi = (ar.firstChild)?ar.firstChild.nodeValue:'n';
		else { fi = ar.nodeValue; };
		bl += fi.length;

		if (bl >= this.ag) {
			return ((ar.className) && (ar.className == ‘red_span’))?ar.id:'’;

This one is not so obvious, it’s a part of the logic that determines where a change has been made. It loops through nodes and find the first and last word affected by counting characters. The SpellingCow one only includes the check for the last word.

This is just a few of the obvious similarities, there are plenty more.

I’m not saying that they’ve copied my LiteSpellChecker implementation, there are far too many differences in key parts of it for that.
However it seems quite likely that they’ve used my implementation as a starting point, and in some places it remains unchanged and shines through.

It’s not that I mind people using and extending my work, that is in fact exactly why I chose to talk about it and to release it under an open source licence. However removing the copyright notice is not okay and I can’t believe people still think they can get away with it simply by obfuscating the code.

And why? The MIT license clearly allows royalty free commercial usage, all that is required is that the copyright notice is left intact.

Turns out they actually give me some credit:

The red squiggley was produced by Emil A Eklund who also inspired much of the mechanics of SpellingCow. This is my omage to him. Thanks!!

That’s pretty much all it takes, just wish it wasn’t tucked away in the installation instructions section…

Update 2:
See the followup.

Printing Canvas

August 28th, 2006

Since I first started with IE Canvas the number one request has been printing support. The same applies to the successor, ExplorerCanvas and the WebFX Chart component.
In fact about 90% of the mails I’ve received about these projects has been about printing.

So a while ago I started to look into it, however Rostislav Hristov beat me to it. Thanks to him the current trunk version of ExplorerCanvas now supports printing of canvas elements in IE.

I’ve updated the WebFX Chart component to include the latest excanvas and also removed the media=”screen” declarations from the style sheet links.
As I don’t have access to a printer right now I haven’t been able to test it properly yet, however it shows up in print preview, both in IE and Mozilla and printed to file it looks fine.

Print preview in IE:
Print preview of WebFX Chart componenet in IE

Print preview in Mozilla:
Print preview of WebFX Chart componenet in Mozilla

Now that graphics generated client side can be printed in both major browsers the biggest obstacle has been removed and there shouldn’t been much holding it’s adaption back.

WebKit for Windows

August 7th, 2006

Now this is great news; Apple’s WebKit (the rendering component powering the Safari web browser) has been ported to Windows, complete with a browser called Swift. I doubt many will use it, at least for now, it’s not quite as mature as the other offerings. However having another browser to chose from is a good thing and I’m sure it will improve over time.

The most important point it that it will give web developers using Windows the ability to test their web pages and applications in a WebKit based browser, without having to acquire Apple hardware or install KDE. - Thus this could very well lead improved support for Safari and Konqueror.

Download from getwebkit.org.

In other related news, a windows port of the Evolution has finally been released; Evolution on Win32.

Evolution has been my e-mail client of choice ever since I first tried it some years ago. Now I can finally use it on all my computers. It’s a bit unstable under windows still and I can’t get the LDAP support to work properly for contacts, but other than that it seems quite complete and usable.

WebFX License Change

May 30th, 2006

We’ve just announced a license change over at WebFX, replacing our commercial/non-commercial/gpl triple with the Apache Software License 2.0.

Although most of the components available there are relatively old, the bulk of them from 2001/2002, some even older, I still believe some of them are relevant today.
With this license change more projects, open source as well as commercial, will hopefully be able to use and extend the collection of widgets, libraries and techniques we’ve created and refined over the years.

Head over there for the full announcement and post your feedback in the webboard.

The List, One Year Later

May 29th, 2006

About a year ago I wrote about Joel Spolsky’s list of things that cannot be well done in web applications.

  1. Create a fast drawing program.
  2. Build a real-time spell checker with wavy red underlines.
  3. Warn users that they are going to lose their work if they hit the close box of the browser.
  4. Update a small part of the display based on a change that the user makes without a full roundtrip to the server.
  5. Create a fast keyboard-driven interface that doesn’t require the mouse.
  6. Let people continue working when they are not connected to the Internet.

Last year Erik established that Item 2, 3, 4 and 5 where already accomplished and predicted that item 1 would be implemented within a year.

Now, a year later it looks like he was right; Rafael Robayna’s Canvas Painter may not be feature complete but it sure is fast.

That leaves item 6 as the only outstanding point, I doubt it will be that way for long though.

The recent introduction of dojo storage could well contribute to solving that, we’ll just have to wait for an application to utilize it to achieve offline usability..

Even more existing is the the implementation of Client-side storage in recent builds of Mozilla, including the preview release Bon Echo Alpha 3, which opens up the possibility to implement more exotic storage mechanisms.

Projects such as TrimQuery may become a lot more useful an relevant now that there’s a mechanism for storing large amounts of data on the client side.

This is going to be a very interesting year in the web development world!

Chart 1.02

April 16th, 2006

I’ve update the WebFX Chart widget to use the new, and superior, ExplorerCanvas IE Emulation layer instead of my old IECanvas one.

I’ve also included the excellent jsgraphics painter implementation contributed by Ma Bingyao in the official version.

This brings the number of painter implementations to three; canvas, svg and jsgraphics, although the svg one is still lacking, and the browser support now includes most, if not all, modern browsers.


March 28th, 2006

Shortly after I announced my Canvas in IE project a while back it became apparent that I wasn’t the only one trying to bring Canvas to IE. Glen, and Erik, had recently started a similar venture and suggested we join forces. Having one good IE implementation makes a lot more sense than two competing ones.

Check Glen’s site for the full story and a demo.

The actual implementation can be found on SourceForge as ExplorerCanvas.


March 14th, 2006

Been quite busy lately, work has occupied most of my time and it looks like it will be that way for the remainder of the winter and spring as well, most of which I’ll spend in Manila.

Anyway, it seems like Mark Finkle picked up the ball and started working on SVG in IE. Although not explicitly stated I’m assuming he’s aiming at supporting SVG Tiny, or a subset of it, rather than the full SVG specification.

Just as my Canvas implementation it’s using VML and behaviors. Unlike Canvas however, SVG is in a separate namespace and it seems like it should be possible to bind the behavior directly to the SVG elements, without the need for a separate initialization script. That should also allow dynamic creation of SVG elements, without having to manually initialize the emulation for them - which in turn could make adding IE support to a SVG powered website/application as easy as including an IE behavior. Assuming there’s a way to overcome the Content-Type problem (Mozilla requires a XML Content-Type type for inline SVG, which IE does not support).

Good luck Mark and keep up the good work!