Archive for the ‘coding’ Category

Webkit doesn’t fire the load event on images

Saturday, April 30th, 2011

Well that’s not strictly true. The full headline reads something like this:

Webkit doesn’t fire the load event on images when you change the src attribute and the new src is the same as the old

That seems reasonable

That seems like reasonable behaviour. I mean, the image is already loaded. Changing the src attribute to it’s current value isn’t really changing it at all. It’s staying the same. If the src is the same and the image is already loaded, why fire the load event? You would only want to do that if the image was reloaded but doing that would be pointless as it’s already loaded. Loading it again would be a waste of bandwidth and make the experience feel slower; not what browser manufacturers are aiming for.

So what’s the big deal?

Inherently lazy

Developers like myself are inherently lazy. I don’t mean we’re workshy, but rather we always look for the easiest, cleanest solution to problems. This behaviour in WebKit fails twice on this count.

  1. It’s inconsistent with other browsers. I have to work around it, potentially adding browser-specific code. That’s not good.
  2. It forces me to add extra code to cope with it’s specific requirements. Let me explain:

If I was writing for a JS-guaranteed environment this wouldn’t be such a problem but I’m a conscientious sort of guy and realise that not everyone will have the benefits of a modern browser with all the options set to ‘awesome’. I want to cater for the JS-disadvantaged as well.

Let’s assume I’m writing a carousel for a photo slideshow that shows 4 pictures at a time. I want to show the first 4 pictures by default so that at least some content appears even for the non-JS users. Then, using non-intrusive JS I augment the slideshow to add next / previous buttons and the ability to click the image to enlarge it in a lightbox.

To avoid repeating a lot of code in a setup function that would also be present in the next/previous function I can write a single function to set the page of the carousel, setting up the images and their click events.

var picturesPerPage = 4,
    pictures = $('#pictures img');

var loadGalleryCarouselPage = function(pagenumber){
    var imageStart = pagenumber*picturesPerPage;
    pictures.each(function(i){
        var picture = $(pictures[i]),
            pictureContainer = picture.parent();
        picture.hide();
        if(carouseldata.images[imageStart+i]){
            picture.show();
            picture.bind('load',function(){
                pictureContainer.removeClass('loading');
                picture.unbind('load');
            });
            pictureContainer.addClass('loading');
            picture.attr('src',carouseldata.images[imageStart+i].thumbnailurl);

            picture.unbind('click');
            picture.bind('click',function(e){
                e.preventDefault();
                pictureLink.fancybox({
                    "href": carouseldata.images[imageStart+i].imageurl
                });
            });
        }
    });
};

loadGalleryCarouselPage(0);

I’m using JQuery and Fancybox for this example.

So what we have there is a function that loops over the four img tags, pulls information out of an array (carouseldata) based on the page offset passed as an argument, sets up click and load listeners and changes the image’s src attribute. This will work for any page at any time. In theory we could add a ‘jump to page’ option where the user could choose the page number to skip to. But we won’t.

This is especially handy as we can simply call loadGalleryCarouselPage(0); to set up the event listeners when the page first loads without having to duplicate most of the lines elsewhere. We even get a natty little loading spinner if we take advantage of the loading class that is set.

Making things difficult

When the page loads it’s a bit of a race. The results of this function varies between refreshes for me. If the image has not yet loaded when the JS runs then it works fine. If the image has already loaded, however, here’s what happens:

  1. A load event listener is set
  2. The loading class is applied which shows a spinner and hides the image
  3. The src of the img is set
  4. The load event DOES NOT FIRE in WebKit because the image is already loaded
  5. The picture remains hidden and the spinner keeps spinning even though the image is loaded

And that is frustrating.

It’s an intermittent problem though, only when loading race conditions fail. Here’s another situation where it happens every time.

The dead cert.

The total number of images in the carousel doesn’t divide perfectly by four, so on the final page there are only two images showing. The final two of the four img elements are hidden from view. They are hidden rather than removed because:

  1. They act as spacers so that other elements flow around them correctly
  2. The img tag needs to stay so that we can easily change the src attribute if the user navigates back to a page with 4 images on it.

So say we’re on page 9 of 10 and click ‘Next’. Images 1 & 2 are updated to show the final two pictures and images 3 & 4 are hidden. Importantly: the src attributes of images 3 & 4 don’t change. When we click ‘Previous’, images 1 & 2 are changed back but 3 & 4 are stuck with the loading spinner. That’s because, like before, the src was already set and it was equal to the new value.

