Including HTML Files from Other HTML Files, on the File:// System


“I wont be sending an officer because your not in any danger at all. You have obviously just put a blanket on a dog while it is sitting in your car and taken a photo. “

I still have a passion for web apps that run on the file system. It’s an extremely easy development model and extremely flexible. You can send a file (or set of files) to anyone and be confident they can open the files and run your web app, regardless of their operating system and without imposing on them the requirement of setting up a server. Furthermore, they can stick it on a share drive and BAM, guerilla multi-user system. I’ve had the habit long before I developed for TiddlyWiki but my time with TiddlyWiki focused my attention on the benefits and taught me a number of Single-Page App (SPA) hacks which most web developers are still oblivious to.

And let the SPA hacks roll on …

As I start to think about resetting the slideshow framework I’ve been randomly sniping at conferencesrecently, one thing I’d like to do is the idea of a file per Master Slide, containing all of the HTML, JavaScript, and CSS. This is more or less how TiddlyWiki themes work, and a very neat modularisation tactic.

Unfortunately, HTML – bless it – can include JavaScript (<script src="something.js">) and CSS (<link href="something.css"> etc), but not HTML (which would look something like <div src="something.html"> in my dreams). So what are the options for pulling in one HTML file from another HTML file:

  • Server-side includes: We’ve long had server-side includes. I powered my homepage from this less-than-stellar technique for modularisation around 15 years ago. The problem is none too hard to derive from their name. Server, I don’t want one.
  • XMLHttpRequest: We could make a XHR call and actually this is possible from file to file. Unfortunately, Google Chrome (and maybe others?) sees each file as belonging to a separate domain, making it impossible, and other browsers may issue a warning or confirmation, making it obtrusive.
  • File APIs: Again, we could use the magic of $.twFile to read the other file. But this relies on browser-specific hacks and they have to be degraded to a separate Java applet, which requires a proper Java installation, in the case of Chrome, Safari, Opera, and others. Firefox uses Moz-specific API and IE uses ActiveX, which are good but also incur warnings and may be blocked by firewalls. Still, it’s not a bad solution. The extra Java applet is a big downside in TiddlyWiki, because you suddenly need to send around two files instead of one, but here I’m already assuming there’s a bundle of files to be sent around.
  • Outputting HTML inside JavaScript: Since we can read Javascript, we could just spit out the HTML from JavaScript. The benefit here is it works, and works for the most ancient of browsers. But it would require a lot of string manipulation, which would look minging and be unmaintainable, and I massively value elegant code (or at least, the possibility of it). Many times I have wished JavaScript supported Here Docs, but alas, it doesn’t :(. The best you get is a long sequence of lines ending in . Unacceptable. You can also achieve this kind of thing with E4X, but that’s not widely supported.
  • Hiding HTML in JavaScript or CSS: I’ve considered tricks like embedding the entire HTML inside a JavaScript or CSS comment, but the problem is the same reason we need JSONP; when you source a JS or CSS file, your app feels the effects of it, but your code doesn’t get to see the source. I’m still holding a candle for the possibility of some CSS hack, like based on computed style, which would let you trick the browser into thinking the background colour of a button is an entire HTML document or something…which would be worth doing just for the sake of being insanely ace.
  • Or. iFrames.

Thinking it through, I decided iFrames are your friend. You embed the file to be included as a (hidden) child iFrame. This can work in a couple of ways.

The parent could read the DOM directly:

javascript
< view plain text >
  1. var dom = document.querySelector("iframe").contentWindow.document;
  2.     document.querySelector("#messageCopy").innerHTML = dom.querySelector("#message").innerHTML;
(The child contains message element, the parent contains messageCopy.)

This works on Firefox, but not Chrome, because Chrome sees each file as belonging on a separate domain (as I said above, wrt XHR). So we need to make a cross-domain call. We could be AWESOME and use the under-loved Cross-Origin Resource Sharing (CORS) capability to make cross-domain XHR calls, but in this case, it doesn’t work because it involves HTTP headers, and we’re doing this with pure files.

The solution, then, is another kind of iFrame technique: Cross-domain iFrames. It’s been possible to do cross-domain iFrame communication for a while, but fortunately, modern browsers provide an explicit “HTML5″ API for cross-domain iframe communication. I tested it in Chrome, and it works. On Files. Yay.

Under this paradigm, “index.html” contains:

  1. <script>
  2.   window.onload = function() {
  3.     window.addEventListener("message", function(e) {
  4.       document.querySelector("#messageCopy").innerHTML = e.data;
  5.     }, false);
  6.    document.querySelector("iframe").contentWindow.postMessage(null, "*");
  7.   };
  8. </script>
  9. <h1>Test parent</h1>
  10. <div id="messageCopy"></div>
  11. <iframe src="included.html"></iframe>

while “included.html” contains:

  1. <script>
  2.   window.addEventListener("message", function(e) {
  3.     e.source.postMessage(document.getElementById("message").innerHTML, "*");
  4.   }, false);
  5. </script>
  6. <div id="message">This is the message</div>

Point your spiffy HTML5 browser to index.html and watch in glee as the message gets copied from included to includer. I wasn’t sure it would work, because certain other things – like Geolocation and Workers – simply don’t work in all browsers against the File:// URI, even though they probably should. (Probably because the browsers keep mappings of permissions to each domain, and these systems assume the domain is served with HTTP(s).)

This technique will also degrade to older browsers using those “pre-HTML5 hacks. (As the Romans used to say, Omnis enim API HTML V, aequivalet HTML V pre-furta..)

So I’m glad this technique works and intend to use it in the future, nicely abstracted with a library function or two.

OpenSocial: A Beautiful Platform for Server-less Web Development

It’s belatedly dawned on me how OpenSocial makes a great server-less Ajax platform. When you create an OpenSocial gadget, you’re building a lil Ajax app that performs much coolness that would normally require a server, but doesn’t. Effectively, you’re delegating the duties of the gadget’s host environment. All you have to do is write a single XML file and host it somewhere. In fact Google Gadget Editor gives you an editor to write it in and a place to host it.

In fact, this is so good that if I was teaching someone Ajax in 60 minutes or less, I would consider getting them to write a gadget. It’s that simple. Previously, I might have got them to write a static HTML file, with embedded Javascript and CSS, but now they can do the same thing and benefit from server-side features too, features they never had to install. This is like getting the usual benefits of the DOM and the Javascript API, but with much more. You’re getting both a set of APIs, just like when you use JQuery or Prototype, but you’re also getting a server which many of those APIs rely on. So it’s a true platform.

By the way, I’m assuming you’re using XML content type, not URL. XML content type is the way forward, especially as we move to a Caja world. XML gadgets will get you the richest functionality. URL gadgets are only suitable for legacy concerns, and in some cases, accessibility.

So you’re using XML content type, which means the entirety of your Ajax app lives in one file. What does an OpenSocial platform provide to your little XML gadget spec?

  • Persistence. The most important thing is you can save application data. You can serialise any state you care to persist, and then store it as a hidden user preference. This is really neat. It means you can write a TODO gadget, for example, and ensure all the TODO tasks will be saved, but without having to store them on your own database and without having to build a persistence layer around it. And of course, the platform will (theoretically) provide the necessary security to ensure users only see their own tasks.
  • Preferences. You can easily let users customise your app via the preference API. For free, you get form fields to let users state their preferences, with drop-downs for enumerated types. And of course, the preferences are persisted without any effort on your part.
  • Content Fetching. It gets even better. On-Demand Javascript is often bigged up as the way to get data from third parties. But thanks to OpenSocial’s remote fetching APIs, it’s just as easy to grab content via the containers Cross-Domain Proxy. This means you can get any internet content and call any API, not just those specifically designed for cross-domain browser calls. There are also value-adds here, e.g. caching, scheduling periodic updates, converting feed data of any format into a consistent JSON structure.
  • Identity and Social Networking. Ah yes, the almighty “social” in OpenSocial. On the right platform, your server-less Ajax app can get info about the page owner and the page viewer, and their friends. Friends and social stuff and knowing whose page it is, that’s cool and all. But what’s really exciting here is much simpler than that – it’s simply IDENTITY. What matters most is: With a single API call, you get an ID for the guy viewing your gadget!!! You didn’t have to handle registration, password reminder links, etc. etc., and go through all the associated security questions around it. All the usual stuff you have to do if you want to know who’s using your application. It’s all *free* to you, gadget developer. (Downside is your company doesn’t “own the user”, so your gadget will never get you pina coladas on the beach at noon and an environmentally-conscious hybrid vehicle to sip frappachino grandès in.) With this user handle, you can also save a hashmap of information about the user, persisted by our friend the server.
  • Many other services. Each platform is free to provide any number of features beyond the required API; this is how platforms compete against each other in the OpenSocial world. “Feature” is actually the term used for a particular set of browser and server services, e.g. “dynamic height” – the ability for a gadget to update its height – is a standard OpenSocial feature. Notifications – the ability for gadgets to add little notes to the top which the user must manually clear – well, that’s a custom feature iGoogle provides but others don’t. So anyway, there are lots of these features and your gadgets get them for free. In most cases, they are just libraries which you could also get for free by using Scriptaculous or whatever…but still, with OpenSocial, it’s really, really, easy! No download or installation. (Although it must be said that with library CDNs like Google’s new effort, you can also use many Ajax libraries without installation too. The difference here is that at least some of these features are tied to server-side services as well.)

So, these days, OpenSocial may well be the easiest way for a newbie to learn Ajax, and for any developer to get an Ajax app up and running. I actually think we need more tools and services to allow standard Ajax apps – not gadgets – to get up and running fast too. After all, the UI in OpenSocial is based on a widget/gadget model, and this is a mini web app running on a bigger page. Nowadays, we have canvas gadgets, which are bigger, but still not so big and run inside another web page. What if you took the same cloudish platform principles and applied it to a big old Ajax app, occupying the entire page and with its own URL (though probably coming from the platform’s domain). You can sort of do that already by looking at the content of the gadget iframe, but it’s not designed for that purpose…and if it was, you could do some interesting stuff.

For instance, I envisage a simple-to-use cloud storage system based around principles of Persevere and CouchDB, and for limited use, it could even allow anonymous access. I’ll perhaps demo all this at some stage.

CSS Coding Style and the Unbearable Tendency for People to Adore Whitespace in their Source Code

CSS coding style doesn’t get a lot of play. Most people are happy to stick with the convention of one property per line, like this:

  1. #score {
  2.   background: yellow;
  3.   width: 12em;
  4.   border: 1px solid orange
  5.   padding: 2em;
  6.   margin: 3em 0;
  7.   display: none;
  8. }

