2005-09-20

Free Opera!

I feel my conversion to Opera got more steam today, with the news of their ads gone for good now. Still, converring from a familiar environment to a non-familiar one is always a hassle. More, the less your destination environment does to mimic the feel of your source environment. So, I downloaded Opera 8.50, and played around a little. There are lots of goodies to be found, scattered in-between the lack of basics that you would have expected to find -- still a very mixed impression, in other words. I think I'd need to work a bit side by side with an already converted Opera fan, or better still, an Opera developer or (geeky) early adopter.

Ctrl+j brings forth a very nice view of all links on the page, where you quickly filter out everything but the links whose link text or target address matches (any substring) what you type. Just to start typing what you want, and when the list is short enough, tap tab to focus the link and enter to go there. (The Links view remains in a background tab rather than go away now, to my very slight irritation, but perhaps it's configurable, and either way it's a very minor nuisance.) Another very neat use case is when browsing an image gallery or directory listing, type "jpg" , tap tab, shift-End (to mark all occurrences) and Enter, and they're all loaded into a set of tabs in one fell swoop. Very neat. My brain expects tab switching to go on Ctrl+Page Up/Down, but by default it's on 1/2 (numeric pad or main keyboard).
You can switch key bindings more or less easily in the preferences views, but I think I'll try to learn living with the Opera default of using Ctrl+Page Up/Down as Page Left/Right and try to like it. (Ctrl+Home/End does not analogously move to the far left/right, though. Boring.)

Come so far I get Emacs itches, wanting to set up custom keyboard shortcuts to my own bookmarklet navigational aids with cushy key bindings, something I admittedly haven't done in Firefox yet (how do you do that?), but don't manage to find out how that would be done in Opera, either (how do you do that?). Why hasn't this been a basic additional field "keyboard shortcut" in the bookmark edit page of all browsers since the dawn of the web? Hm. An old bugzilla feature request ticket on the subject has been rotting away since the dawn of the millennium. At least now it has my vote on it, too.

It seems Opera allows for a more or less easy way of adding custom interface buttons, though not with keyboard shortcuts, from what I can see. (Why does every BlogThis! link work with pop up windows?! Mine, custom crafted now on the spot for this occasion, does not. Opera users should be able to click-and-install it, just like on the page linked above.)

Enough ranting and exploration for now. Back at home in Firefox land with my 'old fart user interface behaving as I expect it to. Which admittedly is often "rather badly", but at least I'm accustomed to how. :-)

2005-09-19

Book Burro -- find cheap books

Seen the Book Burro link in my side navigation pane? I was under the misconception that it was just a Mozilla extension, until today. Fortunately, though, it seems to have started as a greasemonkey script. Which means a field day for me. (It's very bothersome to hack Mozilla extensions, being all zipped up gobs of RDF, chrome and who-remembers-what other cruft, besides their javascript core parts.)

Anyway, after fixing a few forward compatibility bugs today (the 0.6 series GreaseMonkey needs some verbose talk to add onclick handlers), I kept improving a version of my own, adding a few Swedish book stores to it, and for kicks, a peek in the local university library inventory for available (and unavailable) copies. Try it out below to get a general feel for the general looks of it, in case you haven't already installed it (all the details on how to install are on the proper GreaseMonkey book burro homepage, in case you need them). It is not quite as instantaneous when running live, but not very far from it, either.

Book Burro screenshot

Did I miss any stores while at it, by the way? I'm not a frequent customer when it comes to buying litterature; I try to make do with my local library whenever I can. Past few years, I think the only book I've bought is Outrageous Japanese, not counting the occasional course subject material.

...this thing probably ought to become a plugin system, much like the search bar. :-)

2005-09-16

Behaviour and the Javascript Prototype framework

All right, lesson learned. Always write something about interesting things you end up reading and exploring in your technical blog, or you'll end up spending needless time scratching your head trying to get back to it before long. Sometime recently I noticed the Giant-Ass Image Viewer, a somewhat more open source version of the AJAX visualization technique employed by Google Maps to break up the world into tiny bits that your browser can handle. Interesting, but not what I spent my evening reading up on.

Instead, I enjoyed the featured tech peek and zapped away to Behaviour by Ben Nolan. But don't go there to read up on it yet; Ron Lancaster wrote a great article explaining in greater clarity than Ben himself how Behaviour separates javascript from content similar to how CSS separates style from content in HTML. Add your event handlers in a neat and tidy javascript literal, mapping CSS class selectors to a set of functions applied to all matching nodes in the page, and be done with it. A keep-it-beautiful approach to integrating javascript behaviour with web pages, rather than cramming in lots of tags armed with on-something event handler attributes all over the place.