Working around it

We could set the hidden images to a transparent .gif or .png instead of hiding them which would solve the second problem but because we want the images showing for non-JS users when the page loads we can’t use that technique to fix this. Also, downloading that extra image means extra bandwidth and latency times that we’d rather not have to deal with.

It turns out that setting the src to '' (empty string) immediately before setting the image url will fix the problem. But! It causes the images (and consequently their container) to collapse to zero width and height in Firefox while the new images are loading which looks really bad if you’re trying to navigate a slideshow.

Here’s my solution:

var picturesPerPage = 4,
    pictures = $('#pictures img');

var loadGalleryCarouselPage = function(pagenumber){
    var imageStart = pagenumber*picturesPerPage;
    pictures.each(function(i){
        var picture = $(pictures[i]),
            pictureContainer = picture.parent();
        picture.hide();
        if(carouseldata.images[imageStart+i]){
            picture.show();
            picture.bind('load',function(){
                pictureContainer.removeClass('loading');
                picture.unbind('load');
            });
            pictureContainer.addClass('loading');
            picture.attr('src',carouseldata.images[imageStart+i].thumbnailurl);

            picture.unbind('click');
            picture.bind('click',function(e){
                e.preventDefault();
                pictureLink.fancybox({
                    "href": carouseldata.images[imageStart+i].imageurl
                });
            });
        }
        else{
            picture.attr('src','');
        }
    });
};

if($.browser.webkit){
    $('#pictures img').each(function(i){
        $(this).attr('src','');
    });
}
loadGalleryCarouselPage(0);

I added an else so that if there aren’t enough pictures to fill all the img tags the src of the unused images is set to an empty string. There will always be at least one image on each page so there will always be an image at full height to prop up the carousel container while those hidden img tags are primed to receive more content.

I also added a little if block directly before initialising the carousel, at the bottom. If the browser is webkit-powered then it’ll loop over the img tags and prime them (set their src to empty) before initialisation. Because this is done using JS, non-JS users will still see the images.

Grumpy

I’m grumpy about having to put in that extra, browser specific code. Setting the src to an empty string seems hacky. But it works and the logic is still clean and minimal. So it’ll do.

I hope that helps anyone having image loading javascript issues. And as usual I’d be interested to hear if you have any alternative / better solutions!

Check out the carousel in action here.

The Web in 3D – the Nintendo 3DS web browser

Monday, February 14th, 2011

The Web in 3D – the Nintendo 3DS web browser

Last Sunday my wife and I went and had a sneaky preview of the new games console from Nintendo: the 3DS.

The Nintendo 3DS

Let’s not beat around the bush: this is a very impressive device. It’s tricked out with all the latest technologies (or the latest applications of ‘old’ technologies, wherever you choose to draw the line). The thing people are really talking about, of course, is the 3D aspect of it. I’m sure you have read about it – the top screen is a 3D display which importantly doesn’t require glasses. I can’t stress enough how good the 3D effect looked. It felt completely natural and I didn’t find myself getting any kind of a headache or nausea like some people are worried about.

There were demos available of most of the functionality: Lots of games that’ll be available on launch or shortly thereafter, 3D photography, augmented reality (including the ‘reality’ part shown in 3D due to the 3D cameras on the lid – the most impressive thing for me) and street pass (Nintendo’s social discovery system). But the thing that actually holds the most interest for me wasn’t shown and indeed is barely talked about. I’m hoping that will change.

A complex web

I’m talking, of course, about the web browser which will come built in to the device as part of the extensive suit of software bundled on-board.

*YAWN*

A web browser? What’s so great about that?

I don’t know yet because no-one is talking about it, but I’m hoping it will inspire (even more) innovation and creativity on the web. I’m hoping it will have some semblance of 3D integration and capability. And if not, why not? Surely this is the way the web is going. More and more devices will be 3D enabled in the near future and you can bet that if the 3DS doesn’t kick-start the 3D web some other device will. You can buy a 3D TV to put in your living room for crying out loud – this is 2011! They reckon you’ll be able to buy a HOLOGRAPHIC 3D TV in 2012. I’m all over that. And I want the web to make sure it isn’t left behind. After all, a lot of modern TVs have integrated browsers. It’s the next logical step.

Least they could do

The least I could hope for is support for 3D images displayed in web pages. The LEAST. The standard open format is .mpo and fortunately the same format in which the 3DS saves it’s 3D photos.

