Monday, December 03, 2007

Defining $ for Dojo and setting a function prototype

This is a little story about defining a $ function for Dojo, and a more fundamental discussion about setting a prototype for a function.

Defining a $ function for Dojo

I generally prefer using full namespace names when using JavaScript libraries. It helps avoid confusion when passing around code or copying/pasting code that may be used in mixed-library environments. However, if you have contained environment, you may prefer extreme brevity over namespace robustness.

So, how can we define a function named $ that works with the base Dojo functionality (the stuff you get in dojo.js: DOM querying, event handling, XHR, style, basic effects, JSON, array, language and object hierarchy helpers). Ideally, $ would be mapped to dojo.query, to allow easy node selection, but then any other dojo method, like dojo.connect() would be available via $.connect().

Here is something that works:

$ = function(){
return dojo.query.apply(dojo, arguments);
};
dojo.mixin($, dojo);


That does the basics: defines a function $ that returns the result of a dojo.query() call, then copies all the properties of dojo to the $ function object (via dojo.mixin()).

This allows for making these sorts of calls:

//Set on onclick listener for all divs in the body
$("div").onclick(function(){
alert("div clicked.");
});

//Do an XMLHttpRequest POST using data from
//a form with a DOM ID of "myFormId"
$.xhrPost({
form: $("#myFormId")[0]
});


Setting a prototype for a function

So that is nice if all I want is dojo base. However, Dojo has a neat module loader, and ideally I want to be able to do something like:

dojo.require("dojo.io.script");


Then later in the code just do a $.io.script.get(). However, this will not work if you define the $ function before doing the dojo.require() call. dojo.mixin() only copies over object properties that exist at the time of the dojo.mixin() call.

So, ideally, I want to set the prototype lookup for $ to be dojo. That way, as new things are added to the dojo object, they will be automatically available via $.

At this point I hit a wall, at least with my current understanding of JavaScript. I was hoping I could do something like this:

var Dollar = function(){
return function(){
return dojo.query.apply(dojo, arguments);
};
};
Dollar.prototype = dojo;

$ = new Dollar();


In JavaScript constructor functions, you do not have to return a value. If you do not, whatever the "this" value was inside the constructor function gets returned as the result of the call the "new" call.

However, if you return a value from the constructor, that is what is returned as the result of the "new" call. This is what Dollar does above.

What I was hoping would happen is that the returned function (that calls dojo.query()) would have its __proto__ property set to Dojo, but I think the __proto__ binding happens before the constructor function is called, so that you can access methods on "this" inside the constructor.

Suppose there was a constructor function called ConstructorFunction, and you called "new ConstructorFunction()". Here is some very crude psuedo-code for what I think the "new" call is doing:

var temp = {};
temp.__proto__ = ConstructorFunction.prototype;
return ConstructorFunction.apply(temp, arguments);


If this is what is going on, it makes sense that by returning a value from the constructor function call, I lose out on the __proto__ binding.

So why not just set the $.__proto__ value directly after $ is defined? Accessing __proto__ is strongly discouraged. It is an implementation's private implementation detail, subject to change. Also, doing that only works right now for Firefox 2 and Safari 3 (failed for IE 6 and Opera 9.24). So stop thinking about it.

I also tried something like this:

Function.prototype = dojo;
$ = new Function("return dojo.query.apply(dojo, arguments);");


but that fails for what I believe to be similar reasons as my first prototype attempt.

I am very interested if someone can suggest how I can modify the $ function's prototype lookup, so that it uses the dojo object. It is neat to think about defining an object that also supports the () operator on it (with a custom prototype).

I want something that works with today's browsers, not ECMAScript 4. Although seeing an ECMAScript 4 implementation might be instructional for the future.

Saturday, July 28, 2007

Ajax Experience Talk about Dojo XD Loader

I attended the Ajax Experience West conference this week, and gave a talk about Dojo's XD loader. It was my first time giving a talk in that venue. I got some good feedback from Naveed (from the dev.aol.com group): I was talking a bit too fast, and didn't make enough eye contact, focusing too much on the slides. The content was good, but I could improve on the delivery. I was suspecting as much, but it was great to get the good feedback. Apparently I'll get feedback (hopefully) from the talk evaluation sheets.

