Not Your Grandpa’s Framesets: Premasagar Rose shows us IFrame 2.0!

usual live blogging caveats – spelling errors, messy, etc etc

@premasagar is visiting the Osmoplex today (thanks @jayfresh for arranging it) and is taking us through his work on iframes, widgets, and sandboxing. I’ve realised we could perhaps be collaborating as my jquery-iframe plugin is so close to his. Different emphases, but much overlap.

GitHub is where you can see what he’s been working on. Basically, this guy is a guru on all things iFrame. In particular, all the quirks around squirting dynamic content into iFrames, as opposed to pointing them elesewhere using “src”.


“sqwidget is my tiddly”


In patternesque speak, the basic problem is:

Problem: You want to embed 3rd party content into another site.


  • You want the 3rd party content to have its own style
  • BUT it will inherit style from the parent page



Works great (in fact, I’m using it in my TiddlyWiki playground app, to be documented at some point, and is similarly used in Jon Lister’s TiddlyTemplating.

The problem is you sometimes want the widget to jump out of the iframe, e.g. a ligthboxed video. So …



Basically a CSS reset, but whereas CSS resets will only handle browser defaults, cleanSlate blats everything! This is exactly the kind of thing I was looking for when I was trying to cancel out tiddlywiki styling. In that case, I was flipping the entire page back and forth, so I could just cheat by removing stylesheets and re-add them. (Prem pointed out there’s a “disabled” attribute on style tags – true, so I should really use this instead, assuming it’s portable, which he thinks it is.)

Problems: - Difficult to maintain CleanSlate library, because new CSS stuff and browser quirks keep coming up - IE6 and IE7 don’t support “inherit”, so need CSS expression. - When using Javascript to interact with CSS style properties, e.g. slideDown(), these will override CleanSlate. The solution is to set the “style” attribute with !important, but it becomes an arms race! - Doesn’t solve iFrame security model


Inject (aka squirt, inject; summary) content into a fresh iframe.

The content comes from the widget site. The sqwidget library injects it. This resolves the tension with wanting independent CSS on the same page. If the sqwidget library is running on the host page, it could even (potentially) lock down capabilities, i.e. do Caja-style sanitisation.

Sqwidget also does templating, using Resig’s micro-templating. (That thing’s getting to be very popular; I’m using it myself in Scrumptious via the UnderScoreJS library after @fnd gave us a talk about them.)

Also, prem is playing around with the idea of a div, with custom (data-*) attribute pointing to the third-party URL. You could put inside it “now loading” and then the script tag will pick those things up and load them.

Various points:

  • for content-squirting into the iframe, you should be setting doctype, otherwise IE will load in.

  • worth investigating use of tag

    Web Tablets: The Tipping Point is Nigh

    It’s been said that the world hasn’t been this excited about a tablet since Moses came down the mountain. January 27, 2010, is the day Apple is slated to finally put us out of our misery and tell us what it’s all about. But imagine Steve Job moseys onto the stage, launches a forceful history of Apple’s portable record, and then announces Apple’s launching some new ipod speakers. (It happened a few years ago.) No “one more thing”. No tablet. Not even an iPhone OS 4.0.

    Even if this happened (it won’t), Apple will have added huge value by sparking a conversation about the future of computing. While some say all the speculation is a waste of time, in this case, I’ve actually found some of the discourse rather fascinating. In particular, Gizmodo’s invocation of Jef Raskin and the “information appliance” dream, and John Gruber’s analysis.

    I think Gruber nails it. Steve Jobs, in what many consider will be his final act at Apple, is attempting no less than the next generation of computing UI. Many people are already finding they can get by with just their iPhone for many tasks. Myself, I actually prefer to read blogs on iPhone NetNewsWire and Instapaper on iPhone Instapaper. I can read these away from the distraction of the big machine, whether at home, commuting, or in a shopping line. I’ve been trying to read stuff on mobile devices since the Palm Pilot, and now it’s truly practical. If people are finding their phone does some things better than the computer, imagine what will happen when you have a big touch-screen, let alone any secret-sauce innovations like tactile feedback or live docking into desktop equipment. I think we will find it’s more than adequate for many casual users and a valued extra device for power users.

    But this is about much more than Apple. I think we can take it for granted that the medium-term future will be all about touch-screen tablets. We’ll struggle our way through questions about how to stand them up and challenges like their never-satisfying battery life. And what happens when they fall on the floor? Oh, and there will be patent wars galore. But the category will grow fast, as many people start to reap the benefits of a double whammy: better interaction, more convenient form factor.

    The really interesting question is how will the UI on these tablets work? The Gizmodo and Daring Fireball articles point in the right direction – it will be more like the new “super phone” mobile generation and less like the traditional PC. Lots of sandboxing, lots of highly-customised idiosyncratic interfaces (but with common idioms), and lots of abstraction (==liberation) from the file system, lots of app stores and repositories.

    Now one model for all this is iPhoneOS, the custom-built operating system Apple put together or its own phone. Is there another model?

    Of course. The web.

    And we can do it today. Apple won’t, others will. We have the makings of an operating system that does all that. Lots of sandboxing? Yep, the whole security model of the web assumes domains don’t trust each other, unlike traditional desktop applications. Lots of customised interfaces? Yep, with Canvas and CSS3 and SVG and WebGL and audio and video and screamingly-fast browsers and a million brilliant libraries and toolkits, yes. Lots of abstraction? Yep, the web never did like file systems, and with offline storage, it doesn’t have to. App stores? Yep, a simple system of URIs and standard HTTP security techniques can do it easily.

    Most developers would rather code in technology they already know, that’s open, and has a diverse community contributing a wealth of “how-to” knowledge.

    It’s all happening now. Google has ChromeOS. Palm has WebOS. Nokia and others have the W3C web widget standard. Stick these things on tablets, and a whole new generation of UI will flourish.

    Testing OpenSocial Apps – Current Challenges

    At present, the OpenSocial containers are new and the whole process is still quite difficult from a developer’s perspective. These are unfortunate barriers to adoption which the containers could overcome with some redesign.

    The challenges at present are:

    • Manual signup and approval process required. Even to get onto the sandbox area, you have to go through a manual signup and approval process, which usually takes a day or two. A human is in the loop verifying your details. At that time, the developer may have lost interest, or the mail may never get to them.
    • Lack of test accounts. The sandbox accounts usually can’t interact with non-sandbox accounts – that’s what makes them “sandboxes” and this is a wise policy. However, what do you do, as a developer, when you want to test with a large group of friends? You could friend other developers, but testing is going to get a little tired if you have to keep asking them what happened. The problem is the manual signup process – you have to add new users with fake details for approval by the container – weird! On Orkut at least, you can also invite friends from your test account, so it’s possible. And, oh yeah, I hope you get a kick out of CAPTCHA. You’ll be filling in a *lot* of CAPTCHA forms as you build up your social network of imaginary friends! One to create the account, one to verify email, one for good luck here, one more just because why not. And that’s for each user on each container! If I was running these containers, I would make it even simpler and just create an initial block of 10 fake friends (some friends with each other…pick your favourite soap opera) and let the user seamlessly add new ones too.
    • Older versions and Missing Features. Ning and Plaxo are hosting OpenSocial 0.5, whereas 0.7 has been out for a while and 0.8 is about to come out. I can’t blame them for not upgrading all the time, but it still makes development more difficult. As for missing features, I noticed this with Hi5. It’s a good implementation, but still missing a critical feature – UserPrefs. This is just the multi-container nature of OpenSocial.
    • Slow code-test cycle. When testing with a container, you have to change the gadget on the server and the container will then reload it and render it. This reloading process will always take some time, but the container can do as much as possible to eliminate any other delays. Unfortunately, they don’t make it easy at present. I refer specifically to caching. You obviously don’t want your gadget to be cached during code-test cycles. Caching is usually enabled by default, even in the sandbox. That’s kind of dubious – you would think a sandbox should load the gadget each time. But okay, I can accept that decision as it’s useful when you’re testing the gadget 100 times not to be re-downloading it all the time. However, it’s usually not clear or documented how to suppress caching when you want to. Again, the containers should be making it dead simple if they want to encourage development…how about a caching on/off checkbox somewhere in the gadget chrome or settings menu?

    There is a lot the containers can learn from Facebook, and they will need to for OpenSocial to really take off and compete against it. For comparison, here is how Facebook deals with the issues above:

    • Manual signup and approval process required. Facebook makes this completely automated. You create a normal Facebook account and simply visit a special URL to make it a developer account.
    • Lack of test accounts. It’s easy to create test accounts in Facebook – you just keep creating normal accounts and flipping them to become developer accounts by visiting that special URL.
    • Older versions and Missing features. With Facebook, there is only one implementation, so only one definitive version of the API. There’s nothing OpenSocial can do about this directly. It’s simply a consequence of the “Write Once, Run Many” aspiration and the only way for the community to deal with it is to be better in other ways, and to at least be very explicit about what each container does and does not support.
    • Slow code-test cycle. This doesn’t arise in the same way because the model is either based on an iframe directly to your site, or FBML you enter into a form. The OpenSocial gadget model – an XML sitting on a server somewhere – is neater as there’s no form involved; everything’s encapsulated in the XML file. However, it does introduce the whole question of caching and the containers should be doing all they can to simplify the development process to that end.

    I’m a great believer in the OpenSocial vision; hence, I hope the containers will be working to minimise these obstacles. Right now, it’s okay for professionals, but there are enough hurdles there to hold back an army of potential hobbyist developers from uneashing their creativity on this platform.

    Music As She’s Developed

    I made a little music mashup you might enjoy using.

    A Little Music

    As I was playing around with the new layout of this blog, I added a Last.FM widget to the sidebar. It looks like this:

    (May not render if you’re reading this from a feed reader.)

    Great. Now I can listen to trance from the blog. Trance is good for coding. So that’s ace. But I couldn’t stop there, could I?

    Widgets, Widgets, Everywhere!

    I made this page with 30+ gadgets in all sorts of genres. This is good for those times when I’m not coding and want to listen to something else. Being in the cloud, it means I can easily listen to whatever I feel like when I’m in an internet cafe or the such like. So even more ace.

    Make Your Own Jukebox

    In the spirit of sharing, you can easily make a page containing your own favourite music. The URL for the default music page resolves to something with a ridiculously long list of genres:

    … which means you can hack the URL and make your own jukebox with your own genres and bookmark it and it will work and live on in the cloud and you too will be able to listen to your favourite music anywhere you go.

    For example, you might be a more passionate fan of contemporary Japanese music than myself, in which case you would concoct the following URL and save it to your delicious bookmark manager to enjoy many years of musical gratification:,j-pop,j-punk,j-hop,j-rap,anime

    Add A Player on the Fly

    One other feature is that you can add a new player on the fly. This again is great for travelling around as it will let me easily listen to any genre I care for without even having to edit the URL.

    (Unamusing trivia: I actually caused a bug at first by using “gray” as the colour name here. I’m used to working with American spelling for programming of course, but then is a UK company and the name you want is actually British spelling, i.e. “grey”.)

    Obligatory Wishlist I’ll Not Really Get Around to Implementing But it’s Cathartic to Braindump it Anyway

    If I was going to do more work on this, I would:

    • Make it into a widget-like portal which lets you add/remove/layout etc and save settings within a hackable URL (using Unique URL. You could argue the whole thing is useless as you could achieve the same thing in iGoogle/Shindig, but sometimes a specialised interface, tucked neatly inside a separate tab, works best.
    • Provide more flexibility on layout
    • Add support for bands, not just tags
    • Run it on a separate page without the blog layout
    • Keep the loldog

    Cross-Domain Communication with IFrames

    An update in the era of HTML5 (May 6, 2011)

    This post has been heavily commented and linked to over the years, and continues to receive a ton of traffic, so I should make it clear that much of this is no longer relevant for modern browsers. On the one hand, they have adjusted and tightened up their security policies, making some of the techniques here no longer relevant. On the other hand, they have introduced technologies that make it easier to do cross-domain communication in the first place.

    With modern browsers, you can and should be using postMessage for this purpose.

    Library support is now available too. All of the following provide an interface to postMessage where it’s available, but if it’s not, fall back to the primordial techniques described in this article:

    Now back to the original post … (from March 31, 2008)

    This article explains iframe-to-iframe communication, when the iframes come from different domains. That you can do this effectively is only now becoming apparent to the community, and is now used in production by Google, Facebook, and others, and has powerful implications for the future of Ajax, mashups, and widgets/gadgets. I’ve been investigating the technique and working some demos, introduced in the article.

    Background: Cross-Domain Communication

    Ironic that in this world of mashups and Ajax, it’s not very easy to do both of them together. Ajax applications run in the browser and such applications were never intended to talk to anything but the server from whence they came. So it’s not easy to mash content from multiple sources, when everything must be squeezed through the originating web server. A few hacks have arisen over the years to deal with this, such as On-Demand Javascript, and the most recent one is a hack involving iframes, which I’ll explain in this article. As we’ll see later, the iframe technique is arguably more secure than On-Demand Javascript, and it’s also better places for communication within the browser, i.e. from one iframe to another.

    Related to this article is a demo application and a couple of variants.

    The first mention I’ve seen of this hack originated on James Burke’s Tagneto blog in June, 2006, though I’m fairly certain it’s been used in some quarters long before that. It’s now used in production by Google in Mapplets. It’s also used in Shindig for widget-container communication. The technique also happens to be the best way to make safe cross-domain calls from the browser directly to a third-party server, which is why it is employed by Facebook’s new Javascript Client Library.

    The Demo

    First, let’s see what we can do with this hack.


    In this demo, we have a control on the top-level document affecting something in the iframe and vice-versa. This shows you can run communication in both directions with this technique. Typical of the technique, the communication is between two browser-side components from different domains (as opposed to browser-to-server communication, although there is actually server communication involved in making this happen).

    The Laws of Physics: What you can do with IFrames

    To understand the hack, we need to understand the “laws of physics” as they apply to iframes and domain policies within the browser. Once you appreciate the constraints in place, the pattern itself becomes trivial. This demo was created to explore and illustrate these constraints, and contains some simple code examples.

    Definition I: A “window” refers either to an iframe or the top-level window (i.e. the “main” page). In our model, then, we have a tree-like hierarchy of windows.

    Law I: Any window in the hierarchy can get a handle to any other window in the hierarchy. It doesn’t matter where they live within the hierarchy or which domain they come from – with the right commands, a window can always refer to any other window. Parent windows are accessed as “parent”, “parent.parent”, etc., or “top” for the top-level. Child windows are accessed as “window.frames[0]” or “window.frames[name]“. Note in this case that the name is not the iframe’s id, but rather the iframe’s name. (This reflects the legacy nature of all this stuff, relating back to ugly late-90s frames and framesets.) Thus, to get a sibling handle, you might use “parent.frames[1]“.

    Law II: Windows can only access each others’ internal state if they belong to the same domain. This rather puts a kibosh on the whole cross-domain cross-iframe thing. All this would be so easy if iframe scripts could talk to each other directly, but that would cause all manner of security shenanigans. HTML 5 does define explicit communication between iframes, but until wide adoption, we have to think harder …

    Law III: Any window in the hierarchy can set (but not read) any other window’s location/URL, even though (from Law II) browser security policies prevent different-domain iframes from accessing each other’s internal state. Note: Exact details for this law needs further investigation Again, it doesn’t matter which domain it comes from or its position in the hierarchy. It can always get a handle on another window and can always set the window’s URL, e.g. “parent.frames[1].location.href”. This establishes window URLs as the one type of information on the page which is shared across all windows, regardless of the domain they come from. It seems sensible that a parent can change its child windows’ URLs, BUT not vice-versa; how strange that a child window is allowed to alter its parent’s (or uncle’s, sibling’s, etc.) URLs! The only justification I know of is the old technique of escaping the frame trap, where a website, upon loading, ensures it’s not inside a frame by simply setting the top-level URL – if it’s different to itself – to its own URL. This would then cause the page to reload to its own URL. However, that’s a special case and hardly seems worth justifying this much leeway. So I don’t really know why you can do this, but lucky for us, you can!

    Law IV: When you change the URL’s fragment identifier (the bit on the end starting with a #, e.g., the page doesn’t reload. This will already be familiar to you if you’re familiar with another Ajax hack, Unique URLs to allow for bookmarkability and page history. Normally, changing a document’s “href” property causes it to reload, but if you only change the fragment identifier, it doesn’t. You can use this knowledge to change the URL symbolically – in a manner which allows a script to inspect it and make use of it – without causing any noticeable change to the page content.

    Exploiting the Laws of Physics for Cross-Domain Fun and Profit – The Cross-Domain Hack (URL Polling version)

    The laws above are all we need to get cross-domain communication happening. The technique is simply this, assuming Window A wants to control Window B:

    • Window A changes Window B’s fragment identifier.
    • Window B is polling the fragment identifier and notices the change, and updates itself according to the fragment identifier.

    Ta-da!!! That’s the whole thing, in its glorious entirety. Of course, you had to know the laws of physics in order to understand why all this works. It simply relies on the fact that both Window A and Window B have one common piece of state – the URL – and the fact that we can change the URL unintrusively by manipulating only the fragment identifier. For example, in the demo, the iframe’s URL changes to and once the iframe script notices it, it updates the colour.

    A few observations:

    • This works in either direction. Parent to child, child to parent. As the demo illustrates.
    • It requires co-operation from both parties; it’s not some magic way to bypass browser security mechanisms. Once Window A changes Window B’s fragment identifier, it’s up to Window B to act on the change; and it’s up to Window B to be polling the fragment identifier in the first place.
    • Polling the fragment identifier happens to be exactly the same technique used in the Unique URLs pattern.

    There are a couple of downsides: (a) Polling slows down the whole application; (b) Polling always involves some lag time (and there’s always a trade-off a and b – the faster the response, the more cycles you application uses up); (c) The URL visibly changes (assuming you want to manipulate the top-level window). We’ll now consider a second technique that addresses these (albeit in a way that introduces a different downside).

    The Cross-Domain Hack (Marathon version)

    Here’s a variant which no longer involves polling or changing any URLs. I learned of it from Juliene Le Comte’s blog, and he’s even packaged it as a library.

    Looking back at Law II: “Windows can only access each others’ internal state if they belong to the same domain”. At the time, I made this sound like a bad thing, but as David Brent likes to say, “So, you know, every cloud …”. The law is bad if you state it as “cross-domain iframes can’t play with each others’ toys” (paraphrasing the informal version of Demeter’s law). But it’s good if you spin it as “well, at least same-domain iframes can play with each others’ toys”. That’s what we’re going to exploit here.

    As for the demo, the functionality is the same, but since this one involves spawning iframes, I’ve left them intact, and made them visible, for your viewing delight. Normally, of course, they’d be invisible, and the application would look exactly the same as the previous demo.

    Here’s how this technique works:

    • Every time Window A wants to call Window B, it spawns a child iframe, “Window B2″ in the same domain as Window B. The URL includes the command being issued (as a CGI parameter, fragment identifier, or any other URL pattern which will be recognised by the destination script).
    • When window B2 starts up, its Javascript inspects the URL, gets a handle on Window B, and updates Window B according to the URL (e.g. a CGI parameter).
    • Window B2 destroys itself in a puff of self-gratified logic.

    So in this case, we create a new, short-lived, iframe for every message being passed. Because the iframe comes from the same domain as the window we’re trying to update, it’s allowed to change the window’s internal state. It’s only useful to us on startup, because after that we can no longer communicate with it (apart from by the previous fragment identifier trick, but we could do that directly on the original window).

    Window B2 is sometimes called a proxy because it accepts commands from Window A and passes them to Window B. I like to think of it as Pheidippides of fame; it passes on a message and then undergoes a noble expiration. Its whole mission in life is to deliver that one message.

    This technique comes with its own downside too. Quite obviously, the downside is that you must create a new iframe for every call, which requires a trip to the server. However, with caching in place, that could be avoided, since everything that must happen will happen inside the browser. So it would simply be the processing expense of creating and deleting an iframe element. Note that the previous variant never changed the DOM structure or invoked the server.

    Also, note that in either versions of the hack, there is the awkward matter of having to express the request in string form, since in either pattern, you are required to embed the request on the window URL. There is an inspired extension of this hack that also has some untapped promise in this area. It involves setting up a subdomain and updating its DNS to point to a third-party website. When combined with the old document.domain hack, you end up with a situation where your iframe can communicate with a cross-domain iframe, without relying on iframe. (The technique described in the article is about browser-to-server communication, but I believe this iframe-to-iframe is possible too.)

    A Third Hack Emerges: Window Size Monitoring

    A newer third hack by Piers Lawson is based around the porous nature of window sizes and the use of window.resize(). Fragment IDs are used like in the first technique here, but instead of polling, window resize events are used to cause a more direct trigger.


    Cross-Domain IFrame-to-IFrame Calls … and Widgets/Gadgets

    In the world of mashups, iframes are a straightforward way to syndicate content from one place to another. The problem, though, is limited interaction between iframes; in pure form, you end up with a few mini web browsers on a single page. It gets better when the iframes can communicate with each other. For example, you can imagine having iGoogle open, with a contacts widget and a map widget. Clicking on a contact, the map widget notices and focuses on the contact’s location. This is possible via Gadget-To-Gadget communication, a form of publish-subscribe which works on the iframe hack described here. And speaking of maps, check out Google Mapplets, which are a special form of gadget that work on Google Maps, and also rely on this technique.

    In terms of gadgets, another application is communication between a gadget and its container, and this is something I’ve been looking at wrt Shindig. For example, there is a dynamic-height feature gadgets can declare. This gives the gadget developer an API to say “I’ve updated, now please change my height”. Well, an iframe can’t change its own height; it must tell its parent to do that. And since the gadget lives in an iframe, on a different domain as the container (e.g. iGoogle), this requires a cross-domain, cross-iframe, message. And so, it uses this technique (“rpc” – remote procedural call – in shindig terminology) to pass a message to the container.

    Cross-Domain Browser-to-Server Calls

    The best known technique for calls from the browser to an third-party server is On-Demand Javascript, aka Javascript APIs aka JSON/JSONP. This was obscure in 2005, with Delicious being the best example. Now, it’s big time in Web 2.0 API land, and Yahoo! has exposed almost all of its APIs this way, and Google also provides data such as RSS content via JSON.

    It works by spawning a new script element programmatically, an element pointing to an external Javascript URL. Since there’s no restriction on third-party Javascript running, the browser will faithfully fetch and execute the script, and so the script will typically be written to “return” by updating variable and/or calling an event handler.

    There are two major security issues with On-Demand Javascript. Firstly, you have to trust the API provider (e.g. Yahoo!) a lot because you are letting them run a script on your own web page. And there’s no way to sanitise it, due to the script tag mechanism involved. If they are malicious or downright stoopid, your users may end up running an evil script which could ask for their password, send their data somewhere, or destroy their data altogether. That’s because whatever your web app can do, so can the third party’s script, even if you’re only trying to get a simple value back. The mechanism forces you to hand over the keys to the Ferrari when all you want is a new bumper sticker. Secondly, what’s to stop other websites also making use of the external Javascript? If your own site can embed the script to call a third-party JS API, so too can a malicious fourth-party. This is fine for public, read-only, data, but what if you’re relying on the user’s cookies to make privileged calls to the third-party? Then the fourth-party’s web app will be just as capable of issuing calls from the browser to the third-party, and they might well be more evil calls than you’re making, e.g. “transferFunds()” instead of “getBankBalance()”. The moral is: Javascript APIs can only be used for serving public data.

    Whoa!!! Public data only? That’s a tragic restriction on our cross-domain API! For mashups to be truly useful, it must be personal. We’ll increasingly have OAuth-style APIs where users will tell Site X that Site Y is allowed to read its data. But how can that work in the browser? How can Site Y expose its data so that it’s usable from the browser, but only when Site X is running? It can’t work with On-Demand Javascript. Site Y could try reading the referrer headers to see where the call is coming from, but anyone could write a command-line client with fake headers.

    In fact, the answer is to use the iframe hack described in this article. As I mentioned earlier, this is how Facebook gets the job done, with what is essentially the same “power of attorney” delegation model as OAuth (BTW thanks to my colleague Jeremy Ruston for the “power of attorney” OAuth analogy – albeit it was stated in a slightly different context from OAuth).

    I haven’t looked too much into the mechanism involved with the Facebook API, but it looks like it’s essentially using a variant of the Marathon technique. From memory, there’s an ever-present invisible iframe. Each time your web app make a Facebook call, the Facebook JS library spawns a new “proxy” iframe, which passes the message on to its same-domain ever-present frame, which makes a bog-standard XHR call to Facebook. So now we’re making an XHR call to another domain, which we can get away with because it’s coming from a separate iframe. Once the XHR call returns, I think the message is returned to your application (this happens via another same-domain iframe you must host on your server, though I think that’s unnecessary) and the proxy iframe disappears.

    Note that all Facebook ever exposes is a standard web service that relies on the user being logged into Facebook – there’s no Javascript involved. The user must be logged in and must have given permission for the application to access its Facebook details. Effectively, the user is allowing a particular website URL to make Facebook calls, since the application developer must register the URL. If you look back at the iframe algorithms I described earlier, you’ll see that it’s straightforward for Facebook to ensure that only this application (and any other application the user trusts) can access the data. The iframe (whose behaviour is controlled by IFrame and can’t be tampered) simply has to inspect the URL of the parent window and pass it to the server as part of the XHR call. The server can then check that the logged-in user has authorised this application, using the URL to identify it.

    As for the first concern of cross-domain Javascript – having to trust the third-party API provider – I believe the iframe technique overcomes this concern too. never gets to run arbitrary Javascript on your server. Of course, you have to trust the Facebook library and the Facebook-provided iframe you’re required to host on your domain, but those could be audited prior to installation. All those things are set up to do is call callback methods inside your top-level application; you could inspect the library and ensure that’s all that will ever happen.

    Thus, cross-domain iframe-based communication solves both problems which have plagued On-Demand Javascript. It is slightly more complicated, however.


    Yes, this is a somewhat complicated technique. Actually understanding the problem it solves is really the hard part! Once you understand that, and once you understand those laws of physics, the trick is actually quite straightforward (either version of it).

    The technique will be critical for gadget containers such as Shindig. As OAuth takes off, we’ll also see the technique used a lot more in mainstream applications and APIs.

    With HTML 5, cross-frame messaging will render the hack unnecessary for iframe-to-iframe communication. Indeed, the aforementioned Cross-Domain library uses that technique already for Opera, in a fortuitous twist of fate since Opera doesn’t actually support everything this hack requires. However, the notion of using iframes for cross-domain calls will still be present, no matter how the windows talk to each other.

    Shindig Architecture: Java Gadget Server 2 – Servlets

    More raw Shindig notes. This time, looking at org.apache.shindig.gadgets.http. See Shindigging tag. I’ll structure them just a little more this time.

    Main Servlet – data struct for country/language/locale – The servlet that accepts gadget spec URL and prefs, and outputs the gadget content (typically in an iframe). Delegates heavily to GadgetServer, in order to get a Gadget, and then serialises the Gadget itself with outputGadget(). outputGadget() will output the gadget as either URL or HTML type, depending on the content type. (I expect those output methods will probably be extracted to a seperate class, or to their own strategy classes.)

    1. gadget = servletState.getGadgetServer().processGadget(gadgetId,
    2.           getPrefsFromRequest(req), context.getLocale(),
    3.           RenderingContext.GADGET, options);
    4.       outputGadget(gadget, view, options, contentFilters, resp); extends ProcessingOptions – Allows URL params to override default options, e.g. to allow caller to suppress caching

    Javascript Servlet – Outputs Javascript content

    Proxy Servlet – Provides implementation for ProxyServlet, which is a thin wrapper around this class Handles Fetch commands for gadgets, i.e. allowing them to get remote content. Delegates everything to ProxyHandler


    RpcServlet is a “meta” servlet. Initially I thought it was just for debugging/administering the container, but it plays a more important role as it lets the browser-side gadget container issue a query to find out about the gadgets it’s hosting. (The gadgets are of course in an iframe, so due to security restrictions, it can’t directly inspect the gadget content to find out, for example, its name, which it needs to know in order to show the gadget chrome/wrapper). – Context for JsonRpc stuff. Used by RpcServlet – Meta-model of a Gadget (ie just its defining features – URL and moduleId – and not fields to populate it as in Gadget). Used by RpcServlet – Used by RpcServlet – Used by RpcServlet – Used by RpcServlet – Boring exception class – Provides Gadget meta-info – allows a programmer/tester to get info about the gadget server and list its gadgets. See[email protected]/msg00317.html.

    Used by All Servlets – Servlet scoped state (ie instances of the same servlet always get this state object) - – Defines accessors for globals such as the GadgetServer, so that each Gadget can get a handle on them. implements CrossServerState – creates globals such as the GadgetServer (and defines accessors for servlets to access them). Also includes some utility methods for the servlet (which could really go elsewhere).

    Misc implements ContentFilter – Caja filter – adaptor/bridge to Caja project, which sanitises JS, intended for inlined gadget.

    Shindig Architecture: Java Gadget Classes

    This is the first of an open series on the architecture of Shindig, the new open-source gadget/widget framework project. As mentioned here earlier, this project is building something similar to iGoogle, i.e. an environment for serving gadgets, a run-time environment for the gadgets to operate in, and a gadget container (as well as OpenSocial support).

    I’m currently digging into Shindig’s architecture and will document my progress.

    For the record, there’s not much discussion of Shindig’s architecture to date. The most useful summaries I’ve seen are a couple of notes on the mailing list:

    •[email protected]%3E
    •[email protected]/msg00369.html

    Also, be aware that Shindig has server-side implementations in both Java and PHP, and potentially more languages in the future. I’m focusing on Java at this time.

    I’ll be tagging each of these articles with “shindigging” (as well as “shindig”, a general tag for anything on this blog about shindig). Thus, you’ll be able to find a full list of articles from

    Java Gadget Server

    I’ve walked through each file in the Java gadget server, in the main package – org.apache.shindig.gadgets and taken a very raw set of notes on each file / public class, as well as sketched a quick summary of the process. I’ll refine all this later.

    Java Gadget Server – Tracing from gadget spec to page content

    A gadget server takes an XML file on a server somewhere and converts it to some HTML/JS/etc content inside an iframe. After looking at org.apache.shindig.gadgets, the Java gadget server achieves this task as follows.

    • GadgetServer is invoked from the web app to render a gadget whose spec sits at a URL
    • GadgetServer uses CacheLoadTask to load the _Gadget_ object if possible
    • If not found, GadgetServer uses SpecLoadTask, which uses RemoteContentFetcher, to grab the Spec.
    • GadgetSpecParser converts the XML string into a GadgetSpec, which is a Java representation of the XML spec.
    • Gadget constructs itself from a combination of the GadgetSpec and the preferences.
    • GadgetServer passes Gadget to each required GadgetFeature (going by the required features declared in the spec). These GadgetFeature objects perform some kind of transformation on the Gadget – typically they add one of more JS libs to it (a gadget has a list of JS libs).
    • At this point, classes in the http package kick in to render the Gadget object, of which more in a different blog post.

    Java Gadget Server – Files / Classes in org.apache.shindig.gadgets (raw notes) – [part of GadgetServerConfig] dumb implementation of GadgetBlacklist – file based – dumb implementation of GadgetDataCache – Just a hashmap – dumb implementation of GadgetSigner “Provides dummmy data to satisfy tests and API calls” – dumb (String) implementation of GadgetToken – server-side remoting proxy implements GadgetFeatureFactory – Bidirectional language support (i18n). Performs “hangman” substitutions (MSG_foo). Builds up a Substitutions and executes it. – It’s a gadget! This object is created from a GadgetSpec and ultimately serialised to a string representing the HTML/JS/etc content that sits on the page. Prior to serialisation, the object is subject to a set of transformations, one for each GadgetFeature it requires. interface – [part of GadgetServerConfig] persists blacklist and lets you query if a given URL is blacklist interface – String->String filter interface to transform the HTML/JS/etc widget content for the browser, e.g. for Caja sanitisation – This object is passed to each GadgetFeature in the processing sequence to tell it what’s going on and help modify its behaviour, since it contains info about gadget server options – ProcessingOptions – as well as Locale, RenderingContext and ServerConfig. – [part of GadgetServerConfig] Cache interface. Simply a map from string ID -> Type T. – Exception base class – Transforms a Gadget so it will implement a particular feature. prepare() on initial call and void process(Gadget) later on. TODO more – Simply an interface to create Gadgets “GadgetFeature create()” – [part of GadgetServerConfig] A map of gadget features in this gadget server. Essentially Gadget ID string -> {feature object, other features it depends on}

    • Includes processGadget(), which is called by gadget servlet. GadgetID [ie gadget URL] -> Gadget object ready for rendering
    • processGadget() adds a sequence of task objects (commands) and executes them:
      • CacheLoadTask – load gadget from cache instead of fetching/constructing it
      • SpecLoadTask – load gadget from remote URL (using low-level class, RemoteContentFetcher)
      • EnqueueFeaturesTask – popalate Gadget’s list of required gadget feature objects
    • Uses a workflow process: Works iteratively – each cycle, it works out which tasks need to be performed. Keeps iterating until all tasks completed or no new tasks can be added. Meanwhile, accumulates all gadget exceptions for all iterations so they can be bundled together in a big exception option that’s thrown if any exceptions occurred. [Note: I'm not sure why this complicated workflow algorithm is required, when afaict only 3 task objects are present. Maybe more will be added later on.] Configuration options for the gadget server. Composed of java.util.concurrent.Executor, FeatureRegistry, GadgetDataCache, MessageBundleCache, RemoteContentFetcher, GadgetBlacklist, SyndicatorConfig Nothing much right now. You’d think it parses a config file or something, but it just ~replicates GadgetServerConfig interface – defines interface for mapping token ID string -> GadgetToken – Dumb data structure encapsulating the spec (xml) ie user prefs, required features, gadget URI, HTML content data, random info-garbles (author etc.) – String xml -> GadgetSpec.

    1. GadgetSpecParser specParser = new GadgetSpecParser();
    2.       GadgetSpec spec = specParser.parse(gadgetId, xml.getResponseAsString());
    3.       wc.gadget = new Gadget(gadgetId, spec, prefs);
    4.       (ie xml file becomes spec, spec becomes gadget) – Effectively a token string, with a method to sign URLs interface – An immutable view of the gadget – Goes into a directory and recursively finds all files matching “feature.xml” Reads each file into a GadgetFeatureRegistry.Entry and registers it into registry (e.g. feature.containerJs.add(JsLibrary) (remember a GadgetFeature modifies the gadget in some way. In the case of a JsFeature (defined in JsLibraryFeatureFactory), the modification is simply to add some JS libraries) [jsLibraries is part of Gadget] – Represents a JS library – holds its source u.g. URL/file) and capable of reading it to get a string of the JS. The source may be a string representing the JS itself, which is useful if the client simply wants to construct the script text programatically. implements GadgetFeatureFactory – Provides GadgetFeatures in the case where the gadget feature is simply a JS file (or a list of container JS files and a list of gadget JS files). In this case, the feature’s process() method is simply to add all the libraries to the gadget (gadget.addJsLibrary). JsFeatureLoader uses this after trawling through to find the feature.xml for each gadget, since that file simply identifies a bunch of JS libraries. [part of GadgetServerConfig] String ID -> Message map. XML file -> MessageBundle implements GadgetFeatureFactory – Provides MessageBundleSubstituterFeature. This feature is a Javascript library that “compiles” the MessageBundle to Javascript, for a particular locale. It sets up language and country preference (String setLangFmt = "gadgets.prefs_.setLanguage(%d, "%s");"; String setCountryFmt = "gadgets.prefs_.setCountry(%d, "%s");";), and then sets up, for each message, the JS mapping from ID -> Message ( String setMsgFmt = "gadgets.prefs_.setMsg(%d, %s);" ); – Includes ModuleSubstituterFeature which simply replaces MODULE hangman string with the module ID. – Provides OpenSocialFeature – Tweaks GadgetServer.processGadget algorithm (methinks this seems like a weird pattern – should instead be attributes of GadgetServer). – Encapsulates results of HTTP call – the content as well as status code, size, etc. [part of GadgetServerConfig] – HTTP client to grab gadget spec (nb IMO too much BDUF abstraction going on here) – Encapsulates request for HTTP call – headers etc. – enum { GADGET | CONTAINER } – boring exception class [part of Gadget] – A collection of Substitutions – each Gadget has a Substitutions object, which it uses for get() queries, e.g. “public String getTitle() { return substitutions.substitute(baseSpec.getTitle()); }”. * Several substitution types MSG BIDI UP(user-prefs) MODULE * A map for each substitution type, mapping substitution key -> substitution string * Runs the sequence of substitutions on a given string [part of GadgetServerConfig] Unclear – related to OpenSocial and JSON. [part of Gadget] – Builds up JSON object with preference values, using Substitutions to perform any substitutions (???) – preference ID -> string (value of preference)

    Widget/Gadget Containers: What are they good for?

    Ajax, AjaxPatterns, Gadgets, OpenSocial, Shindig, Web, Web 2.0, Widgets


    Widgets are small “mini websites”, typically self-contained blocks of content, on a larger web page (with Ajax Design Patterns, I referred to them by the nom du jour Portlets). They are used in a couple of ways:

    • Embedded in a normal web page. For example, my blog currently contains a BBC weather widget and a “Twitter Badge” showing my latest tweets. Widgets embedded in this way are combined by the publisher, often with some manual HTML coding (script tags), and are usually a sideshow affair.
    • Combined within a widget container (aka “widget portal”, “Ajax homepage”). Websites such as iGoogle and NetVibes are primarily designed as widget aggregators, allowing an end-user to construct a personalised page for themselves. They are the Ajax/Web 2.0 successors of the “My <whatever>” hype (My Yahoo!, My Excite!, etc.) of the mid-to-late ’90s. To some extent, a social networks like Facebook fits into this category too, with the proliferation of applications available to users to embed on their homepages. And as Facebook in particular illustrates, widget containers are not always private “productivity tools”, but may also be available to a user’s friends or the general public. Indeed, the more conventional widget containers have recently started allowing users to make their public portals, which starts to move them in the territories of CMS and social networking.

    Well, this is an article is about widget containers. Specifically, I’m currently compiling a list of typical features you’d expect to see in widget containers, so if this post sounds stream-of-consciousness, well, it sort of is.

    Anatomy of a Widget (well, Gadget and OpenSocial) Container

    A good place to start is the breakdown of functionality for Shindig, the new Google-supported Apache project to build a reference implementation for the OpenSocial standard. OpenSocial is heavily intertwined with the whole idea of widgets and widget containers, since it’s basically Google Gadgets + standardised social networking APIs. Hence, Shindig is essentially the high-profile open-source project involving a widget container. Shindig has been broken into four parts:

    • Gadget Container JavaScript — core JavaScript foundation for general gadget functionality (read more about gadget functionality). This JavaScript manages security, communication, UI layout, and feature extensions, such as the OpenSocial API.
    • Gadget Server — an open source version of, which is used to render the gadget xml into JavaScript and HTML for the container to expose via the container JavaScript.
    • OpenSocial Container JavaScript — JavaScript environment that sits on top of the Gadget Container JS and provides OpenSocial specific functionality (profiles, friends, activities).
    • OpenSocial Gateway Server — an open source implementation of the server interface to container-specific information, including the OpenSocial REST APIs, with clear extension points so others can connect it to their own backends.

    That’s a very useful overview, though I’m looking more at specific features which generally cut across at least some of these parts. For example, gadget preferences. These are part of the container Javascript because there is a UI to change the preferences, they are part of the gadget server because it must initialise the gadget according to preferences, and they are part of the server because they must be persisted.

    Feature List

    First cut at a feature list (# indicates not directly available in iGoogle)

    Gadget Preferences

    • Preference defaults
    • User can set preferences
    • Publisher can set preferences when embedding widget on a page
    • Preference variable types: string, enumerable (set by dropdown, set by combobox), boolean, list, location, etc
    • Preference persistence: by database against session vs. in cookie
    • Preferences persisted for anonymous user (Lazy Registration)
    • Gadgets can access preferences via API, consistent access regardless of content type and embedding model
    • # Gadget can be notified of preference changes, so that it’s not necessary to reload the entire gadget/page after each change

    Gadget Appearance

    • Gadget appearance customisable by publisher
    • Gadget appearance customisable by user
    • # Round corners, shadows, background images (e.g. Schmedley has all of these)

    Gadget Content Type

    See Widget Content Type article * HTML – from container provider’s domain * URL – from gadget provider’s own domain * # Inlined – embedded on container. This implies a security model, e.g. Caja

    Container Appearance

    • User can change gadget skin, header, footer, background, widget preferences via constrained mechanism (ie can’t change everything; e.g. config file or UI)
    • Publisher can change look and feel via HTML/CSS
    • Gadget themes available, with gallery, for pre-defined configurations
    • Animation used for features such as preference setting and expand/collapse of widgets
    • Drag-and-drop shows preview of page appearance after drop

    Container Layout

    • Multi-column (usually 3) vs. freestyle
    • Gadgets can be dragged around the page, displacing other gadgets
    • Tabs allow for multiple layouts
      • Tabs can be renamed
      • Gadgets can be dragged into tabs

    Gadget Manipulation

    • Gadgets can be added
      • by URL (with security warnings etc)
      • from Gallery (see Gallery below)
      • by cloning a gadget on user’s page or someone else’s public page or external page
    • Gadgets can be removed
    • Can limit singleton gadgets to one instance per container

    Gadget Gallery

    • Gadgets displayed and rendered with metadata embedded in gadget spec, e.g. thumbnail image, author, etc.
    • Gadgets can be browsed by category, date added, etc.
    • # Users can tag gadgets and gadgets can be browsed by tags; tag cloud; etc
    • Users can rate gadgets
    • # Users can recommend gadgets to their friends
    • Users can comment on gadgets

    Gadget-To-Gadget Communication

    • Gadgets can communicate with each other (Google PubSub)

    Gadget Size

    • # Gadget content can be expanded and collapsed (BBC Beta Homepage)
    • Gadgets can be dynamically resized

    Content Sharing

    • Container can be made public (what happens to personalised widgets?)
    • Container can be shared with “friends” (how are friends decided)
    • Users can invite/permit/disallow friends to use their container

    Gadget Services

    • Proxying service
    • Caching service (extends proxying service)
    • OpenSocial (or generic social networking) service
    • # Advertising service – Gadgets can serve as ads with revenue model
    • # Financial service – Gadgets can charge for services (subscription, one-off, etc.)

    Admin Functions

    • Admin function to tweak gadget/theme rankings, scrutinise/moderate/eliminate gadget/theme submissions, etc
    • Admin function to view metrics, e.g. number of page views, popularity of gadgets, back-end service usage (e.g. proxying and OpenSocial calls)
    • Admin function to manage users (provide support, ban, etc.)

    Where Do Widgets Come From? A Look at Widget/Gadget Content Types

    Ajax, AjaxPatterns, Gadgets, Google, Web, Web 2.0, Widgets


    A while back, I walked through a Google Gadget I made called Digg Roundup, which simply shows Digg headlines and can be customised on topic and popularity. In my quest for an uber-simple tutorial, one thing I skipped on was content type, the subject of the present muttering. There are several content types possible, each with distinct implications for the page architecture and where the gadget sits within it. Below I’ll explain the options and help you understand how to decide between them.

    A gadget is always expressed as some XML sitting at a URL somewhere on the net. When a developer uploads a gadget to Google, all they do is indicate their server location where the gadget is hosted. So a user’s iGoogle homepage is really just a bag of URLs (with layout and preferences for each). Anyways, the gadget is really a mini web page, and inside the gadget XML is a content tag that describes the HTML, CSS, and Javascript that makes up this page. The tag has a type attribute (i.e. <content type="xxx">) and there are three types available…

    Three Content Types

    html (<content type=”html”>)

    All the page content is included in the XML file. It’s just like your standard web page HTML; it contains the (initial) HTML body from top to tail (sans enclosing <body> </body> tags), and CSS and Javascript, which can be inlined or linked, just like a normal web page. The content will be served in an iframe and Google will wrap its own html content around it. In particular, Google will look at other info in your XML file and output html code at the top, such as pulling in libraries and setting up preference data. Incidentally, Google won’t add any visual wrap around the widget at this stage; that is the responsibility of the container, which could be iGoogle, Ning, or any standalone web page that chooses to render the gadget.

    Although the XML is held on your own server, the iframe will ultimately be served from a Google subdomain as its source. e.g. If you look at iGoogle, you’ll see the source is actually something like The point of the “50″ is that each gadget on a page comes from a different subdomain, thus isolating gadgets on the same page from each other for security reasons (there are ways around that restriction for consenting gadgets).

    The main differences between a <htm> gadget and a normal web page is that your content must be static – the XML file cannot be generated on the fly by your server, because Google will cache it. Think of this kind of gadget as a single file that describes your gadget in isolation, which you could mail around or stick on a USB key. The dynamic behaviour comes from the fact that you can link to external CSS and Javascript (which could be generated on the fly) and, moreover, you will typically be making remote calls once the application is running (either on startup, due to a timer, or in response to system events).

    url (<content type=”url” href=”…”>)

    url. This could alternatively be called “external” or “hosted”. You simply provide a URL on your own web server and deliver up the gadget content from there. Specify “” and the user will see an iframe pointing to “”. The content for url gadgets must be dynamically generated, the exact opposite of html gadgets, which are always static. This is due to the way these gadgets use


    This is no longer supported, but it’s worth knowing because (a) the contrasting model helps you understand the other models; (b) older gadgets, as well as Google’s own gadgets, are still able to use this model; (c) Google says they might allow special cases through in the future; (d) other containers do support this model (NetVibes anyway). In contrast to the previous types, inlined gadgets are not contained in an iframe; they are served as plain old HTML embedded into the fabric of the page. Just some content in a div.

    A Note on Preferences

    Persistent preferences are a key feature of widgets. Just a quick note that preferences work slightly differently on html vs url widgets (and inline, but I didn’t look into it).

    With html widgets, the preferences are set as part of the HTML wrap around your widget’s HTML. ie some Javascript variables are set up.

    With url widgets, the iframe source URL includes some CGI parameters expressing the preferences (e.g. up_storyAmount=5).

    Either way, though, Google’s preference library abstracts this detail. As long as you declare a dependency on the library, it will be included and will let you access preferences the same way.

    Decisions, Decisions: Which content type to use?

    So that’s the definitions. Let’s look at the capabilities of each and understand how we might make decisions on which model to use.

    Inline or IFrame (content-type=html/url)?

    Firstly, inlined (html-inline) versus iframed (html and url), assuming inlined is an option for you. Living in the same DOM means that inlined gadgets can talk to each other; dead simple cross-gadget communication is a key benefit of the inlined model. Furthermore, gadgets and the main portal page can talk to each other. You can change the Google logo, for example, or put up a lightbox effect across the entire page.

    Not too surprising that raw inlined gadgets were banned; the security risks are high. e.g. An inlined gadget that asks for your gmail credentials would end up storing username and password in global page space, which means a second gadget could simply read that data and upload it somewhere. You could also screw with iGoogle’s branding, which Google PR probably didn’t appreciate and could lead to phishing attacks. Google could rely on code reviews to minimise the risk of inlined gadgets, but that’s a very manual, unscaleable, approach, and users can in fact add gadgets from any URL (although they made it a lot harder to do that a few months ago; AFAICT you need to use the developer gadget in order to add a gadget that’s not in the catalogue). Maybe inlined gadgets will come back if a product like Caja proves itself worthy of automatically sanitising web apps. It’s a hard problem, but there are some good benefits to be had if widgets can live safely together on the same page and domain.

    In summary, choose inlined only if you need the extra functionality available and only if you have assessed the risks of tampering from third-party widgets.

    IFrame from widget server (content-type=html) vs IFrame from your own server (content-type=url)

    Second and really the key decision you have to make with Google Gadgets today, html versus url. Reason to choose “html” content type over “url” content type:

    • Javascript API. You can use the full Gadget Javascript API with no effort – you don’t even have to include a script tag because iGoogle injects it for you. With the “url” type, using the Gadget Javascript API is a hassle – you have to dynamically generate the script tag, which is not only a bit of scripting effort, but forces you to generate your iframe from a script. Furthermore, cross-domain restriction means that even if you do pull in the API, you can’t use the remoting support (the _IG_Fetch* functions for proxying and caching).
    • Cross-gadget communication. You can run cross-gadget communication using the Pub-Sub framework.
    • Resources. Google serves it for you, which saves bandwidth and maintenance costs. In fact, you will have zero bandwidth costs if your widget functionality is either self-contained (e.g. a calculator) or only hits third-party services (as opposed to your own servers’ services).
    • Popularity. Pure speculation, but you’re probably more likely to be featured in Google’s gadget gallery as they can inspect everything about your application’s workings (except for any remote calls back to your server).
    • Inline-like. If inlined widgets do make a comback, you’ll have an easier migration path.

    Reasons to choose “url” content type over “html” content type:

    • Full page refreshes. Because it’s coming from an external iframe, you can do traditional, non-Ajax, programming. ie. User clicks on link to see a new page. User submits a form. Page auto-refreshes. With this type of widget, you could write a perfectly working gadget without knowing a button of Javascript.
    • Personalisation and Security. You can make XHR calls from the widget directly to your own server and make use of any cookies that have been set up. e.g. If the user has already logged into your main web app, then you widgets will be able to present personalised data. With “html” widgets, you would only be able to make use of cookies using cross-domain JSON, and cross-domain JSON is unsafe. You could possibly do it more safely using the cross-domain iframe fragment identifier hackFacebook’s new JS lib works that way – but I haven’t investigated it.
    • You can easily host the widget as a standalone web page (though it’s largely pointless and will look silly unless you pull some CSS madness).

    Want to Learn More?

    Ajax Functionality and Usability Patterns – Podcast 3 of 4: Visual Effects

    This is the third in the four-part series on Ajax functionality and usability patterns (Book: Part 4, pp 327-530). An audio discussion of visual effects is ideally short and sweet, so this podcast is but 13 minutes long.

    This 13-minute podcast covers ten patterns of Ajax Architecture (Book: Chapter 16, pp 445-472):