That’s not to say you will be able to simply embed the 3D photos in your site and have them work in the 3DS’ web browser though. Think how that would look in a desktop browser… Well it probably wouldn’t show up or show a broken image.

No, no, no, don’t even think about making a separate site for 3D devices. I thought we were past all that. What are you going to have yet another separate site for 3D+Mobile? We want to serve one page that works on all devices.

The trouble is, without images having a similar failover pattern to the one available to video and audio in HTML5, you simply couldn’t use the image inline in your page as non-3D-enabled browsers wouldn’t recognise the format. This just proves that there are always new image formats emerging; they are not all supported by all browsers as it’s easy to assume (if you forget about IE6 and .png’s) so why should we assume that that’s the case with the markup?

This has been discussed by Bruce Lawson and makes sense (no matter how frustrating it is). So until all browsers support the display of 3D images on 2D screens we will have to find another way.

The other way to include images in the page is, of course, CSS background images. This one has legs. The 3DS browser could easily respond to an @media query, something like @media screen and (-3ds-min-device-spatial-dimensions: 3) { ... }. Then you could alter how the page looks on a device that has 3D capabilities. Once you have the 3D background image in place you can mark it up to include a 2D version for the rest of the world:

HTML:

Pretty forest scene

CSS:

@media screen and (-3ds-min-device-spatial-dimensions: 3) {
    .forest-picture{
        background: transparent url(../img/forest-3d.mpo) no-repeat 0 0;
        width: 400px;
        height: 250px;
    }
    .forest-picture img{
        display: none;
    }
}

The best of both worlds! We can dream…

Reality check

Before we go on, I just need to make it abundantly clear (if it isn’t already) that this article is pure speculation. I don’t know if the 3DS browser supports any of this kind of stuff, but imagining the possibilities and how they might work is an interesting exercise. Oh wait, it looks like Google has already looked into 3D browsing. My mistake ;)

Let’s explore further down the rabbit hole…

Going the extra dimension

What if we wanted to move beyond just sticking 3D images in our pages? As awesome as a 3D gallery might be, there are so many more possibilities. Imagine if the whole page could be rendered in 3D; if each element on the page had it’s own depth setting. I think the most obvious thing to do would be to push the background actually into the background giving the site content more prominence, and if you start down that road you should just be able to let your imagination carry you forwards.

I know what you’re thinking, and it’s what I thought at first too… why not use z-index for that? The reason why not is because z-index controls the stacking order of elements on a single plane. If you change the function of z-index to control depth on 3D devices, how would you re-order a group of elements sharing the same depth on a 3D page? It’s clear that we need a separate property to do that. I’m going to be bold and use depth in examples, for want of a better attribute name.

So where are we? We’ve got 3D images and the ability to assign depth to elements. That’s a good start, but it seems a little restricted, doesn’t it? A bunch of flat panels sitting at different depths in a 3D space. We’re not really making the most of the technology. We need to add a little style in there… style that can bridge the gap between depth-levels. Fortunately, Webkit is one step ahead of this game with it’s CSS 3D transforms. These could easily be adapted to show in real 3D instead of 3D rendered in 2D.

Curves would be nice

Yes they would, and so would a mansion on the beach in Barbados. We don’t even have the ability to define curves in 2D CSS yet. But then in 2D we might not have wanted to do crazy things like making a callout or title bow inwards or outwards, which would work pretty well in 3D. But maybe just one step at a time…

What is 3D anyway

To develop in 3D you need to understand how it really works. Fortunately understanding it is a lot simpler than getting your head around designing and developing in it:

3D works by each of your eyes seeing a slightly different image.

Simple enough, and in real life this works pretty well. But when generating your own 3D content you have to be ever-mindful of it.

Mind the gaps

Imagine a blank page. You make the background a fetching pinkish sort of red colour and set the depth to be way back in the distance.

3DS Screen showing plain background

You then have a look at it and wonder why it doesn’t look like it’s way off in the distance. You check to see that your 3D depth slider is turned all the way up and when you find that it is you’re left feeling a little confused.

The reason why this doesn’t appear to be in the background is because your eyes are seeing the exact same image. There needs to be some more detail in there before your eyes can be tricked into thinking that it’s way off in the distance. Here are some suggestions:

1. You could give it a border that makes it look like you’re looking into a box. Of course the edges of the border would need to be firmly in the foreground for it to work.

3DS screen showing a background shaded to look like you are looking into a box

2. You could give it a pattern or image. Beware with repeating patterns though: looking at 3D images forces your eyes to cross slightly and a repeating pattern could cause you to think it’s not at the depth you intended.