It was neat talking with one of the jQuery contributors, Yehuda Katz, who has been thinking about how to handle serving jQuery plugins. They have been considering a way to load plugins, and possibly specify dependencies. They may not need the full xd dependency mapping, but it would be neat to share some ideas in that area. In the talk, I talked about trying to modify other toolkit code from YUI, Ext and jQuery plugins to load via the dojo loader. I still might try that as an experiment.

In the presentation, I talked a bit about xd loading in general, and to address the security concerns of how to verify the code you are xd loading has not been corrupted or changed. I was thinking using digests of some kind would be good. Ideally, the browser could do it before it evaluated the JS code.

I mentioned the idea to Douglas Crockford, and he had nice idea of being able to specify more than one URL for the script, to serve as backups in case there was a failure with one of the first one failed. He thought it would make a good browser plugin. The multiple URL thing also came up as a suggestion in the Q&A part of the talk, but in the context of dojo.registerModulePath().

Initially, part of the Q&A discussion was ways to do the digest checking without needing a browser plugin, but the more I think about it, it really needs to happen by the browser, since we need to do the check before the imported script is evaluated in any way. And if we are doing xd-loading of the script, that means (at least today) that a script tag is going to be used. So we need to extend the functionality of the script tag.

Looking around at the web, it seems like (as usual) these ideas have been in the air before. I went to Andreas Andreou's cross-site js sharing post, which pointed me to this moz.dev.platform group discussion. This mozilla bug was referenced, and from there, I went to Gervase Markham's Link Fingerprints page.

The Link Fingerprints seems like a workable system. Most of the discussion has been focused on sharing of library code, but it also seems to dovetail nicely with a security aspect. I might ask the moz.dev.platform group about the status of the mozilla bug and mention my desire to have it for security concerns. I'll also ask about supporting alternate backup URLs for download. Maybe as nested script tags?

If this works out, I would feel much more comfortable strongly suggesting xd loading for folks. It would result in a safer web. Very nice.

Saturday, July 07, 2007

TinyBuddy IM: Instant Messaging for iPhone

