LoJAX (Low-technology AJAX)
12th June 2006
LoJAX
is a re-creation of the
window.XMLHttpRequest
object, designed for
low-specification and legacy browsers.
It can also be used in browsers that already have support, to provide additional functionality not available in any native implementation.
LoJAX uses a combination of client-side and server-side code to make real HTTP requests, which have the same context and permissions as regular XHR requests, and return proper headers and status information.
LoJAX
supports GET, POST and HEAD,
and provides all of the principle methods and events,
like open()
, send()
and
onreadystatechange
.
However it doesn't support synchronous requests,
and more notably, there's no support for
responseXML
.
responseText
is supported fine,
allowing data to be received as plain text,
HTML or
JSON
(or any text-based format), and although individual requests are
always asynchronous, LoJAX
has the ability to synchronize multiple
requests on the same or multiple object instances, to
ensure that each request is returned in the order it was made.
Since the actual HTTP request is made server-side, LoJAX also has the ability to retrieve data from external domains.
- Browser support
- Get the script
- Client-side configuration
- Server-side configuration
- What's the point?
- Specification
Browser support
The script is known to work in the following browsers:
- Sony PSP (Version 2.00 or later)
- Opera 5, 6 and 7 (Versions 5.0 to 7.5 inclusive)
- Mac/IE5 (Version 5.0 or later)
- Konqueror 3.1
- OmniWeb 4.5 and 5.0
- Espial Escape 5.1
This is in addition to being supported in all browsers
which natively support XMLHttpRequest
, and when used in these
browsers it can be configured either to have a different function name,
or to have same name and hence over-write the native object (except in Safari and IE7;
please see the additional browser notes for details).
Get the script
Download the zipfile [18K] and unzip it; it contains all the script files you need, along with three test files for demonstration which you don't need to keep.
The core components of LoJAX are a JS
script called "lojax.js"
, and a
PHP script called
"lojax.php"
. ("lojax.js"
is compressed so that it loads faster,
but there's also a fully-commented version included in the zipfile,
for reference or further development.)
The only script you need to include directly on your page is the JavaScript (hence the page itself does not need to be parsed by PHP - LoJAX can be used on plain HTML pages, or those parsed by a different language).
The script can go anywhere on the page, and its functionality will be immediately available to any scripting that comes after it:
<!-- LoJAX (Low-technology AJAX) by brothercake - http://www.brothercake.com/ -->
<script type="text/javascript" src="lojax.js"></script>
For maximum usefulness overall, the optimum place to put the script
is at the very top of the <head>
section.
However, since it uses procedural code to generate a
<form>
and <iframe>
(which act as a data courier),
it would actually invalidate the
DOM on the fly
to have it in the <head>
.
I could argue on a purely pragmatic level, that isn't really so bad. The validator won't know the difference, and real people aren't affected - because crucially, the elements are generated in such a way that they can only be accessed programatically by the script - users can't reach them manually.
But a script-aware validating parser would be affected, so therefore,
the script can only be safely used like this on pages served as
text/html
(because on pages in
XHTML mode,
a validating parser may throw a DOM
exception and abandon the output).
Otherwise - the script will generate fully valid output if
it's used in the <body>
section of a page,
having a doctype of
XHTML
1.0 Transitional or earlier.
Client-side configuration
By default, the JavaScript will assume that the PHP script is in the same directory as the page it's being used on, and if this is true you're good to go.
But if the files are in different directories, or if you're using the script in different places within a site or application's heirarchy, you will need to define an absolute or web-root path to the PHP script. (This also means you can write your own server-side component in another language, and then specify the path to that.)
You can define the path using the optional client-side configuration
file, "lojax-settings.js"
; this must be added
before "lojax.js"
in the source code:
<!-- LoJAX (Low-technology AJAX) by brothercake - http://www.brothercake.com/ -->
<script type="text/javascript" src="lojax-settings.js"></script>
<script type="text/javascript" src="lojax.js"></script>
Then you can configure it. The path to the server-side script is the most significant setting, but there are four other options as well:
- path to server-side script
-
The script works by submitting a form to a server-side script, and this value defines the path. The default value is a local path to the same directory, but if you're using the script in different places throughout a site or application, it's simplest to set a web-root path like
"/lojax.php"
, or an absolute value like"http://www.domain.com/lojax.php"
.You could also vary a local path on a per-instance basis, by having multiple copies of
"lojax-settings.js"
in different places. - what kind of implementation to use
-
The script's default behavior is to attempt to implement the re-creation only if a native
XMLHttpRequest
object is not found; this behavior is defined with the value"auto"
.You can also set the value
"lojax"
, which means to use the re-creation for all browsers irrespective of native support; or the value"none"
to disable this script entirely. - client time-out for abandoning a request (seconds)
-
Specify a value in seconds, for how long the script should wait before abandoning a request.
- expose the courier mechanism
-
Specify
true
orfalse
for whether to expose the generated<form>
and<iframe>
. This is a development setting and should always have the valuefalse
in production use. - try to negate iframe history events
-
Every time the script makes a data request through the courier
<iframe>
, a history event may be generated. Whether or not it actually is generated varies widely by browser and circumstances, and so the script attempts to find a safe path through these quirks and negate as many history events as it can.The purpose of this is so that when a user presses the Back button, the top-level page will go back, rather than it stepping through pages in the hidden
<iframe>
.But the negation errs on the side of caution - if an event is not known to be safe, it will not be negated, and in this case "not safe" means there's a possibility the top-level page will go back, instead of the
<iframe>
page; this is clearly not acceptable, hence the need to be cautious.So even if with this setting enabled (set to
true
) it's possible that residual events are still generated, resulting in the occasional need for users to click the back button twice, to go back to the previous page. - lojax function name
-
You can specify the function name which is used for the re-creation object. The default value is obviously
"XMLHttpRequest"
, but you can call it anything you like.This might be useful if you wish to separate scripted functionality between devices using this implementation and those using the native object, or if you wish to implement the framework for all browsers without over-writing their native object.
It's also necessary if you want to implement the framework in later builds of Safari (1.2 or later), or in Internet Explorer 7, in which it's not possible to over-write the native object, and therefore it will only work if the object has a different name (see additional browser notes).
Server-side configuration
By default, the PHP script is configured to reject requests outside its host domain. This is necessary for security, because an open framework would amount to a free proxy for spammers.
But you can specify additional hosts using the optional server-side
configuration file, "lojax-hosts.php"
. If this file exists
in the same directory
as "lojax.php"
, and contains
a valid $lojax_hosts
array of one or more host names,
then the script will be allowed to make requests
to any of the specified hosts. For example:
<?php
//list host names or IP addresses
//to which the script is allowed to make requests
$lojax_hosts = array(
'www.mysite.com',
'www.myothersite.com'
);
?>
What's the point?
That's a good question!
I originally wrote this because I wanted the animated songpointer in my iTunes web interface to work on the Sony PSP; yet in that specific case I still haven't succeeded, due to the severe limitations of DOM scripting generally in that browser.
And that's the rub - what's the point of extending AJAX functionality to devices which don't have the complementary DOM support?
Clearly in many cases,
the answer is none at all
, and I'm certainly not suggesting that
everyone should start trying to making all their applications work in these
ancient and low-tech devices!
But it's nice to have the option, and it's for niche applications that this script really comes into its own. Maybe you need to support the PSP, Mac/IE5 or Opera 7, for whatever reason. Maybe you need data from several domains, or you'd like a managed solution that provides control over the order in which requests are returned. The LoJAX framework:
- opens up AJAX-based scripting to a wider range of devices
- can be configured to retrieve data from anywhere on the internet
- provides a platform for synchronising multiple requests
There's nothing revolutionary in all of this - there are plenty of
alternative RPC techniques,
that can be used in browsers which don't
support XMLHttpRequest
, or to bypass its normal limitations.
But LoJAX abstracts the mechanics into a familiar JavaScript object, which is instantly easy to use because there's no new syntax to learn.
Specification
The implementation is based on the retro-specification drafted by the W3C, which itself appears to be largely based on what Firefox does. However there are missing features, as well as additional features, and there are deviations from the letter of the specification in places.
Core features supported
-
GET
,POST
andHEAD
requests -
open()
,send()
,abort()
,getAllResponseHeaders()
andgetResponseHeader()
methods -
readyState
,responseText
,status
andstatusText
properties -
four
onreadystatechange
events per request (one each of states1
to4
) -
301
,302
,303
or307
redirects are followed (up to five per request) - username and password authorization (but see the security note below)
- same-origin security restrictions apply by default
Unique features in LoJAX
- same-origin security restrictions can be overridden on a per-host basis
- browser progress bar is always shown when a request is made
- multiple requests are buffered to return in the same order they were made
- additional
open()..send()
commands can be made without re-instantiating the request object -
two custom error codes may be returned:
466 (Host Not Allowed)
when attempting to make a request outside the current host or list of allowed hosts; and467 (Unsupported Protocol)
when attempting to make a request using an unsupported protocol (such ashttps
)
Features not supported but safe to use
-
setRequestHeader()
method exists so that it can be used without errors where native implementations require it forPOST
requests, but it doesn't actually do anything
Missing features
-
responseXML
is not supported (it's alwaysnull
) -
abort()
cannot physically stop a request, it merely resets and stops watching the current request object - the server-side process is uncontrollably asynchronous at this point, and data may be subsequently returned, but it will be ignored -
binary submission using
multipart/form-data
is not supported -
headers are only available if the
readyState
is3
or greater -
no support for sending an XML
document with the
send()
method -
status
andstatusText
, whenreadyState
is an inappropriate value, will returnnull
instead of raising a DOM exception -
the challenge/response behavior for password protected resources
is not exactly the same - if you don't send the right details
in the original request, the browser won't prompt you to enter them,
you'll just get a
401
Other varations from native XHR
- requests made from the server-side script are HTTP 1.0
-
the returned headers may not be identical to those of a native
XHR request;
for example, they won't include
"keep-alive"
or"TE"
because the connection type will be"close"
not"keep-alive"
; and additional values may be present, such as"set-cookie"
Security note
If a request includes username and password authorization,
this information will be sent unencoded to the
"lojax.php"
script. Although JavaScript can do
Base 64 encoding, the process requires bit-shifting, which many of the older supported
browsers can't handle (such as Mac/IE5).
Additional browser notes
LoJAX is not supported
in Safari 1.0 or 1.1. Although the core scripting works, the interface
breaks the back button (pressing Back reloads
the current <iframe>
page,
instead of going back).
I don't know of any way to fix this, so the script has
to be specifically disabled.
In Safari 1.2 or later and Internet Explorer 7
(both of which already support XMLHttpRequest
),
the script cannot over-write the native object; so if you wish to
use the LoJAX framework for these browsers,
you will need to define a
function name other than "XMLHttpRequest"
.
Discuss this page
Comments? Suggestions? Join the discussion!