I, for one, can’t stand that style. I’m heavily biased towards information-dense coding styles, which don’t have much whitespace. People mechanically argue “oh but whitespace is so important etc” as if we were designing a modern art installation. Well, whitespace is indeed essential, but you have to discriminate between a touch of elegant whitespacing for the sake of clarity, and “oh dear! My 80-column row seems to have just 12 characters on it. Come to think of it, every row on my 30-inch Cinema Display has 12 characters on it.” Seriously, I don’t know why anyone would use an 80-column terminal anymore, unless they enjoy coding on their iPhone. I tend to think 120 or 160 is ideal; any more than that and most people won’t be able to see the whole thing without moving their head. Anyway, I’ll be conservative and assume 80 columns. The average end column in the above code snippet is roughly 15, which means in your scrawny 1975 80-column terminal, you’re filling 20% of it with information and 80% with whitespace.

There’s an old figure that says the average programmer codes 10 lines per day. It’s obviously not a very accurate figure and maybe it was meant to include all the non-programmers on the project or something, but the point is interesting, as there’s probably some reasonably consistent LOC per developer figure (obligatory mutatis mutandis caveat). Seizing on this historical figure, Bruce Eckel said a while ago (paraphrasing) “if I only get 10 lines a day, I want them to be good ones”.

That’s why I favour information-dense coding styles. As a programmer, you’re supposed to be an expert user who spends all day in front of the same system. This is a typical example of a situation which demands a “Sovereign Posture”. It’s an expert system. When people long for glorious pastures of whitespace, they’re ignoring the fact that with sufficient practice, they’ll be virtually as proficient at reading and writing a more information-dense style, and vastly more efficient by having so much more code available at any time. It’s the same reason why Real Programmers commit to learning Unix and Vi or Emacs; if you’re going to spend decades working in this environment, why wouldn’t you spend a few days committing to the most efficient tools available?

