published on 12.07.2009 15:52.

Updated is-gd for Ubiquity

See this post for a description of Ubiquity.

I adapted the script for the API changes introduced with 0.5, and put the script on github.

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

published on 28.03.2009 19:46.

Ubiquity is the shit!

Ubiquity is a Mozilla Labs add-on for Firefox. It’s a new way of interacting with the browser and web content. Imagine Quicksilver, but for everything that can be reached from the browser. Common examples are controllign the browser, translating text on a web page in-place, looking up an Google maps address in-place, you imagine. It let’s you throw out half of the other add-ons. Oh, and on the Mac, it integrates with Growl.

And it’s super-easy to extend with JavaScript!

Example – using is.gd to shorten URLs

I hacked this together using the pretty good documentation. The code takes the text selection (an URL) and shortens it via is.gd.

CmdUtils.CreateCommand({

  name: "is-gd",

  description: "Replaces the selected URL with a short URL generated with is.gd.",

  author: { name: "Phillip Oertel" },

  takes: {"url to shorten": noun_arb_text},

  execute: function(urlToShorten) {
    var baseUrl = "http://is.gd/api.php";
    var params  = {longurl: urlToShorten.text};
    jQuery.get( baseUrl, params, function( shortUrl ) {
      CmdUtils.setSelection( shortUrl );
    });
  }

});

I embedded the above script into this page—if you installed the Ubiquity add-on, Firefox will notify you. Install the script, then select an URL anywhere on the page or in the location bar, press Alt-Space and type “is-gd”. The selected text will be replaced with a shortended URL. Neat!

They really have easy administration and extension in mind—go to chrome://ubiquity/content/cmdlist.html to get an in-browser interface to Ubiquity. You can directly write the scripts in there. Oh, and did I mention it already ships with jquery ?

Posted in , ,  | no comments | no trackbacks

published on 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 on 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 on 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 on 05.06.2006 20:22.

test your web-development knowledge

Posted in ,

published on 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 on 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 ,