(Update 7/15/2007: I've moved the TinyBuddy IM-related info over to another blog: Tiny Notes)

Have an iPhone, but wish you could IM your buddies? Now you can, with TinyBuddy IM. It is an AIM® Enabled web-based IM tool. It works by using the Web AIM API, OpenAuth and Dojo.

The nice thing about this solution: you do not send your AIM password to me -- you are redirected to AOL's OpenAuth servers for authentication. My JavaScript only sees an auth token. Furthermore, my web page has to get your explicit consent before accessing your buddy list data and before sending the first IM or presence change.

So the price of this added security is pop-up windows. A new window will be popped so you can authenticate with the OpenAuth servers, and also when giving consent to the application to access your buddy list and IM. For the consent prompts, you can choose "Grant Always" to avoid them on subsequent logins. I think the pop-ups are worth the added security, and at least in iPhone's Safari, window popping looks neat. Unfortunately, the OpenAuth Sign In and Consent pages are made for larger windows, so you will have to double-tap zoom to read them.

Another neat feature of this web application: it is pure JavaScript, HTML and CSS. No server-side languages needed. Dojo really made it easy to do this. I used Dojo 0.4.3 because I want to reuse this code for some other projects that are on 0.4.3, but if/when I get enough time, I would like to port it over to the 0.9 code.

So give it a whirl if you like. I'm sure the code it not bulletproof, and I've noticed enough weirdness with iPhone's Safari to guarantee that I will not be able to give comprehensive support. Also, even though I'm an AOL employee, AOL does not endorse this project or have anything to do with it (but thanks to my co-workers for ideas and early testing, all done of their own accord).

Also, I'm using an OpenAuth dev key, so if there is too much usage you might see some rate limit errors, but we'll see how it goes.

Some other interesting tidbits:
  • The Web AIM API is a Comet API. It uses long polling to work cross-domain in the browser. I'm using more of a short poll with pauses between the polls to hopefully smooth out network hiccups on the phone.
  • Don't like the CSS? You can make your own and tell the app to use it instead. Go to the test launcher page to specify the path to your CSS. Click the Launch button, then copy launch URL. Use that URL when you want to use the application. This feature is not allowed for IE browsers given its security problems with CSS "expressions".
  • I'm serving the code gzipped. The HTML, CSS and JavaScript combined come to about 90 KB. So it is tolerable on the EDGE network.
  • Use the iPhone two finger scroll to scroll the buddy list and IM conversations.
  • Typing IMs should be optimally sized for use with the virtual keyboard. Just type in the text box at the bottom of the IM window and press "Go" on the keyboard.
  • I'm using Dojo Accordions for the IMs and buddy list. I like the use of space with that model and that I can show you incoming IM text if that IM AccordionPane is in the closed position.
  • onbeforeunload does not seem to fire for iPhone Safari. That makes it hard to log out correctly, so to clear your OpenAuth cookies, be sure to use the Available, Sign Out menu item.
To use TinyBuddy IM, just type http://tybyim.com in the iPhone Safari browser. You can try it in other browsers, but it looks best in the iPhone Safari.

Friday, May 25, 2007

Searchable Ajax with JavaScript controllers and a headless Gecko

I'm back from a much appreciated vacation, and just wanted to jot down some thoughts that I want to expand on later. This is a continuation of how I want to build web applications on the RIA-end of the web scale, but an approach I could use for just about any data-driven web site.

I only want to use the server to serve files and to implement data storage. There should be no server-side work dealing with the UI controller. This means the "application server" part of the server (the PHP, Java, .Net, RoR part) should only be handling the API to save/modify/get the data. It should not be doing any presentation or UI flow. JavaScript, CSS and HTML should be used for the presentation and UI flow.

So, the app server implements the data API as a REST API. This gives a good boundary of what should be done in the app server. The data should use XML (preferrably ATOM) or JSON as the data markup. The only presentation part might be to apply something like an XSLT transform of the data if the GET request matches a browser or searchbot user agent. Alternatively, use the domain name of the GET request to know whether or not to apply the XSLT transform (sometimes it is nice to host the pure data API on a different domain for security and load reasons).

Progressive Enhancement should be used for adding the JavaScript controller to the page, so that hyperlinks to other resources can be followed by searchbots or non-JS enabled browsers. Editing of the resources via an HTML interface could be restricted to JavaScript-enabled browsers (mostly where a UI controller is needed anyway, and search bots only care about indexing GET resources anyway).

So that is a baseline. This baseline now cleanly separates the UI controller from the server side, and makes it less relevant what sort of app server technology you use. The UI controller is kept in the JavaScript realm, no more splitting it between JavaScript and [Struts, RoR, Django, etc...].

Now it is time to jump the shark:

Restrict the data API to its own domain, separate from the domain used for presenting the UI for the browser/search bots. For example, use api.domain.com for the data API and ui.domain.com for the browser/search bot interface.

The HTML files on ui.domain.com use Ajax-like techniques access the resources on api.domain.com to show the UI. The Ajax-based data loading would have to be a cross-domain ajax. If the API and UI domains share a common sub-domain, document.domain tricks could be used. Otherwise (and more interesting to me personally), JSONP or XMLHttpRequest IFrame Proxy could be used.

A nice benefit of this approach is that all of the UI pieces (HTML, JavaScript and CSS) are highly cacheable and servable from very different domains than the API domain.

So what about search bots? They cannot do the Ajax techniques, at least not yet. For them, in the web server config, route their user agents to a web server module that uses Gecko to fetch the page, and do the ajax loading. The HTML can send an event to Gecko (this custom, headless gecko can export a function, something like window.onAjaxFinished()) to tell it when it is done rendering. Then the Gecko module serializes the current state of the DOM and sends that HTML string back to the search bot. It could even keep a local cache if it liked, if the resources did not change that often.

This approach would allow the search bot to get a good representation of the page for their indexing, and since Progressive Enhancement was used to bind to resource hyperlinks in the document, the search bot can still navigate to other resources on the domain (and those requests would be funneled through the web server's headless Gecko module).

In this model, REST via HTTP becomes the new JDBC. And more importantly to someone like me who enjoys the front-end -- I don't need to learn about any of the app server flavors of the month to implement data access (the other app server-hugging developers on my team can do that).

So now I just need to do a custom build of a headless Gecko that I can use in a web server module. Any pointers are appreciated. Ideally someone should do this as an open source project so everyone can benefit.