published about 1 month ago (30.08.2008 11:57)

eBoy pixel art

click the image to start the storm.

<script type="text/javascript"> //<![CDATA[ var villageImages = ['prestorm-16k-1', 'poststorm-11k', 'reconstructed-05k'].map(function(name) { // too lazy to use regexes ... return 'http://me.phillipoertel.com/files/shp-village-' + name + '-tm.jpg'; }); // preload images villageImages.each(function(url) { (new Image()).src = url; }) Event.observe('eboy-village', 'click', function() { var img = $('eboy-village'); var currentImageNr = villageImages.indexOf(img.src); var newImage = villageImages[currentImageNr + 1] || villageImages[0]; img.src = newImage; }); //]]> </script>

Posted in ,  | Tags ,  | no comments | no trackbacks

published 11 months ago (24.11.2007 20:39)

Firefox LiveHTTPHeaders bugfix for Alt-L / @

the firefox LiveHTTPHeaders extension is a great tool for monitoring the http requests going on between your browser and a server—ideal for debugging requests or optimizing caching.

however, the extension has a small but very annoying bug, especially on Mac OS X. it’s sidebar shortcut is Alt-L, which is already occupied by the @ sign. so with this extension installed, you can’t type @s anymore. aaarrgh!

the bug is already (somewhat) fixed in CVS, but it doesn’t look like there’ll be an official release anytime soon. so i dove into the code, did the bare minimum to fix the problem, and made a new xpi. install the patched extension here: livehttpheaders-cvs-patched-071124.xpi.

i moved the sidebar shortcut to Shift-Cmd-J. ”!!?$ Shift-Cmd-J ???!!”, you ask?
“why from Alt-L to Shift-Cmd-J?” well, first, the CVS version already moved the command to Shift-Cmd-L. good. unfortunately Shift-Cmd-L (and Shift-Cmd-K) are already used by the firebug extension, so i moved the key leftwards to the first unoccupied one.

as mentioned above, i did the bare minimum to get the extension to work. no perfection here … but i hope it works for you, too!

Posted in , ,  | Tags , , , ,  | 1 comment

published about 1 year ago (09.09.2007 16:06)

refactoring five year old javascript code to use the prototype.js library

i’ve long been skeptical of javascript libraries. web 1.0’s myriads of “dhtml” / cross browser libraries didn’t improve the javascript programming experience much. at least there was no library i found so useful that i’d use it in all my projects. so all i had was a couple of snippets picked up here and there, like addEvent() / removeEvent() by the good ole Scott Andrew.

web 2.0 brought a lot of new buzzwords, and more frontend fun. and frontend fun requires: new javascript libraries! fortunately, these libraries seem to be different:

  • they are professionally written, complete with unit-tests
  • their developers don’t have to waste their time coding for horribly bad, pre-DOM browsers (Netscape 4, IE/mac anyone?).
  • they are better integrated in the core of the language
  • their developers have learned from their and other’s experience
  • there are a handful of popular libraries that have been widely adopted, which are continuously developed and improved, producing high quality, reliable code.
  • they focus on developer productivity, instead of eye-candy and supplying drop-in high-level components
  • computers have become faster, allowing to write code that makes developers more productive, although it requires more cpu

ENTER PROTOTYPE.JS

as an experiment, i wanted to refactor a web page i created in 2002, thomasgraggaber.com, to use a current javascript library. since i’ve already used prototype.js, the library is well-established, well-documented, robust (and bundled with ruby on rails), i decided to use it for the experiment.

so here are the stats for the “application” javascript, i.e. everything that’s specific for the site:

  • original lines of code: 571 (65 of them in cross-browser helper methods)
  • lines of code after refactoring to use prototype.js: 445. that’s 22% less code.
  • the cross-browser helper methods were “library code” as well. ignoring those it’s still 10% less code.

of course you add 3271 lines of code for prototype.js (in v. 1.5.1). what’s important however: you usually don’t have to maintain, debug and completely understand the internals of prototype.js when creating your application.

the major code changes are:

Iterators, $$ and Event.observe

before:

var allDivs = document.getElementsByTagName('DIV');
for (i=0; i < allDivs.length; i++) {
    // ...
    else if(allDivs[i].className == 'sidebar')
    {
        addEvent(allDivs[i],"mouseup",dragManager);
        addEvent(allDivs[i],"mousedown",dragManager);
    }
    // ...
}

after:

$$('.sidebar').each(function(div) {
    Event.observe(div, 'mouseup', dragManager);
    Event.observe(div, 'mousedown', dragManager);
});
  • $$() is the swiss army knife for selecting elements in a document. give it a css selector and it will return all matching items in a collection. it probably isn’t the most efficient thing to use, but fine here, and is short. prototype’s document.getElementsByClassName() could also have been used.
  • the temp variable allDivs can be removed, since the iterator function #each works with the result of $$’s return value directly.
  • Event.observe is prototype’s way of registering an event handler cross-browser.

another example. nothing new here, but it takes even more advantage of the iterators:

// photosThumbs viewer
var photosThumbsNodeList = contentDivs['photosThumbs'].getElementsByTagName('IMG');
for (i=0; i < photosThumbsNodeList.length; i++)
{
    addEvent(photosThumbsNodeList.item(i),"click",photoManager);
    addEvent(photosThumbsNodeList.item(i),"mouseover",photoManager);
    addEvent(photosThumbsNodeList.item(i),"mouseout",photoManager);
}
var newsThumbsNodeList = contentDivs['news'].getElementsByTagName('IMG');
for (i=0; i < newsThumbsNodeList.length; i++)
{
    addEvent(newsThumbsNodeList.item(i),"click",photoManager);
    addEvent(newsThumbsNodeList.item(i),"mouseover",photoManager);
    addEvent(newsThumbsNodeList.item(i),"mouseout",photoManager);
}
var portraitThumbsNodeList = contentDivs['portrait'].getElementsByTagName('IMG');
for (i=0; i < portraitThumbsNodeList.length; i++)
{
    addEvent(portraitThumbsNodeList.item(i),"click",photoManager);
    addEvent(portraitThumbsNodeList.item(i),"mouseover",photoManager);
    addEvent(portraitThumbsNodeList.item(i),"mouseout",photoManager);
}

becomes

// photosThumbs viewer
$w('photosThumbs news portrait').each(function(divName) {
    $$("div#" + divName + " img").each(function(img) {
        $w('click mouseover mouseout').each(function(eventName) {
            Event.observe(img, eventName, photoManager);
        });
    });
});
  • $$() is used again. it could be replaced by “$A($(‘border’).getElementsByTagName(‘div’))” if it gets too slow.

the $w function

before:

// add event handlers
var naviDivs = document.getElementById('border').getElementsByTagName('DIV');
for (i=0; i < naviDivs.length; i++)
{
    addEvent(naviDivs.item(i),"click",ggNaviManager);
    addEvent(naviDivs.item(i),"dblclick",ggNaviManager);
    addEvent(naviDivs.item(i),"mouseover",ggNaviManager);
    addEvent(naviDivs.item(i),"mouseout",ggNaviManager);
    addEvent(naviDivs.item(i),"mousedown",ggNaviManager);
    addEvent(naviDivs.item(i),"mouseup",ggNaviManager);
}

after

// add event handlers
$$('div#border div').each(function(naviDiv) {
    $w('click dblclick mouseover mouseout mousedown mouseup').each(function(eventName) {
        Event.observe(naviDiv, eventName, ggNaviManager);
    });
});
  • $w is a nice adoption from ruby (where it’s %w). it splits the string argument by whitespace, returning an array of the items (“words”). very readable in this case.

Event.element(e), and returning only element nodes

e = (e) ? e : ((window.event) ? window.event : '');
eTarget = e.srcElement ? e.srcElement : e.target;   
while(eTarget.nodeName != 'DIV') {
    eTarget = eTarget.parentNode;
}

becomes

eTarget = Event.element(e);

nice.

  • prototype handles finding the event target in a cross-browser way. (note: prototype 1.6.0 will make this even simpler. the event handler will be bound to the event target, so the event target will be available in the “this” variable.)
  • prototype only returns element nodes, so the while() loop looking upwards for a div element becomes obsolete. it was required since it’s possible that the text node inside the element was clicked. this behavior allowed removal of a couple of these loops.

“DOM walking” with up(), down()

scrolledDiv = first_child(node_after(eTarget.parentNode.parentNode));

becomes:

scrolledDiv = eTarget.up('.box').down('.content')
  • firstchild() and nodeafter() were some of the utility methods to walk through the DOM, and already ignoring whitespace and text nodes. element.up(…).down(…) do the same, but are nicer, shorter, clearer to use.

show() and hide()

before:

function displayNavi()
{
    document.getElementById('sponsorFlash').style.display = 'none';
    document.getElementById('border').style.display = 'block';  
}

after:

function displayNavi()
{
    $('sponsorFlash').hide();
    $('border').show();
}
  • the code becomes more concise, and more expressive.
  • i don’t have to remember which elements require which display style in order to be displayed correctly (block/table/inline elements have different values).

conclusions

  • by using prototype.js i was able to remove 10% of the code
  • at the same time, the code is more readable than before
  • as an additional benefit, the site now probablyworks in safari (and all browsers that prototype supports)!

prototype.js rocks, because it’s developed by smart people, and because javascript itself rocks!

Posted in ,  | Tags , , , , ,  | no comments

published over 2 years ago (05.06.2006 20:22)

test your web-development knowledge

Posted in ,

published over 3 years ago (30.01.2006 15:53)

why we love internet explorer so much

what’s wrong with this javascript?

function showDetailInNewWindow()
{
   // set new window properties
   top    = 200;
   left   = 250;
   width  = 680;
   height = 300;
   // ... proceed to open new window
}