Ron also later moved on to improve further upon Behaviour, integrating it with a more capable CSS parser. I find it a bit disturbing that the DOM doesn't yet (to the best of my knowledge, anyway) let us use the core browser methods that map a CSS class selector to a matching node set, but it's at least some consolation that we can use XPath already to solve a similar set of problems.

This kind of tool would be useful to pull in when doing GreaseMonkey hacks.

The real perl I encountered during this session, however, and today wanted to get back to, also for purposes of not reinventing the wheel in every other GreaseMonkey script, was Prototype, a javascript framework developed by the Ruby on Rails team. Like any self respecting thing calling itself a framework, at least in the world of javascript whose concept of classes is a crufty mess syntactically, it provides its own little OO toolkit, but also a lot of other neat convenience goodness, like the $() alias to document.getElementById() (or a slightly improved version which can take multiple id names and return an array featuring all the corresponding nodes). Or the String methods escapeHTML and unescapeHTML, and useful DOM dribbling tools I too often reinvent on the spot, even if they are just a tiny for loop, regexp application or similar. (Interestingly, it does not have an escapeRegExp method, though, but that's done in a flash, though.)

It seems a toolkit made to have you write code that does things, readably, rather than write lots of Java namespew for your day to day needs, or make a Perl syntax mess to do the things which should have had DOM methods a browser is bound to do often, but didn't. I lack some of the tools I use a lot (notably http_get(url, callback, callback_args) and its http_post(url, vars, callback, callback_args) counterpart, passing the result page, the request object and any additional callback_args on to the callback) -- but then these are also things that can be remedied fairly easily. Minimized typing to action ratio. And it's good to build upon the works of others.

I think I'll play with Rico some time in this blog too, perhaps to clean up the spacious navigation I'm still not very happy with. All in due time, though.

2005-09-11

GeoURL bookmarklet

Upon stumbling upon GeoURL today, and before further stumbling on the GeoURL firefox extension, I made a quick scriptlet hack to see a GeoURL marked page in Google Maps, meaning to develop it a bit further into a GreaseMonkey script, if I eventually get another inspiration going with the Google Maps API. It's at least somewhat neat, and I love the expressivity of a tiny chunk of javascript code:
var meta = document.getElementsByTagName('meta'), data = {}, i, ll, at;
for( i=0; i<meta.length; i++ )
if( meta[i].name && meta[i].content )
data[meta[i].name.toLowerCase()] = meta[i].content;
if( (ll = data['geo.position'] || data.icbm) &&
(ll = ll.split( / *[:;,] */ )).length == 2 )
{
at = data['geo.placename']||data['geo.region']||data['dc.title'];
ll = ll.join(',') + (at ? encodeURIComponent(' ('+at+')') : '');
location = 'http://maps.google.com/maps?q='+ ll +'&t=h&hl=en';
}

2005-09-09

Fixing transparent PNG breakage for IE 5.5+

Today I decided it was time my school blog looked decent in Internet Explorer, despite that browsers being broken when it comes to rendering transparent PNGs (at least PNGs with eight bits of transparency, which, after all, is what you want). At work I had to come up with a solution to that problem sometime late last year, and made a horrible gob of code that addressed the problem in a dynamic HTML application, right at image generation time.

This time, I settled for serving proper HTML and kludging up a hot fix when the HTML parts of the page has loaded, assuming any *.png image served needs to be addressed when your Win32 IE 5.5+ visitors look at the page. I'm going to keep this in very basic tutorial form, since google can get you proper discussions about the details of the kludge elsewhere.

Near the end of the page, or at a point after which you know for sure that there will be no more transparent PNG images linked in the page, add this portion of code:

<script type="text/javascript"><!--
if( (navigator.platform == 'Win32') &&
/MSIE (5\.5|[6-9])/.test( navigator.userAgent ) )
{
for( var I, i = 0; i<document.images.length; i++ )
if( /\.png$/.test( (I = document.images[i]).src ) )
{
I.style.filter = 'progid:DXImageTransform.Microsoft' +
'.AlphaImageLoader(src="'+I.src+'",sizingMethod="scale")';
I.src = 'http://www.lysator.liu.se/internal-roxen-unit.gif';
}
}//--></script>
Congratulations! Now IE, too, will render your page the way it should look. You might want to save the linked image somewhere locally, and change the link (it's a one by one pixel transparent GIF weighing in at 42 bytes, so you can afford the storage space), but otherwise you will be just fine.
Categories:

2005-09-03

Hiding the mouse cursor in Internet Explorer

Yesterday I had a deadline race for sweeping up a web application that would run some movie trailers, navigate through a set of menus and browse a mock movie repository. I had written most of the code during Summer already, but as it happened, there was a bit of change regarding the look and feel of the menu system; a 2D move-in-all-directions kind of layout being switched for a 1D navigate-up-and-down-this list. It was for the better, interface usability wise, and as it turned out, my code was already made smart enough for the only needed change to read "cols = cols || 1" where before there was "cols = cols || 3". (It was truly a beautiful moment, realizing that.)

Anyway, it's a set top box interface navigated by remote control, and though it's a web application technically, we don't want to bother the user with that particular bit of needless technical cruft, and most of all we don't want to prompt her with an ugly mouse pointer she has very little use for. So, we turn to CSS, browsing what you can do with the cursor declaration. Okay, not much, as turning it off all together goes. Sane for a web environment -- inconvenient that it does not solve our problem. So at the time, we settled for making the cursor smaller (the crosshair, to be quite precise). And it sucked.

Enter Internet Explorer embrace and extend non-standard additions to the CSS cursor attribute, for once very nearly in the spirit of the standard, cursor:url('my cursor');. I say very nearly, for while the code certainly looks the way CSS should, it's not quite that good. We can't point it to our SVG, PNG or even GIF image and have it appear as the mouse pointer, though we can use "common" Windows mouse pointer files, *.cur or even the horrible animated ones, *.ani. Just don't ask me what program can edit, or much less create, them, where the format is defined and standardized.

As it turns out, though, editing them in Emacs isn't much of a hassle. Find a cursor file somewhere (I picked the text editing cursor), M-x hexl-find-file it, and lo! You will see the pixels quite clearly, each hex nibble (four bits each) being a colour. 0 for the transparent parts, F for the black, I believe. Replace all those 0F, F0 and FFs with 00s and save your brand new, blank, empty, fully transparent cursor.

And presto! It works. Internet Explorer kindly hides the cursor when hovering the element we equipped with the cursor declaration. (Try hovering this paragraph.)

And that was that. I just felt like sharing, so maybe the next guy who has the same sad problem to solve will have better google karma than I did back in July when the issue reared its ugly head initially.
Categories:

2005-09-01

GreaseMonkey techniques

Like all better hacks, GreaseMonkey is evolving -- and with it, so could your GreaseMonkey scripting habits. With early versions, it was a good habit encasing all of your code in a big nameless function scope -- (function(){ /*your code*/ })(); -- to not unintentionally spill any bits into the invasively compositioned page. Since 0.3.3, this is done quite transparently by GreaseMonkey anyway, so you don't need to clutter up your code with it.

Similarly, many early scripts seem to package up everything in one init() method which does the real action, and invoke the script by adding an event handler for the load event -- window.addEventListener( 'load', init, false ); -- which doesn't quite seem to cut it anymore; I believe it's from recent (0.5.1 final, in my case) versions don't inject your code until the load event has already fired, and then you can't very well sit indefinitely waiting for it to happen again. At least I hope this is the story; UTSL, to find out for real. It would make a lot of sense - why burden all developers with another indirection before getting their script code run?

One indirection that does, however, still seem to need to be there in your code is a chunk of code that ensures you haven't already been invoked before on this page, and avoids doing it again. My boilerplate (well, not really, as you'll see) now starts with this chunk of code:

if( 1 == (window.ID=typeof window.ID=='undefined'?0:1) )
return;

where ID is a unique identifier I come up with for every script. Quite against the GreaseMonkey spirit (pages that don't want to get injected and know of the identifier I picked may opt out of my script by defining that value themselves), but it does work. I expect we won't have to keep doing these things forever.

And having adapted and extended a few scripts made by other benevolent scriptwrights recently, I thought I'd drop a kind hint or two to the less bearded post-millennial ecmanauts out there who were not plagued and formed in the pre-DOM days: there are lots of nifty shortcuts in the pre-DOM document object model to rid you of the boring typing of document.getElementsByTagName('body')[0], and so on. Here are a few of them:


document.getElementsByTagName('a'):

document.links

document.getElementsByTagName('img'):

document.images

document.getElementsByTagName('embed'):

document.embeds

document.getElementsByTagName('body')[0]:

document.body



Also, in places like that last one, where you are tempted to index an array (or collection / nodeset, really, as the case may be) of nodes with a constant like this, there is a safer option, to duck indexing out of bounds errors -- use .unit(0) instead of [0], and you will get a null value when the item was not there. Then break free if there was no such element, and your script will spew less embarrassing junk in the console when it failed to execute.

(Of course you may want to GM_log( 'Some friendly debug aid' ) to yourself so you quickly find what assumption didn't hold, for the day when the page you inject into changes into something slightly different -- just don't be lazy.)

That concludes today's hints and tips.