For the record, I still have plenty of whitespace in my programs, but when there is a decision to be made between one popular convention and another, I almost always go for the one which reduces whitespace. I have enough faith in programmers’ pattern-detection skills to know that in time, they will be fine with either style, but the one with less whitespace will always give them more bang for their Cinema Display buck.

Back to CSS. Here’s how I’d format the above snippet:

  1. #score { background: yellow;  border: 1px solid orange;
  2.              width: 12em;  padding: 2em;  margin: 3em 0;
  3.              display: none;
  4. }

What I do is group similar attributes on each line. I’m pragmatic about the precise rules. If it’s just a handful of properties, I’ll stick them all on the same line. If there’s more than that, then typically I’ll have one line for appearance (colour, fonts, etc.), another for layout, and possibly more for other things like display settings or detailed positioning. You also don’t need whitespace between each rule either, so you can often end up with a whole sequence of related one-liner rules:

  1. /******************************************************************************
  2.    SCOREBOARD
  3.  *****************************************************************************/
  4. #scoreboard { background: black; color: white; }
  5. #homeTeam, #awayTeam { color: pink; text-align: right; position: absolute; }
  6. #homeTeam { top:20px; left: 120px; font-size: 8em; }
  7. #awayTeam { top:80px; right: 120px; font-size: 5em; }
  8. ...
  9.  
  10. /******************************************************************************
  11.    ARENA
  12.  *****************************************************************************/
  13. .player { color: #f99; }
  14. .runner { color: #99a; }
  15. ...

This format is not only denser but also easier to comprehend as related properties are grouped together. The typical one-line-per-property format tends to include a random jumble of properties; rarely does any thought go to the order in which the properties appear.

Another thing that helps with CSS brevity is learning shorthand formats. Use “border: 1px solid orange” instead of “border-width: 1px; border-style: solid; border-color: orange”. Use “margin: 3em 0;” instead of “margin-left: 3em; margin-right: 3em;”, and so on.

Operator Overloading Considered Insanely Useful

I am amazed that for all the talk about future Java features, there is hardly any mention of operator overloading. Certainly syntactic sugar is high on the priority list, with the Java world starting to realise the benefits of powerful literals and closures. (Good thing too. Steve Yegge: “Java’s biggest failing, I’ve decided, is its lack of syntax for literal data objects. It’s an umbrella failing that accounts for most of the issues I have with the language.”). Yet operator overloading is another feature that no real-world language should be without, and hardly anyone gives a second thought to the fact that time has moved on since the concept was rejected in 1995.

In Java:

earnings2007.greaterThan(earnings2008)

In Ruby/Python/C++/C#/anyOtherDynamicOrNonDynamicLanguage:

earnings2007 > earnings2008

Of course, many a Java destractor has noted the irony that is String.”+” overloading, which is to say that Java’s creators broke the rule and did something you can’t do, by making “a”+”b” a legal operation, even though String in theory is just a run-of-the-mill Java class. This is a subtle way of saying maybe there are cases where operator overloading makes sense; at least, the people who prohibited it thought so. I think it’s called “do as I say…”.

The arguments against operator overloading are pretty much the same as those against dynamic classing and make a trade-off in favour of robustness over productivity which is rarely warranted. The reality is that production errors are hardly ever caused by typing confusion, as long as you make the operator definitions meaningful. I can trust you to do that because you’re a professional, right? Even if your testing regime consists of nothing more than dipping your web server into your bowl of Corn Flakes every weekend, I challenge you to come up with a production error caused by confusing String.+() with Integer.+(). If you can grok earnings2007.greaterThan(earnings2008), I’m sure you’ll do just fine with earnings2007 > earning2008. The only problem with operator overloading comes when people do stoopid things with operators, and you can just as equally do stoopid things with method names (or booleans if you want to get really sad :)), so why victimise the operator?

People will say “can’t have operator overloading – it leads to weird code that doesn’t make sense”, which would be like banning electric guitars because someone might use it to make weird music.

I would take overloading further than what we do today. e.g. how about multiple operands:

assert(x < y < z)

You could do that with today’s terminology by having < return some kind of state object instead of just a boolean. So what I’m talking here is as much about patterns as new language features.

How about more visual representations:

Ω  v Δ u dx  =  Ω   i vxi uxi dx  +  ∂Ω  v
du
—-
dn
 dS

Yes, that’s right, a formula that looks like a formula. Surely this is the natural extrapolation from current trends – (a) domain-specific languages, where the code looks like the requirement; (b) smart IDEs. I discussed this a while back with reference to the (now-defunct?) Jackpot project. Operator overloading is one piece of that puzzle.

“Ajax Design Patterns” – Book of the Month

Ajax Design Patterns is Book of the Month in this month’s .Net mag (p.23, Issue 155, October, 2006). Incidentally, the mag is about the ‘Net, not specifically MS .Net (which it pre-dates).

<

p>The review says:

So AJAX might be the hottest thing in programming since, er, ordinary Javascript, but it’s no good just learning how to implement it – you need design inspiration too. Ajax Design Patterns fits the literary void that exists in AJAX design by using real examples of best practice to enhance your apps.

 

<

p>I’m glad they emphasise use of real examples, because we can debate ad infinitum about whether everything in the book is a pattern or not, but the more important thing is that the examples are real, concrete, and as accessible as typing a URL into your browser.

Thankfully, Ajax Design Patterns is one of the most organised books on any programming subject. It’s a massive book, but you won’t get lost as the chapters are sensibly divided up and the sound layout means there’s nothing whatsoever to fear.

<

p>I’ve had a lot to say about presentation of patterns in the past The fairly unusual presentation of the patterns is the reason it’s not an O’Reilly animal book, and it’s good to see it helped.

The Uncanny Valley of Programming Languages

Coding Horror mentions Applescript’s well-intentioned attempt to feel like English. Quoting John Gruber

The idea was, and I suppose still is, that AppleScript’s English-like facade frees you from worrying about computer-science-y jargon like classes and objects and properties and commands, and allows you to just say what you mean and have it just work.

But saying what you mean, in English, almost never “just works” and compiles successfully as AppleScript, and so to be productive you still have to understand all of the ways that AppleScript actually works. But this is difficult, because the language syntax is optimized for English-likeness, rather than being optimized for making it clear just what the f**k is actually going on.

This is why Python and JavaScript, two other scripting language of roughly the same vintage as AppleScript, are not only better languages than AppleScript, but are easier than AppleScript, even though neither is very English-like at all. Python and JavaScript’s syntaxes are much more abstract than AppleScript’s, but they are also more obvious. (Python, in particular, celebrates obviousness.)

There’s a lot to be said for the Uncanny Valley theory:

The Uncanny Valley is an unproven hypothesis of robotics concerning the emotional response of humans to robots and other non-human entities. It was introduced by Japanese roboticist Masahiro Mori in 1970. It states that as a robot is made more humanlike in its appearance and motion, the emotional response from a human being to the robot will become increasingly positive and empathic, until a point is reached beyond which the response quickly becomes strongly repulsive. However, as the appearance and motion continue to become less distinguishable from a human being’s, the emotional response becomes positive once more and approaches human-human empathy levels.

Just as human emotions (according to that hypothesis) respond in a U-shape curve, so does performance with human-computer interfaces. When we can come up with a near-perfect human language processor, it will probably have some great applications for humankind, allowing “non-programmers” to instruct computers in far more complex ways than they can right now with the most advanced end-user programming interfaces around today (Excel and Second Life).

Until that time, attempts to make programming languages human-friendly, however well-intentioned, fall deeper into the valley. It’s like old-school Visual C++ codegen – sounds great at first, but as soon as you get one tricky use case (inevitable), it all falls down.

A more promising approach is Domain-Specific Languages (DSLs), which talk in terms familiar to the “end-user-programmer” (we need a better name for that actor), but, by accepting critical constraints, is actually workable.

How ‘Bout Those Radiobuttons?

I’m all for Web 3.0 gadgetry in the browser, but how about some JS love for those ancient radio controls. It should be easy and portable to: * Get the current value of a radio group, without looping through each radiobutton to find the one that’s checked! * Catch onchange events (no luck with IE).

Right now, it’s not.

Those are some humble wishes – hopefully they’ll take priority over the grand plans for graphics, rich controls, and the all-powerful built-in physics engine.

Guard Clause Considered Helpful

Apparently, PragDave recently questioned the conventional wisdom about GOTO considered harmful (does this mean all “X considered harmful” articles will be retrospectively struck off the record?). Ivan Moore’s given an example as to why the rule of “a single exit point” sucks, and I agree.

Another reason for multiple exit points is guard clauses. Code units, like classes or functions, ought to look right at a high level. Just like with a piece of writing, it’s important that the overall structure gives you the big picture. Guard clauses help here. You can say, “Okay, let’s get the cruft out of the way – the trivial cases, the exception cases – and now we can focus on the main thing the function’s meant to do.”

  1. void foo() {
  2.   if (nothingToDo) { return; }
  3.   // Implement typical behaviour of foo()
  4. }

is much more readable than:

  1. void foo() {
  2.   if (!nothingToDo()) { // Hold my breath until the end
  3.     // *ugh* The typical behaviour buried in an if-clause
  4.     // And adding insult, we're now indenting more than we need to
  5. }

Steve Freeman points out, Ivan’s particular example could be refactored nicely into a single ternary operator statement. While they don’t scale to larger, more complex, functions, ternary operators are a very handy tool for the reason he explains.

Dynamic Favicons

Favicons should ideally be easy to manipulate, as easy as manipulating the web page’s UI. (Favicons are the little website icons you see in the address bar, browser tabs, etc.) For example, a chat app like Meebo could signal that your buddy’s trying to contact you, a mail app like GMail could indicate You Have Mail!

I’ve found surprisingly little info on this – is anyone doing it? Anyway, I’ve been wanting to play around with this for a while and having recently submitted the final book draft (post pending), I finally had some spare time to play with it. Fortunately, it turns out to be perfectly feasible – it seems that Firefox and Opera both use <link> tags to determine favico, and this can be changed dynamically to satisfaction. The only gotcha is that you can’t try to be too efficient – if you reuse an existing link object and overwrite its href property, the browsers won’t pay any attention. so you simply have to remove the existing link tag and add back a new one with the new icon URL. Unfortunately, IE and Safari don’t seem to use the link technique at all – I think they just use the “favicon.ico” file. If you know of a way their icons could be dynamically manipulated, please mail me or add a comment here.

So I’ve created a library, favicon.js, that lets you manipulate favicons with a single call – changeFavicon("theIconUrl.ico");. You can also set the document title, changeFavicon("theIconUrl.ico", "Hi Everybody!"), which just sets document.title. There are a couple of demos and a brief FAQ:

Ajaxify favicon demo

Implementation Details. Here’s the favicon.js code as it stands, all 32 lines of it :-).

javascript
< view plain text >
  1. var favicon = {
  2.  
  3. change: function(iconURL) {
  4.   if (arguments.length==2) {
  5.     document.title = optionalDocTitle;
  6.   }
  7.   this.addLink(iconURL, "icon");
  8.   this.addLink(iconURL, "shortcut icon");
  9. },
  10.  
  11. addLink: function(iconURL, relValue) {
  12.   var link = document.createElement("link");
  13.   link.type = "image/x-icon";
  14.   link.rel = relValue;
  15.   link.href = iconURL;
  16.   this.removeLinkIfExists(relValue);
  17.   this.docHead.appendChild(link);
  18. },
  19.  
  20. removeLinkIfExists: function(relValue) {
  21.   var links = this.docHead.getElementsByTagName("link");
  22.   for (var i=0; i&lt;links .length; i++) {
  23.     var link = links[i];
  24.     if (link.type=="image/x-icon" && link.rel==relValue) {
  25.       this.docHead.removeChild(link);
  26.       return; // Assuming only one match at most.
  27.     }
  28.   }
  29. },
  30.  
  31. docHead:document.getElementsByTagName("head")[0]
  32. }

Update (Two Days Later)

Crikey! Dugg and on Delicious Popular. And, well, Ajaxian too ;-). Digg is interesting … The last time I submitted my own story to digg, it got precisely two diggs (thanks to the other guy!). This time, I didn’t bother. Is there a moral here?