3DS screen with a patterned background

3. Lay something else on top of it with a higher depth. For demonstration purposes I’m going to go with this one.

3DS screen with a plain background and a green tile overlayed

But even laying something on top like this isn’t too easy for our brains to process. Have a look what each eye would be seeing.

Side-by-side 3DS screens showing the difference in location of the overlaid panel for each eye

There’s not a great deal to differentiate these two images and while your brain knows it’s seeing different things from each eye it is struggling because there are things missing that it’s used to. Usually when you see an object in front of another object it casts a shadow somewhere. Because they are in different locations your eyes will each see that shadow slightly differently. Also the way the object is lit and how it reflects the light could be different in each eye. To make sure we don’t give people headaches we’ll have to sort this out.

.floating-box{
    box-shadow: 5px 5px 5px #ccc;
}

Now the panel has a nice drop shadow which should make it easier on the eyes and easier to see the 3D effect.

3DS screen with plain background and a green panel overlaid with a drop shadow

But how does it get rendered so that each eye sees the shadow differently?

Seeing the light

The way I see it there are two options:

1. The browser provides a default (override-able) light source:

body{
    light-source: 25% 25% fixed;
}

fixed would position the light source relative to the browser viewport, and as an alternative absolute would position it relative to the document.

2. You, the developer, get granular control over what each eye sees:

If you had control over each eye the possibilities would be endless. Set the difference in box shadow offset, show a different background image to achieve a rippling effect. You would OWN all the dimensions.

@media screen and (-3ds-min-device-spatial-dimensions: 3) and (-3ds-perspective: left-eye) {
    .floating-box{
        box-shadow: 3px 5px 5px #ccc;
    }
}
@media screen and (-3ds-min-device-spatial-dimensions: 3) and (-3ds-perspective: right-eye) {
    .floating-box{
        box-shadow: 7px 5px 5px #ccc;
    }
}

I think a combination of both would probably be in the interests of developer and user alike.

It’s not all giant blue humanoids and bio-luminescent flowers

This technology has it’s disadvantages, and you can be sure that there will be some nasty surprises out there when it comes along. As with most visual effects, subtlety is king. Of course there will always be the developers who are irresponsible with this great power and make some eye-bleeding creations, but that’s just inevitable. No, what I’m really worried about can be summed up in two words: Internet. Advertising. If you thought pop-over ads were intrusive now, you ain’t seen nothing yet.

The waiting game

Who knows what you’ll be able to do with the browser? Nintendo maybe? Or if it’s Opera providing the software again, as they did for the Wii and original DS/DSi then I expect they will know. (Please do get in touch if you have insider knowledge!) But until that information is made available or the 3DS is in our hands we won’t know for sure. I hope it’s got at least a few fun 3D features to play with. I’m sure the full set will develop over time.

Update: Now that the browser is available, I had a little play with it and wrote down a few of my thoughts.

Always use www in your URLs

Sunday, January 16th, 2011

…unless you are specifically on another subdomain.

I’m not kidding! You need to make sure that your sites all redirect from http://yoursite.org to http://www.yoursite.org. If you’re not sure whether your sites do this, try now. Go ahead, I’ll wait.

You can score yourself against this list based on what you see:

  • Error page: NO POINTS!
  • Redirected to www.yoursite.org but to the front page instead of the page you wanted: Half a point
  • Page served, URL still has no www: 1 point
  • Redirected to www.yoursite.org and to the correct page: 10 points!

On no account should a user see an error page if they don’t type www before your URL. That’s just a horrible experience and most people will think the site is just broken and go somewhere else. No points at all.

It’s almost as bad if the user enters an address to a page within your site and then gets taken to the home page, even if the URL in the address bar says www. I mean at least the site doesn’t look broken but it’s still not a nice welcome. Having the www gets you half a point.

If the page is served irrespective of whether there is a www in the URL thats… OK. But only OK. Aside from the fact that you’ve got the same pages in two locations (not good for Google-juice) it could lead to errors. As yoursite.org and www.yoursite.org are seen as two different domains, if you have any AJAX on your site that refers specifically to www.yoursite.org it’s going to fail hard if the URL in the address bar doesn’t have the www. This is thanks to the cross-domain security model browsers employ. You don’t want that happening.

What you should be doing is simply redirecting all traffic to the root domain to the www subdomain and maintaining the rest of the URL to ensure a good experience. This is actually very easy to do:

Open the .htaccess file in the root of your site’s file structure. If it doesn’t exist, create it! You can create it on the command line, or directly on the server but Windows and OS X will object to the filename if you try and create it using the GUI. Once it’s open, add these lines to it:

RewriteCond %{HTTP_HOST} ^yoursite\.org$ [NC]
RewriteRule ^(.*)$ http://www.yoursite.org/$1 [R=301,L]

Note that on the first line when you change the domain you need to add a slash before each dot as it’s part of a regular expression.

It’s that simple!

You have no excuse now.

Regex for an email address

Wednesday, October 13th, 2010

It’s something that I’ve come up against several times and each time I google for it I turn up a different result.

How do you validate an email address?

Obviously you want to use a regular expression, but given the specification for email addresses that’s going to be one really complicated line of code.

Following a user’s complaint that they could not register with our site with their (perfectly legitimate) address because of our validation, today’s search yielded more success viagra usual. Near the bottom of the source of the Perl Email::Valid module there is a very long regular expression which I have lifted directly and placed in this page.

/^[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)$/

I won’t lie to you: I haven’t dissected it to manually confirm that it does what it should, but it has been successful so far in the tests I’ve thrown at it. It also works in Javascript which, for me, is a massive win.

Console.log for all!

Saturday, December 12th, 2009

Firebug console

If you’re like me then you probably use console.log a lot. It’s such a useful debugging tool! It’s better that alert in so many ways I won’t bother mentioning them all because- oh what the hell, this is my blog I’ll do what I want. Here is why console.log is better than alert:

  • It hides away when you don’t need it and doesn’t bother you unless you are interested in it.
  • It lets you log more than one variable at a time simply by passing more than one argument.
  • It doesn’t interrupt the flow of the script until you click OK.*
  • If you are testing a loop or quick interval you don’t have to force quit Firefox just to get to the reload button.
  • It integrates perfectly with the rest of Firebug turning a lot of what you log into clickable items able to be inspected in the script or HTML tabs.
  • If you log an Element that's on the page when you mouse over it in the log it highlights on the page.

What's my point? Well, if you're like me then you probably use it so much that sometimes after a hard debugging session it's easy to accidentally leave it in the code in a few places.

The piece of grit that stopped the clock

What harm can it do? Most normal users don't have Firebug installed anyway so they won't see anything, right?

Wrong. Or at least wrong attitude. Developers will see your console.logs on production code and no one will give you more grief about that sort of thing than a developer. But more importantly, it can affect everyone else too.

Without Firebug installed the console object doesn't exist. This means when you try to access console it comes up as undefined. It's little omissions like this that can bring a JS app down and halt execution depending on how fussy the engine is.

IE's 'error on page' icon

IE users will see the horrible little yellow exclamation mark in the bottom left of their browser and be informed that there is a problem with one of the scripts on the page. This doesn't inspire confidence in a site or product.

Undesirable

It's fair to say that you don't want any of this to happen. Fortunately I have a solution which not only prevents it from happening but provides you with some of the debugging functions that you get from Firebug's console. Have a look at the code:

if(!('console' in window) || !('log' in window.console)){
	window.console = function(){
		var r = {},
		glow,
		$,
		logpanel,
		enabled=false;

		r.enable = function(){
			enabled=true;
		};

		r.disable = function(){
			enabled=false;
			if(logpanel){
				logpanel.destroy();
			}
		};

		r.clear = function(){
			logpanel.empty();
		};

		r.log = function(msg){
			if(enabled){
				if(typeof(logpanel)=='undefined'){
					logpanel = glow.dom.create('

'); $('body').append(logpanel); $('body').css('padding-bottom','100px'); if(glow.env.ie && glow.env.ie<=6){ //little hack to keep it at the bottom of the IE window $('#console-log').css('position','absolute'); setInterval(function(){ logpanel.css('height','99px'); logpanel.css('height','100px'); },250); } } logpanel.append([msg,""].join('')); logpanel[0].scrollTop = logpanel[0].scrollHeight; } }; r.init = function(g){ glow = g; $ = glow.dom.get; }; return r; }(); console.init(glow); }

I'm using the Glow library to do this, go check it out it's really very good!

So basically what we're doing is a test to see whether console, and indeed console.log exist or not. If they do we don't need to bother. Let's presume that it doesn't exist.

We then define console but the way we do it may not be familiar to some. Let's strip it back to make it easier to look at:

window.console = function(){
	return {};
}();

I'm setting window.console instead of just console to clearly define the scope. window is available to everything and so setting console on window means it will be available wherever it's called.

It looks initially like I'm defining console to be a function but straight after the closing brace of the function you've got the open and close parentheses which runs the function immediately. This has the effect of setting window.console to whatever the function returns, which is in this case an object.

If, as in the case of the finished code, the object returned (r) has properties then they will be accessible at window.console.property. And of course, the property can also be a function, like log.

Fully functional

The functions that are defined here are:

  • enable
  • disable
  • clear
  • log
  • init

The console is disabled by default. This is to stop the console popping up for your poor IE users when they chance across that rogue log call. You have to want the console on to get it. This doesn't mean it's completely ineffective when disabled, though. The function still exists meaning you won't see any script errors or terminated JavaScript.

To enable it, simple call console.enable();. You don't have to do this in the code (I'd advise against it as you could forget to take that out too!). Unless you are debugging specifically for IE and specifically for something that happens automatically on page load I'd recommend enabling it manually by typing javascript:console.enable(); into the address bar and press enter.

Likewise to disable the console, just type javascript:console.disable();, or to clear it type javascript:console.clear();.

If you find yourself typing into the address bar a lot, you could drag one or more of these bookmarklets onto the bookmarks bar to make it easier:

Enable console Disable console Clear console

The reason I've included an init function is because Glow supports a sandboxing feature it's useful to be able to pass a specific version of the library to console to use. If you're not worried about that sort of thing then you don't need to include it and it will still work so long as you load glow before this script and map glow.dom.get to $.

Finally the log function is where the magic happens. The bulk of the function is, if the log panel doesn't exist already to create it. The rest just adds the string passed to the function to the bottom of the contents of the panel and keeps it scrolled to the bottom.

There's really not a lot to it!

Got console?

Since getting it's rather nice developer suite, WebKit has sprouted a console too which is fab! It's accessible on Chrome and Safari under the developer menus. Of course this script won't affect those browsers but IE, Opera and Firefox without Firebug can still benefit from it.

* Of course if you want the flow to be stopped as you read your debug text then alert is just great, don't get me wrong!

On forms, submit buttons and browsers

Tuesday, October 20th, 2009

Aah, ambiguity! What a tricky devil you are. The W3C Recommendations are normally very specific and not at all ambiguous, but when things are left open to interpretation you can be fairly sure of varying results.

Bad form, old chap

Have a look at this form:

What gets submitted when you hit Proceed? Well yes, firstname is included as is proceed, but is back? What gets submitted when you hit Back, as the second submit button in the form?

Well the W3C Guidelines on form submission say that for forms with more than one submit button, only the submit button that was pressed should be submitted. This seems fair enough.

But what gets submitted when you press enter from within the text field?

The W3C has no recommendation for this! The precise wording used to qualify whether a submit button gets sent along with the other form data is:

If a form contains more than one submit button, only the activated submit button is successful.

…where ‘activated’ means having been clicked on to submit the form and being ‘successful’ refers to the selection process for including form elements in the request.

Pressing the right buttons (or not, as the case may be)

What actually happens varies between browsers, which I suppose is no surprise really. The surprise is which browsers do what.

We see two behaviors here:

  • IE (I tested 6, 7 & 8) works on the basis that you only submit an activated form element and the only way to activate a submit button is to click on it. Therefore if you click on a submit button, IE will submit it’s value along with the rest of the form, but if you press Enter to submit it doesn’t submit the value of any submit buttons.
  • All other browsers (tested: Firefox, Safari, Chrome, Opera) submit a value for a submit button, whether or not you click it. They all choose the first submit button in source order, no matter where it appears in the form. Thank goodness that’s consistent!

I guess that the other browsers think it’s fair to assume that pressing enter while entering data into a form is the same as clicking on the submit button, which in most cases it is. But what happens when you’ve got two or more submit buttons? How do you know which button the user wanted to click? How can the developer predict that, supposing they even know about this peculiarity? Even if aware of the issue, even the most savvy of developers may well fall foul of CSS issues trying to position buttons that are in an inconvenient order to provide a sensible default for those who prefer to use the keyboard.

Yes, this time I think IE got it right!

How to fix the problem

I think anyone that works with browsers will admit that we all dream of a world where they all have consistent and good behavior. But failing that, consistent behavior would be nice.

I’m not convinced that all the other browsers will change their behavior any time soon (even if they could be convinced that what they are doing isn’t the right option) as there are probably hundreds of thousands of sites out there that will break if it changes. So the best thing to do is to ‘fix’ IE to behave the same.

Here’s the code:

You’ll notice I changed a few things around! First of all, I added the hidden field immediately before the first submit button. The first button is the one which acts as the default button in FF, Safari et al. and so we are making it do the same in IE. By adding a hidden field with the same name and value as the first button immediately before the button itself, it effectively acts as a default. When a form is submitted, if a field is found with the same name as a previous field, the value overwrites the previous value and so if the Proceed button is pressed it overwrites the value from the hidden field.

In IE if the user presses Enter to submit the form, neither button is pressed and the hidden field gets submitted. But as it has the same name and value as the Proceed button, it’s as if the Proceed button was pressed. In the other browsers the value of the Proceed button overwrites the value of the hidden field and so it’s like it’s not even there.

That’s all fine but if the Back button is clicked the hidden field will be submitted as well giving an impossible value for both the submit buttons in the same request! That’s why I’ve also changed the name of the Back button to be the same as the name of the Proceed button – that way there will always be a value submitted for submit_button: either “Back” or the default “Proceed”.

Notice I didn’t call any of the fields “submit“. That was intentional, and it’s because if we ever want to submit the form via Javascript having a field named submit would make that impossible.

But is it really a problem?

That solution is a bit clunky to be honest. Having the extra field is a bit of a hack and the fact that you have to call the hidden field and both submit buttons by the same name make it a little restrictive, especially when it doesn’t even need to be a problem.

Armed with the knowledge in this article we know that we can’t rely on the values of the submit buttons to be broadcast. If we don’t know which button has been clicked, we simply choose a default action and perform that unless we detect the alternative action. In PHP and for the first form it would go something like this:

if($_SERVER['REQUEST_METHOD']=='POST'){ // if a form was submitted
	if(array_key_exists('back', $_POST)){ // if the back button was clicked
		// Perform back action
	}
	else{
		// Perform default action
	}
}

So long as you don’t assume the button to be clicked in order to perform the default action, life will be good!

REST For The Weekend

Tuesday, August 25th, 2009

Oh well, the weekend’s over again. What did you do? I got an impressive sunburn, bought some delicious fruit and veg at the market and looked into RESTful architectures, among other things. Although I would love to tell you about my culinary adventures, what I’m going to write about now is REST. It should be noted that in this case REST is not sitting in front of the TV and has nothing to do with the title of this blog, despite what you may think!

What REST is not

There seems to be some confusion over exactly what a RESTful architecture is. Some people think it is pretty URLs:


http://www.mysite.com/portfolio/design/someclient

as opposed to:

http://www.mysite.com/index.php?section=portfolio ยป
    &subsection=design&client=someclient

This is not RESTful.

Some people think REST is a bunch of actions triggered by visiting URLs:


http://www.mysite.com/contact/email/send

This isn’t RESTful either.

Being resourceful

REST stands for REpresentational State Transfer and it’s all about resources. That is to say, data presented in a formatted way and made available at specific, addressable points of entry. Most of the time when people talk about a REST interface they will be talking about the web and URLs, but REST is not limited to this technology. So long as each resource on a system can be identified uniquely, manipulated, each request for resources includes enough information that the client knows how to process it and has the ability to contain references to other resources then it can be called RESTful.

A RESTful system is a stateless system; each resource request in a RESTful system should be completely independent of any other. It should not rely on cookies, session variables or any other state maintaining solutions. This is because a REST request is intended to be performed on the current state of the specified resource and the resource should be able to be encapsulated in a single response.

Strict instructions

It might have slipped under the radar but a couple of paragraphs above I mentioned that a REST response should include information for the client on how to process the data. In the case of a web service (and it should be assumed from here on out I’ll be talking about web services) the MIME type should be sufficient. For example, if you are serving your response up as JSON you’d want to use the MIME type: application/json. The MIME type is contained within the header of the response.

In a similar manner, the client needs to tell the server what it wants to do with the specific resource. We can do this by specifying the correct HTTP Method as part of the request.

Method in madness

So the HTTP methods most people are used to are: GET (the default method, what you get when you hit enter after typing a URL) and POST (the method used for submitting most forms). The others we are interested in are PUT and DELETE. Using these four methods we can instruct a RESTful interface to perform the basic CRUD operations:

  • Create = POST
  • Read = GET
  • Update = PUT
  • Delete = DELETE (rather unsurprisingly)

Using these methods you can manipulate a resource in any way you like from that single addressable entry point. If you have the URL for the resource, in a RESTful architecture you know you won’t have to append strings to the URL to make it do things (Eg ?action=delete) – you just change the method used to request the resource in the request header.

Obviously if your system is externally accessible you will probably want to limit access to the ‘unsafe’ POST, PUT and DELETE methods.

Not for everyone

So as you can tell, REST isn’t something you should strive to include just for the sake of it. I can’t stress this enough: there’s no point in shoehorning REST into a site or project when it is not really needed. The main use for it it most likely to be for Web Service APIs and anything that is highly resource driven. Think of it as a series of defined views on a database or other resource and you can’t go too far wrong.

I am currently working on a web based file system for storing assets as part of a CMS. It has an API so that you can add, edit, read and delete assets from external locations, providing that you present the right credentials. This almost seems like the perfect application for REST as each entry – be it a file or a folder – needs to have the basic CRUD operations performed on it. The whole URL structure is basically the directory tree and every point in that tree is addressable and will accept all the different methods.

Testing, testing

This is all very well, but how do you go about testing these crazy HTTP methods, practically? You can’t have a page full of links to click because a link is a GET request. There’s no way of changing that.

Forms offer a little flexibility – you can specify the method you want to use with the method attribute. Unfortunately this is restricted to GET and POST. If you try anything other than that it will default to GET.

To test these request methods, you have to hand craft an HTTP request. You can do this in the server-side scripting language of your choice (Eg PHP, Ruby, Python etc) OR you can download one of the handy Firefox extensions that kindly developers have made available for nothing for this very purpose! My personal favourite is RESTClient by Chao ZHOU but there are plenty of others for you to choose from. Here are a few:

Using RESTClient

It’s pretty simple really. The basic interface looks like this:

The RESTClient interface displays fields for method, url, header and request body along with an area in which to display the response. At the top are controls to open & save your request amongst other things.

The RESTClient interface displays fields for method, url, header and request body along with an area in which to display the response. At the top are controls to open & save your request amongst other things.

  1. Choose your request method
  2. Type the resource location
  3. Add any request headers you need (this is like items from form fields)
  4. Type the request body you want to transmit (this can be left blank most of the time)
  5. Hit send

What you will get back is the response as the server sends it. You can inspect the header and the body to make sure it’s coming back as you’d expect and so your application that will consume the response won’t get any nasty surprises!

Hopefully you are interested in RESTful architectures now, and are feeling inspired to go off and write one of your own!

For more information on this topic, check out the Wikipedia article on REST

Location, Location, Location

Monday, August 17th, 2009

All this time, I’ve been happily using window.location in my code, but I never knew it’s dark secret!

When is a string not a string?

This may come as a surprise to some, but window.location is not a string. The reason this isn’t immediately obvious is that if you do this:

alert(window.location);

…you will see the address of the current page.

However, say you wanted to extract the protocol of the current page. You might try something like this:

var loc = window.location;
alert(loc.substring(0,loc.indexOf(':')));

But this will fail! Firebug will tell you that loc.indexOf is not a function… but if it’s a string it should inherit that function automatically!

In actual fact, window.location is an object of type Location. It has several properties:

  • hash – the bit of the URL including and following the # symbol
  • host – the host name and port number
  • hostname – the host name without the port
  • href – the whole URL, unmodified
  • pathname – the path, that comes after the host including the first /
  • port – the port
  • protocol – the protocol
  • search – the URL parameters, including the ?

So actually if I wanted to get the protocol, there’s no need at all for string manipulation because it’s all there separated out rather handily:

alert(window.location.protocol);

The question is: how and why does window.location produce a string when you should have to type window.location.href? The answer: toString.

toString or not toString?

toString is a ‘magic’ function which you can add to an object to make it behave a bit more gracefully. If you run this code then you will simply get [object Object] back in the alert box:

var myObj = {
    'var1': 'foo',
    'var2': 'bar'
};
alert(myObj);

If you modify it slightly to include a function called toString you will see a lot nicer results.

var myObj = {
    'var1': 'foo',
    'var2': 'bar',
    'toString': function(){
        return this.var1+' '+this.var2;
    }
};
alert(myObj);

This code alerts ‘foo bar’ as you might expect. In the case of window.location, I would imagine the toString function would look something like this:

window.location.toString = function(){
    return this.href;
}

toString functions are particularly useful if the object represents an actual thing, rather than simply a collection of data (Eg. a location, a user, a tweet, etc.).

For more information on the Location object, have a look at this Mozilla developer document on window.location.