the error message ie gave me: “not implemented, line 26, character 7”. in _which _of the included js files, you ask? well if it gave that information out, the error would be too easy to find, wouldn’t it?! and when you finally debugged which file it was, the line number was a couple of lines off.

this error message is as useful as asking your grandma’s parrot what was wrong with the code. except the parrot’s reply would be more intelligent.

2 hours lost. talk about efficient development. i’ll consider writing my front-ends in xul.

the error was (i figure): you shouldn’t use “top” inside a function. JS has all global vars in function scope as well, and ie allows you to omit “window” in some expressions. i.e. ie thought i was trying to say “window.top = 200”. which it

  • should have just done
  • complained that it’s a read-only property
  • complained that the object window doesn’t have that (public) property

or maybe top is a completely reserved keyword, why didn’t it tell me???

of course i only had to resort to opening the details in a new browser window since IE first needed loads of hacks and javascript expressions to get the data table i am displaying to look it as i wanted, and then, second completely messed up the while document when dynamically embedding the detail information into the current page (“ajax”).

ie 6 is definitely web 1.0.

Posted in , ,

published over 3 years ago (11.09.2005 00:00)

Google Products Invasion

Google is … a search engine? Think again.

Google keeps presenting more and more innovative products and services – GoogleMaps, GoogleEarth, G(oogle)mail, Picasa, and so on. Incredible how creative and productive a couple of incredibly smart nerds can be.

Their latest product, which isn't yet mentioned on the products page, pioneers biointerface work: it's all about information digestion improvement. If you're too dumb to understand that info you just found with Google Search, have a drink. In fact, a beautifully packaged drink: Google Gulp (I recommend beta caroty).

If we only had enough of it, I'm sure people would stop complaining about Google becoming the next evil empire, and the world would finally become a better place. thank you, google …

goodbye IFRAMES

i just redid this document, now there's only one html page with all the stuff instead of several documents loaded in separate IFRAMES. frames are bad, IFRAMES aren't much better.

thanks to an all-css design & javascript to display the large images, it was a matter of 45 minutes. in firefox. did i already mention internet explorer sucks? well, for some time the site will be a mess in ie, and several things probably won't work. just contact me if you need sth.

Posted in , ,

published over 3 years ago (28.04.2005 00:00)

clean web design is worth it

Last Sunday – five days ago – i did a very quick redesign of my "business" website. it's now one single static html page, dressed up with css and some images for headline text. semantically marked up, that is, put <h1> and <h2> tags around the headlines, no tables for layout, and so on. valid xhtml 1.0 transitional. the page weighs 7.45kB – including the images.

And ta–da: Google already picked it up and ranks me #1 … very nice. ok. #1 for my name. (which is an improvement over not appearing at all. like before.)

for the headlines i used the fahrner image replacement technique. that way it's possible to use custom fonts and nice text rendering for the headlines. nevertheless non-graphic browsers - and search engines - can still read the headlines, since they are present in the html as well. :-).

Posted in ,

published over 4 years ago (03.04.2005 00:00)

so many toys, so little time!

CSS Fiddling

Instead of rebuilding from scratch (using a framework or blogging system i have yet to pick) i played around with the CSS trying to make the page look a little cleaner.

Ruby

Ruby is a modern scripting language from japan, with a quite different but concise syntax and feature set.

a simple example - creating and looping over an array

… in ruby:

theBig5 = %w[lion rhino buffalo leopard elephant]
theBig5.each {|x| puts "a #{x} is a big and dangerous animal!"}

… the same in php:

$theBig5 = array('lion', 'rhino', 'buffalo', 'leopard', 'elephant');
foreach ($theBig5 as $x) {
  print "a $x is a big and dangerous animal!\n";
}

… and in javascript:

theBig5 = new Array('lion', 'rhino', 'buffalo', 'leopard', 'elephant')
for (x in theBig5)
  document.writeln('a ' + x + ' is a big and dangerous animal!')
}

… and in c#:

string[] theBig5 = {"lion", "rhino", "buffalo", "leopard", "elephant"}; 
foreach(string x in theBig5) {
   Console.WriteLine(x);
}

Ruby on Rails

… is a framework for quickly building web applications. The basic components and architecture are already there (filesystem layout, database access, conventions, clean urls, logging, etc.) is already there, relying on standard patterns. So you only implement your stuff using the prepared structure. The application uses the MVC pattern, has some very neat features like ActiveRecords (Access/Modify database data through objects), Routes (Clean URLs without having to wrap your brain around mod_rewrite) and a command-line script to generate code skeletons you then fill with your code. very nice. so all you have to do is learn how to use that and you can develop web applications very fast.

Disclaimer: If you’re a ruby expert, please forgive me any errors or misconceptions. I have been playing with R and RonR for two days only.

XMLHttpRequest

by using this technology, the information on web sites can be updated without reloading the whole page. This way, the user experience is much more application-like. Instead of click-button – page goes blank – wait – new content is there, it’s now click button – wait – page is updated. A lot faster.

Online Examples:

this combination of web technologies technology is generally referred to as “Ajax”

No se puede tocar

Posted in , , ,