New, improved, with animate(). Most people get dynamic favicons, but some people have said this whole thing is about animation, about as useful as a blink tag, etc. Okay, that kind of misses the main point, which is about notifying the user while a tab is in the background. The FAQ makes it pretty clear that animated icons was a kind of afterthought (actually, it only occurred to me after I created the cycling demo – changing according to a timer was simply the easiest way to update a background tab, since there’s no chat functionality needed or anything like that). But, you know, when a simple idea like that causes seems so wrong to some people, there’s probably wheels in it. And while I did say animated GIFs are possibly more elegant in the FAQ, it since occurred to me that it’s a bit of a hassle for the casual developer to make an animated GIF. You also get the benefit of compatibility with Opera (and maybe others?). So I’ve added an animate() method, only 8 more lines in all, that lets you cycle through a sequence of images. I expect to post it over the weekend. Mmm…Eye Candy!

Server-Centric versus Browser-Centric

James Strachan: Is Ajax gonna kill the web frameworks?:

So is the web application of the future going to be static HTML & JavaScript, served up by Apache with Ajax interacting with a bunch of XML based web services (maybe using SOAP, maybe just REST etc)? If so, do we really need a web framework thats focussed on HTTP and HTML, or are we just gonna end up developing a bunch of XML based web services and letting Ajax do all the templating, editing and viewing?

