Friday, October 5, 2007

Two (appropriately) small Opera Mini tips

Opera Mini has some funny rules relating to image display. I was doing some work for a partner company and was staring at this line of code wondering why Opera Mini wasn't displaying it:

<img src="http://www.blogger.com/partner.gif" alt="partner" height="25" width="60" /> (doctype is HTML 4.01 since you ask, works fine so don't knock it)

I noticed that the partner's graphics people had saved the GIF with an animation setting ( a surprisingly comment defect) so I recreated it without. No joy. Didn't work as a PNG. Then I had a brainwave: I deleted the alt attribute and lo and behold Opera Mini displayed the image. Not completely unreasonably, the Opera Mini proxy displays the ALT attribute of an image if present, but if it isn't it assumes that there might be some information required by the user in the picture and displays it. While not unreasonable, this is still a poor decision as it means I now have to generate usability-deficient HTML in order to achieve a desirable visual effect.

The other tip is in the same vein: creating horizontal rules in Opera Mini requires a trip back in time to 1997. OM ignores border directives in CSS, but respects background colour, so you can't mark the bottom of a div like this: div {border-bottom: 1px solid black; background-colour: ghostwhite} - OM will colour the div, but not the bottom. In order to get this effect you have to stick a 1 pixel tall image (again beware, don't call the image something like "spacer.gif" as the algorithm finds those and removes them from the rendering) in a div or its own. OM will shrink the image to a centred 50% or so of the screen but you can now colour the background to match and OM will dutifully render the div to the full width of the screen. Again, not recommended as scalable code for other browsers, but should you ahve the luxury of creating an OM-specific rendering, this does help break up the page visually.

Obviously neither of these tips are relevant for Opera Mini 4 which is an entirely different issue. Equally now I've explained how to fool Opera Mini, Opera's proxy engineers might fix the proxy to ignore 1px divs.

Tuesday, August 21, 2007

Silence is broken: a pair of browser bugs

I know this blog has nearly gone abandoned: I'm currently working on a big Mobile Ajax project and consequently most of what I do belongs to someone else (it's that sort of contract). I may have to restrain myself to quiet musings on mobile interactivity rather than actual source-code, although you'll all be able to rip that when the site goes live...

Anyway, as a peace offering here are two recently noted browser bugs:

1. The N73 (N73-1/33.0638.0.0.1 - I'm keeping my firmware stock) has a nice little bug involving checkboxes. If you select a checkbox and then move the cursor out the checkbox deselects. It's not just a visual effect, the form element is not set to "on" when the form is submitted. Double-clicking (so the control looks checked and then unchecked) has the opposite effect: moving the cursor out switches the control to checked, and the form element's state to "on". We've had to switch to using radio buttons in a single-item group to get around this problem.

2. Netfront 3.3 refuses to display or load iframes unless they have a width set. Width is only optional according to the DTD, but not according to Netfront...

Thursday, June 28, 2007

Having made a tit of myself

...over onChange (read the standards before moaning Smith), I'm wary of dumping my latest S60 browser woe in public, buuuut I'm fairly sure I'm on firmer ground here. It looks like there's no way to query the styles of an element beyond the positional shortcuts: getComputedStyle() throws some sort of exception (I can't get window.onerror to respond either, so I'm in the dark as to what it's suffering from) and element.style is null - which is kind of consistent with the rest of the world. getPropertyValue doesn't work either, but that's not a huge surprise as it doesn't work anywhere else either.

This isn't quite as critical a problem as the lack of any way to query what's in a text box while the user types (and it is still a problem: the text box doesn't want to respond to key events either) but it's still annoying. All I want to do is fake an opacity fade-out (think scriptaculous) by querying the style for colour, then making it closer to the target colour bit by bit. No dice.

In other news I'm loving the meta-ness of Opera Mini 4 beta. The new version now supports XMLHttpRequest. Which means that Ajax calls are served as plain HTTP requests, causing a complete reload of the page. I can totally understand why the Mini team should have put this in, but it's a curious state of affairs to be sure.

Wednesday, June 27, 2007

More thoughts on onChange

I was wrong below. Painful that, but true. Sorry Nokia! Nokia's implementation of the onchange event is entirely correct according to the W3C's recommendations. The best summary is from this working group note on DOM Level 3, but it's essentially unchanged since the HTML 4 spec:

change
A control loses the input focus and its value has been modified since gaining focus.
Now what's odd about that is that virtually no browser actually implements the change event that way: select drop-downs wouldn't be able to react when you release them (because technically they still have focus - or does that count as losing focus even though the element is still selected?) and autocomplete wouldn't work so nicely. Having said that it looks like both Google and script.aculo.us do a certain amount of trickery to ensure that they get the content of the text box. Google's autocomplete script appears to throw focus on and off of the element with keypresses (appears to, the Google obfuscated Javascript is very hard to follow) while scriptaculous adds a keypress event listener. I'm going to go off and try that but I'm not hopeful as the Symbian input method editor (IME - new jargon learned today!) seems to block all key events and only recognises onclick.

Tuesday, June 26, 2007

Browser fragmentation: onchange fails in S60 Browser

We've found an interesting problem with the S60 browser. We're trying to put together an auto-complete form for a page, using Ajax. Possibly this is oldest Ajax pattern of all, as it's the one that appeared with Google Suggest: the user types in a few characters and the browser goes off and fetches some proposed completions.

On the early firmware N95 we have it appears that the S60 browser is incapable of detecting that the user has entered text. None of the form or key events work, with the exception of onfocus and onblur. I put a Javascript test page together that scans the text field repeatedly while the form element is focussed, and it appears that the browser is unaware there's anything in the text field until the user blurs it. One of my colleagues suggested that's because the operating system doesn't release the text to the browser until all the potential T9 completions and multi-key presses have been processed. This makes sense, but doesn't help much.

Interestingly Opera works fine, but it looks like Opera wrote their own text-input-handling routines: possibly to get away from this issue?

As I say, I only currently have a beta N95 to hand. I'd be interested if this issue persists in the production versions and whether anyone has found a workaround.

UPDATE: does the same in the S60 SDK emulator. Peculiar.

UPDATE AGAIN: The S60 Webkit source reveals the following:

From: FControlInputSkin.cpp
// CFormInputSkin::WaitForLastCharCommit - CIdle->callback function that waits for the last character to be commited
// if the text is changed then the text is set to the inputskin and and event is generated into KWQLineEdit to handle
// the textchanged event. The textwidget is deleted after that

So it looks like it's an unholy collaboration between the browser and the OS. Unless I'm reading the wrong file...

Thursday, June 21, 2007

Updates are coming

I've been slightly distracted by trying to form a company, so bear with me. In the meantime let's spare a thought for usability, notably Greg Papadopoulos' thoughts on usability. This one had me scratching my head. Why are Sun trying to position themselves as a usability company? I last saw anything with a Sun GUI on it in 1996. I'm sure someone somewhere still uses CDE but they're about as relevant as OS/2 users.

Papadopoulos's point appears to be revving up the old network computer/smart device thing that Sun have repeatedly tried to claim as their territory. For those of us with experience of the mobile industry this has been a legacy of seven years of ever-worsening fragmentation between Java implementations and multiple JSRs. Put bluntly: Sun has done an awful job of maintaining usability across Java, so why should we believe their tired claims about network computers?

If I was Sun I'd be very worried: the smart client work is moving to web browsers and Sun doesn't have a web browser, or any part of the LAMP (or LAMR) stack, come to think of it. Java will probably find a happy niche as another legacy enterprise programming environment (like C++ was before Java took over), but it's very hard to see how they'll make money out of it.

Wednesday, June 13, 2007

Minim takes wing

I've properly objectified and trimmed the minimal Ajax example I started work on earlier. It's now 336 bytes. My target is to keep the whole thing to under 2k, so I have 1712 bytes to put some other functions in.

It now looks like this:


function TinyAjax(callback) {
this._req = null;
this._sc = function() {
if (_req.readyState == 4 && _req.status == 200) {
callback(_req.responseText);
}
};

this.ajaxRequest = function(url) {
_req = new XMLHttpRequest();
_req.onreadystatechange = this._sc;
_req.open("GET", url, true);
_req.send();
} ;
}


UPDATE: the above has a scoping error in it and _req ends up in the global scope. Ooops (or rather, not OOPS). I am fixing it this evening along with a periodical updater object, which is also bigged down witha weird scoping issue.

Usage is simple: you pass the TinyAjax constructor a function that will handle the response then call the desired URL with TinyAjax.ajaxRequest() method. The class will then do all the work for you.

I haven't yet got hold of a Windows Mobile device to test on, so this currently only works with standards favouring browsers like Opera and S60. When I get one I'll add whichever object Windows thinks it wants.

It's now in a file called minim.js and can be got from here: http://slackr.eu/m/src/minim.js.txt

Still catching up: links

You'll be needing the Frost mobile ajax library. Unfortunately not yet available. Hmm, maybe I'll beat them to it. Their site design is nicer than mine though.

You'll also be wanting to read the Mobile Ajax FAQ.

And if you read German (which I can rather slowly) there's a useful site here: mobileajax.de

The Google Gears/iPhone speculation reaches fever pitch! It's off the chain!

Ian is still musing over how you'd get Google Gears on the iPhone. There is a project for Safari in the gears subversion repository and instructions on how to get it working in WebKit as a plug-in. Sadly the latest version fails to build and I don't do objective C so I'm a wee bit stuck. Looks like it might be time to pull out a textbook.

There's a much better summary of anything I can put together on the subject over here.

Tuesday, June 12, 2007

GooglePhone actually a software platform?

From the Register

This would make more sense, although I have also heard rumours that it's real and it's made of plastic and friends of friends have nearly touched it. The core bit of this wave of speculation is what Google Gears does in mobile. Opera have "endorsed it". Symbian is believed to be putting more or less the same functionality in the next OS release: cobble together an HTTP proxy and a database together with some scripting and you have a Gears clone. Mozilla like it but Minimo is still too big to play nicely in phones. I'd be very surprised if the iPhone doesn't have some cache magic in it.

It would be more sensible to keep it as a reference platform: Google stand to make more money out of extending the reach of their core business (that'll be small ads) than they do getting involved in hardware. Particularly phone hardware, which I have this horrible feeling Apple are about to discover is a horrendous business.

Working with intermittent connections is a big constraint on mobile applications, and it makes sense to cache information where possible. This isn't the best approach for information rich applications (working a day trader screen in Google Gears would be foolish) but standalone apps like Writely^H^H^H^H^H Google Docs don't actually need a network connection: they just need a storage of some variety.

I've just started this blog

I've just started this blog, but it's the culmination of about four years of thinking (!) about Javascript, web and mobile starting with XML Data Islands in Windows PocketPC. Consequently stuff's going to come out in a rather disordered fashion, so bear with me.

It'll also be a while before I make it look nice.

A bit about me might help. I started building websites in 1995 and started building stuff for mobile in 2000, when I worked on a project for a long-gone company called NerveWireless (hello NerveWireless people). I've therefore been making things for smaller screens longer than I have for big screens, although I have kept both domains going.

I'm unusually excited about the possibilities of running proper web apps on mobile devices (Ajax makes more sense when reloading a page hurts as badly as it does on mobile). But coupled with that is the worry that, as happened with WAP, we'll try and shovel too much into the device and cripple ourselves before we've had a chance to prove the point. I've seen some very sexy demos for mobile Ajax but they aren't really usable. Therefore I guess the point of view I'm trying to push is pragmatic Ajax on mobile. But that's way too long for a blog title.

If you want to contact me, the email for the blog is mobileajax (on the domain) slackr.eu. Note the cunning disguise the @ has adopted there.

Safari and iPhone

Yesterday's Steve Jobs announcement was the sort of thing British politicians excel at: re-announcing things in a new way, so it looks like there's something new to report.

We knew that the iPhone runs Safari. Therefore it isn't a huge surprise that Steve suggests that we develop apps for the iPhone using Ajax and HTML. What is surprising is that people should fall for the "iPhone is open to developers" schtick. Well, yeah! What will be interesting is whether we get Apple style widgets to play with or not.

As a side note, David Card rightly blogs that the release of Safari for Windows is designed to increase the developer pool for the iPhone. (Thanks to the rather more alert Ian Betteridge for drawing this to my attention).

And snapback sucks. There. I said something bad about Apple.

UPDATE: it looks like Mr Daring Fireball wasn't fooled...

Monday, June 11, 2007

Trimming it down to the bare essentials

Tatlin tower - about as practical as one of my ideasOne of the difficulties with getting acceptance for mobile Ajax is that Ajax is synonymous with hefty frameworks in the desktop world. Only last week the entire concept was dismissed out of hand by a colleague with the words "I tried it, but you had to load a 70K library to do it."

There's nothing wrong with frameworks so long as you have a fat pipe and loads of memory to deal with it. I've lost count of the times prototype.js (which is indeed 70K) has saved my life.

But on a mobile device bandwidth and memory are typically scarce, so you'll be wanting to cut back on all the helpful features of things like prototype and stick to the core of Ajax.

The core of the Ajax technique is use of the XMLHttpRequest object. This lets you (as the name suggests) make HTTP requests out of the browser under Javascript control. It's almost as misnamed as AJAX itself: you're not constrained to XML. This opaque naming extends to the method names, as we'll see.

The way this works is as follows:

1. You create a new XMLHttpRequest object (new)

2. Tell it to open a URL somewhere (open)

3. Tell it to go off and fetch the url (send)

4. Wait for it to come back with something


The fourth stage is the trickiest as XMLHttpRequest expects you to find out what it's doing via an event handler called onreadystatechange. This reports four possible states and the one you'll be interested in state 4, which is the state signalled when the request has returned some data.

It's probably easier to explain with some code:

(These examples are from majax.js on my site: http://slackr.eu/m/ You might want to fire this up in a browser to see if it works: I've tested it in Opera 8.65 and S60 Browser, but your mileage may vary)

1. var req;

req is a global for now: as I work towards my own microframework for Ajax I'll encapsulate this properly, but it'll do.
2. var callback;

this is a global for the name of the function that will actually do some stuff on the page. I could just code this into the script below, but I'm trying to be a bit formal.

3. function _statechange() {

4. if (req.readyState == 4) {


5. processResponse(req.responseText);



6. }


7. }


Lines 3 to 7 are the function that reacts when onreadystate changes. I should have included some HTTP error checking, but this is an example...

8. function setCallback(myfunc) {

9. callback = myfunc;


10. }

This is the code I call in the page to register the handler that actually writes to the page

11. function processResponse(t) {
12. callback(t);

13. }

And the above passes control to the page

14. function ajaxRequest(method, url, async) {

15. req = new XMLHttpRequest();


16. req.onreadystatechange = _statechange;


17. req.open(method, url, async);


18. req.send();


19. }


And the above is the meat of the whole thing. It gets three arguments in line 14 - a method which is either "GET" or "POST", a URL to get the data from and a boolean that determines whether the request is asynchronous or not - e.g., whether it blocks any further interaction in the browser while it process. Fragmentation alert: I haven't yet been able to get the S60 browser to respond to synchronous events. That's probably okay, as you're unlikely ever to want to do it.

Line 15 gets us a new XMLHttpRequest and stores it in our global req variable. 16 registers the eventhandler with our function at line 3. Lines 17 and 18 prepare the request and send it.

When the request executes the onreadystatechange event fires once for open, once for sending and then fires when the server begins responding. Only when the server has indicated that it has finished does readystate 4 fire and we can process our response.

The response has a number of properties which I'll look it later but for now the only one we're interested in is req.responseText. Unsurprisingly this contains the response as a string - can't emphasise that enough. You can also extract the response as XML if you fancy some hardcore DOM hacking.

In the actual page, I set the callback that writes to the page to simply put responseText into a div as its innerHTML.

And that's it. The actual Ajax Javascript is 406 bytes.

The example code can been seen at

http://slackr.eu/m/src/majax.js.txt
http://slackr.eu/m/src/fx.js.txt
http://slackr.eu/m/src/ajax.html.txt

Sunday, June 10, 2007

First, the mobile web











Before we get in to mobile Ajax, it's probably worth reviewing the best practices for mobile web dev anyway. Mobile web is surprisingly close to web development in terms of technology; but the entire mobile experience is constrained by factors you just don't to worry about (as much) on the wired web.



Constraint one: teensy screens

My Nokia N70 has a maximum screen resolution of 176x208. The E65 I'm currently developing against is QVGA: 240x320 (yes, the smaller, horizontal, number comes first).

The fact that aspect ratio is portrait rather than landscape makes a lot of difference to the design of the page: you can't have a left hand nav, because there's no real estate on the left hand to put it in. When a vernacular three column web page is rendered on mobile the usual result is several screensful of top-nav, ads, left hand nav before you get to the content. The situation might be greatly improved if web developers put the content first in the document and decorated it with left nav etc using careful chosen floats. But they don't.

One approach that all mobile browser vendors try is to give you a virtual viewport that the page is rendered to. It's then up to the user to scroll around the screen and zoom in and out to get an overview. This is, to be frank, teh sux. You wouldn't want to watch a widescreen movie through a periscope; but most people are quite happy to watch the movie on a different aspect ratio when it's on TV. Movies were 16:9 and above and TVs 4:3 for years with no major conflict between creators and viewers; movie directors tended to stick stuff into the central safe area, and viewers learned that occasionally they'd just have to accept they could only see Clint Eastwood's nose. If web developers learned safe areas in the way that film and TV people did we would have less need for wholesale mobile repurposing.

Best practice one then is: design for the screen in hand and don't rely on the browsers rendering to stick all the information on screen.

Constraint two: fiddly nav

The Opera client on my N70 does a good job of dealing with links: by default the cursor jumps to only items on the page that are actionable. The S60 browser wants to be a computer and forces the user to use the four way joystick to mimic a mouse. Which means that some of the time you find yourself trying to get focus on a link that is about this size.

Best practice two then is BIG TARGETS. Actually that's a best practice for all interactive applications, web or mobile, but it's especially relevant for challenging environments like mobile. Most modern browsers react to onclick events on divs so why not make an entire div area active, with the a href included as a fallback for the minority who've actively turned javascript off?

Constraint three: awful networking stacks

The more I deal with the joy of TCP/IP over narrowband wireless the more I come to the conclusion that actually the wapforum guys were on to something. TCP/IP doesn't like dealing with laggy networks: it falls back into a conservative mode where it sends lots of little packets and acts suspicious without lots of confirming ACKs.

The upshot of all this is that loading a web page over wireless can take an awful lot longer than you'd think. The HTTP overhead can add 10 seconds or more to a simple request. The slightly counterintuitive result of this is that it matters less how big your images are, and more how many there are. A 400 byte piece of page furniture will load as quickly as a 3k equivalent, because the payload of the request actually executes quite quickly. It's the set-up and tear-down phases of the request that add time. 3G is particularly prone to this problem, which explains why "blistering 3G speeds" aren't.

Unfortunately, most mobile operators are run by engineers whose prime directive is to minimise network throughput, so mobile proxies tend to crush images into low quality JPEGs. This does sweet eff all for loading times, but gets them a gold medal for having "done something to enable the mobile internet".

So, best practice three is: no more than three or four images on a page. And make 'em big. (Tip: if you make an image bigger than it needs to be (say 320x480) and then scale it to the width of the screen you can ameliorate the effects of mobile proxies because the compression artefacts will be less visible. Not very nice for the user though.)

Constraint four: fragmentation everywhere

In mobile, no-one can agree on anything. Largely because they don't have to.

You're tied to the network your operator runs, the proxies they choose, and the choice of browsers is usually one. So there's no real competition to make anything better.

Typical example: Opera only renders CSS for a page if the CSS is presented to it with a media="handheld" attribute and value. The S60 Browser, on the other hand (because it thinks it's a computer), ignores any CSS in a "handheld" block. The same CSS works more or less identically on both browsers, but you have to waste your time doubling up. Putting both in - media="handheld, screen" works okay, but actually you probably want to distinguish between things with real screens (desktops) and the S60 Browsers, so you now need to do some server-side work to get the right look on each device. Thanks Nokia.

I'll be trying to tabulate the fragmentations I know about in future posts. Hope the above helps.