While I’ve kept [an open mind](http://ajaxpatterns.org/Ajax_Frameworks an open mind), pure separation is certainly the approach I’ve been using and advocating. Perhaps server-centric approaches can work okay for some intranet apps where the emphasis is on getting up and running, and where many developers want to focus on server-side concepts like messaging, DB, business logic, etc. But the bulk of applications are better off as browser-centric.

With tools like Dojo, Mochikit, Scriptaculous, Ajax Pages, you can quite happily get the whole UI encapsulated in JS. Amid all the Ruby uptake (and for good reason), JS has also come along nicely over the past twelve months. Not just libraries, but techniques for OO design, testing, etc.

In addition, as others have pointed out, the approach goes hand-in-hand with SOA: if you’re going to expose a clean REST API on your server anyway, it’s a no-brainer to proceed with a pure-browser UI. Testing becomes a lot easier too. Separating model and presentation has always been a difficult problem in software … when you have a solution as good as this, you have to put up a very strong argument against it.

So why are people still using server-centric frameworks? The biggest force of resistance seems to be the misplaced notion that JS is a hack and we must avoid working with it at all costs. Or maybe it’s less ideological than that, and because people just haven’t had time to learn it. Fair enough – it’s hard enough to keep up with server-side technologies. But a little time learning the basics of JS would certainly ease the pain of web development.