Russian version
Add to Del.icio.us
English version
Digg It!

 Old-School dkLab | Constructor | JsHttpRequest 5: cross-browser AJAX + file uploading | Complete documentation 

Site map :: Project Orphus :: Constructor


2006-07-29
Discuss at the Forum

This article describes the JsHttpRequest library (LGPL license) which elaborates data loading with AJAX (Remote Scripting) method. Here is a short list of its most interest features and differences from analogs.

  • Cross-browser compatibility. Library works with IE5.0+, Mozilla 1.7+, Firefox 1.0+, Opera 7.20+, Safari ("+" means "in specified and newer versions"). By this token, code could work without ActiveX and XMLHttpRequest support (but if these capabilities are avalilable, they are used automatically). Cross-browser compatibility is guaranteed by the library's automatic testing framework.
  • Support and "transparent" work with any character encoding (including any national). You may write your scripts in a usual way, library will take care about everything else. If you use windows-1251, koi8-r or UTF-8, you do not even need to install any additional PHP extensions (like iconv or mbstring).
  • File uploads from user browser to the server without page reloading.
  • Compatibility with prototype. Prototype is a very popular tool to help JavaScript programmer. It includes AJAX support and other features. Library JsHttpRequest could be used as its server part in PHP (after inclusion of a small compatibility module JsHttpRequest-prototype.js). Even so, all additional features of JsHttpRequest (cross-browser compatibility, file uploading support, work with national charsets etc.) are still available.
  • Full support of PHP debug features. If server-side script fails on an error (including fatal error, e.g., call of undefined function), client-side script can handle it correctly and show diagnostics. Besides response data, it is passed the output flow of server-side script which contains PHP error messages.
  • Multi-dimensional data structures exchange (JSON analog) in client request and server responce. At that, are used the standard PHP instruments multi-dimensional associative arrays (request data avaliable at $_REQUEST, response data may be saved to $_RESULT), as well as JavaScript standard capabilities nested objects and properties. No XML in the level of API: you work with "native" language variables.
  • PHP session support using usual way.
  • Optimal data loading method choosing (XMLHttpRequest, Microsoft.XMLHTTP, <SCRIPT>, <IFRAME>) depending on current browser. In particular, ability to load data from foreign hosts.
  • Component-based code allows to manually cut off unneeded methods of data loading to shrink JsvsCript code size. E.g. ifyou do not plan to support AJAX file uploading, you may choose the version with XML and SCRIPT loaders only.
  • Interface is compatible with XMLHttpRequest.

Preface

Not so long ago, the new Google service became rather popular: the so-called Google Suggest. Those, who haven't seen it yes, can look at the service right now: http://www.google.com/webhp?complete=1&hl=en.

The example of Google suggestions

For dummies 

Google Suggest work principle is that if you enter several letters, the special JavaScript program calls the Google site and requests the ten most popular words, which begin with the same letters. The script works so fast, that the drop-down list with variants appears almost instantly. Obviously, the page doesn't reload - everything is realized only via JavaScript and DHMTL.

To realize the dynamic loading, Google uses the following means:

  1. In Internet Explorer: an ActiveX-component, called Msxml2.XMLHTTP or Microsoft.XMLHTTP.
  2. In Mozilla and Firefox: an internal class XMLHttpRequest.
  3. In Opera: the dynamically created null-size <IFRAME> (it's hidden).

The way how Google Suggest works, is described almost everywhere, and I am not going to be repeated. Instead, I'll introduce another method called JsHttpRequest, which gives more browser compatibility than Google Suggest and is more flexible.

The method, which realizes the dynamic load support in Google suggest, is shown below, in example of loading the source code of the current page. (I do not cite the <IFRAME> support, because it's rather difficult. We are talking only about XMLHttpRequest and Microsoft.XMLHTTP ActiveX-component).

Listing 1: test/JsHttpRequest/ActiveX.htm
<script>
function doLoad() {
  var req = window.XMLHttpRequest? 
    new XMLHttpRequest() : 
    new ActiveXObject("Microsoft.XMLHTTP");
  req.onreadystatechange = function() {
    if (req.readyState == 4) 
      alert('Loaded:\n'+req.responseText);
  }
  req.open("GET", document.location, true);
  req.send(null);
}
</script>
<input type="button" value="Show me" onclick="doLoad()">

This code will only work in Mozilla (Firefox) and IE (ActiveX enabled). Opera 7.x and also users who disabled ActiveX because of security reasons, do not see anything.

Library usage

To prevent you from being too bored, I'll demonstrate the JsHttpRequest library in action right now. Enter several words in the text field below, and in 2 seconds the search results for that words will appear (http://forum.dklab.ru forum). The page will not be reloaded.

For dummies 

Code for forum data loading is rather comples. That's why we analyze simplier code, specially written to be easy understandable.

JavaScript: object JsHttpRequest (frontend)

It's very easy to use JsHttpRequest object in JavaScript-program. It's interface almost doesn't differ from Firefox XMLHttpRequest or IE Microsoft.XMLHTTP (it was developed specially in that way).

I'll show you an example of the page, which provides the MD5 hash-code generation for the entered string. The calculation is made on the server, and browser only calls it for data, using JsHttpRequest object. (Live demo)

Listing 2: test/JsHttpRequest/smpl_frontend.htm
<script src="../../lib/JsHttpRequest/JsHttpRequest.js"></script>
<script type="text/javascript" language="JavaScript">
function doLoad(value) {
    // Create new JsHttpRequest object.
    var req = new JsHttpRequest();
    // Code automatically called on load finishing.
    req.onreadystatechange = function() {
        if (req.readyState == 4) {
            // Write result to page element (_RESULT becomes responseJS). 
            document.getElementById('result').innerHTML = 
                '<b>MD5("'+req.responseJS.q+'")</b> = ' +
                '"' + req.responseJS.md5 + '"<br> ';
            // Write debug information too (output becomes responseText).
            document.getElementById('debug').innerHTML = req.responseText;
        }
    }
    // Prepare request object (automatically choose GET or POST).
    req.open(null, 'smpl_backend.php', true);
    // Send data to backend.
    req.send( { q: value } );
}
</script>

<form>
    Text: <input type="text" name="text">
    <input type="button" value="Calculate MD5" 
      onclick="doLoad(this.form.text.value)">
</form>

<div id="result" style="border:1px solid #000; padding:2px">
    Structured results
</div>
<div id="debug" style="border:1px dashed red; padding:2px">
    Debug info
</div>

If you look more carefully, you can easily see that JsHttpRequest appliance almost doesn't differ from using XMLHttpRequest or Microsoft.XMLHTTP. However, there is one very important feature of the library: the smpl_backend.php work result is useful to be gotten from the req.responseJS property. As you can see from the context, loader puts the following hash there:

Listing 3: result object
{ 
  q:   'query', 
  md5: 'MD5-code of the entered string' 
}

The req.responseText field contains data given by smpl_backend.php script to its standart output (echo operator). In most cases it just contains the error messages (if errors took place), and it's why that property is used as debug property.

Lyrical deviation 
Although, nothing stops us from writing the handler so, that it would pass the main work result to req.responseText (but it is not very useful — see below).

So, after loader responce receiving we get the following properties of req object:

  • responseJS: ata with any structure (e.g. multi-dimensional arrays) generated by loader.
  • responseText: all other data and error messages.

PHP: class JsHttpRequest (backend)

Now it's time to look how smpl_backend.php loader looks. It is very simple.

Listing 4: test/JsHttpRequest/smpl_backend.php
<?php
// Load JsHttpRequest backend.
require_once "../../lib/JsHttpRequest/JsHttpRequest.php";
// Create main library object. You MUST specify page encoding!
$JsHttpRequest =& new JsHttpRequest("windows-1251");
// Store resulting data in $_RESULT array (will appear in req.responseJs).
$GLOBALS['_RESULT'] = array(
  "q"     => @$_REQUEST['q'],
  "md5"   => md5(@$_REQUEST['q']),
); 
// Below is unparsed stream data (will appear in req.responseText).
?>
<pre>
<b>Request method:</b> <?=$_SERVER['REQUEST_METHOD'] . "\n"?>
<b>Loader used:</b> <?=$JsHttpRequest->LOADER . "\n"?>
<b>_REQUEST:</b> <?=print_r($_REQUEST, 1)?>
</pre>

You can see that it is enough to fetch parameters passed by JavaScript-part from standard PHP variables ($_GET or $_POST or $_REQUEST), and then — fill special array $_RESULT with any structure information. (In our case it is assiciative array.) This array will be passed without any changes to JavaScript-code and will appear in responseJS property. Fortunately, arrays in JavaScript and arrays in PHP are organized in an almost identical way, that's why we can easily translate the PHP-array:

Listing 5: PHP array with results
$_RESULT === array(
  "q"   => 'query',
  "md5" => 'MD5-code of entered string'
)

...into identical JavaScript object:

Listing 6: identical JavaScript object
req.responseJS === { 
  q:   'query', 
  md5: 'MD5-code of entered string' 
}

File uploading to server

JsHttpRequest library supports file upload without reloading of the page. The interface is the same, but you need to pass the link to HTML-element like <INPUT type="file"> to send() method instead of string or integer.

Listing 7: test/JsHttpRequest/upl_frontend.htm
<script src="../../lib/JsHttpRequest/JsHttpRequest.js"></script>
<script type="text/javascript" language="JavaScript">
function doLoad(value) {
    // Create new JsHttpRequest object.
    var req = new JsHttpRequest();
    // Code automatically called on load finishing.
    req.onreadystatechange = function() {
        if (req.readyState == 4) {
            // Write result to page element (_RESULT becomes responseJS). 
            document.getElementById('result').innerHTML = 
                '<b>MD5("'+req.responseJS.q+'")</b> = ' +
                '"' + req.responseJS.md5 + '"<br> ';
            // Write debug information too (output becomes responseText).
            document.getElementById('debug').innerHTML = req.responseText;
        }
    }
    // Prepare request object (automatically choose GET or POST).
    req.open(null, 'upl_backend.php', true);
    // Send data to backend.
    req.send( { q: value } );
}
</script>

<!-- Please note that we must specify enctype to multipart/form-data! -->
<form method="post" enctype="multipart/form-data" onsubmit="return false">
    File: <input type="file" name="upl">
    <input type="button" value="Calculate MD5" 
      onclick="doLoad(this.form.upl)">
</form>

<div id="result" style="border:1px solid #000; padding:2px">
    Structured results
</div>
<div id="debug" style="border:1px dashed red; padding:2px">
    Debug info
</div>

See that example in action: file upl_frontend.htm. Loader script upl_backend.php looks as usual - the standard PHP methods of handling uploaded files are used.

Listing 8: test/JsHttpRequest/upl_backend.php
<?php
// Load JsHttpRequest backend.
require_once "../../lib/JsHttpRequest/JsHttpRequest.php";
// Create main library object. You MUST specify page encoding!
$JsHttpRequest =& new JsHttpRequest("windows-1251");
// Store resulting data in $_RESULT array (will appear in req.responseJs).
$GLOBALS['_RESULT'] = array(
  "q"     => 'file ' . $_FILES['q']['name'],
  "md5"   => md5(@file_get_contents($_FILES['q']['tmp_name'])),
); 
// Below is unparsed stream data (will appear in req.responseText).
?>
<pre>
<b>Uploaded files:</b> <?=print_r($_FILES, 1)?>
</pre>

The name attribute value of <INPUT type="file"> doesn't matter: you always recieve a file with identifier, that you set in send() method (it's "q" in our example, though the name of INPUT field is "upl").

When you use file upload, you need to follow next rules:

  • You can only upload files that user chose in <INPUT type="file"> field. This is the only way to upload file without violation of browsers' security politics.
  • Place INPUT fields for upload inside <form>...</form> contatiner. Upload won't work many browsers otherwise.
  • It is necessary to set enctype="multipart/form-data" attribute of FORM tag, or upload will not work in IE (there's an error in the browser which doesn't allow to change this attribute dynamically). The enctype attribute is the only one required other attributes are optional.
  • If you upload several files at once, all of them must belong to the same form. You cannot upload files, whose INPUT elements are in different forms.

If you violate one of these rules, the JsHttpRequest will throw an exception which will be easy to find.

Sending of a whole form to the server

It's often needed to send not separate values, but a variety of elements, which are groupped in HTML-form. Instead of listing all that elements in send() parameters you can pass it the link to the FORM container in one of the elements of hash:

Listing 9: test/JsHttpRequest/frm_frontend.htm
<script src="../../lib/JsHttpRequest/JsHttpRequest.js"></script>
<script type="text/javascript" language="JavaScript">
function doLoad(value) {
    // Create new JsHttpRequest object.
    var req = new JsHttpRequest();
    // Code automatically called on load finishing.
    req.onreadystatechange = function() {
        if (req.readyState == 4) {
            // Write result to page element (_RESULT become responseJS). 
            document.getElementById('result').innerHTML = 
                'MD5('+req.responseJS.q+') = ' +
                '"' + req.responseJS.md5 + '"<br> ';
            // Write debug information too (output become responseText).
            document.getElementById('debug').innerHTML = req.responseText;
        }
    }
    // Prepare request object (automatically choose GET or POST).
    req.open(null, 'frm_backend.php', true);
    // Send data to backend.
    req.send( { q: value } );
}
</script>

<!-- Please note that we must specify enctype to multipart/form-data! -->
<form method="post" id="f" enctype="multipart/form-data" onsubmit="return false">
    Text: <input type="text" name="txt">
    File: <input type="file" name="upl">
    <input type="button" value="Calculate MD5" 
     onclick="doLoad(document.getElementById('f'))">
</form>

<div id="result" style="border:1px solid #000; padding:2px">
    Structured results
</div>
<div id="debug" style="border:1px dashed red; padding:2px">
    Debug info
</div>

See that example in action: file frm_frontend.htm.

Caching and choosing the load method

JsHttpRequest library supports several possibilities, which are absent in XMLHttpRequest.

You can set caching of query results:

Listing 10: enable caching
// Enable caching of the query results
req.caching = true;

Now, if you call send() twice with the same parameters, no query to server will be made, but the result of first call will be returned. This is useful when you request static data (e.g. elements of drop-down lists of cities). Of course, you need to disable caching when you send some data, which is entered by user.

By default, JsHttpRequest tries to choose the optimal type of data loader itself, basing on what is supported by browser. For example, in Firefox almost always the internal XMLHttpRequest object will be used (but only if file upload is not used - in this case the loader based on FORM and IFRAME is used). But sometimes (e.g. when you write library testing scripts) you need to point which loader to use. You can do it by assigning the loader property of library object:

Listing 11: explicit loader specification
// Always use FORM loader.
req.loader = 'FORM';

At last, you can make the library to choose the optimal load method (GET or POST), if you point null value in the first argument of open(). This is not typical for XMLHttpRequest (usually "GET" or "POST" is used; null will let the library to choose the best method). Of course, if it's impossible to upload data by the specified method (or loader), the library will report about error.

The simplified interface

The examples, given above, show that the context of using JsHttpRequest library is almost always the same. At the beginning the library object is created, than you set it's properties and point the function, which will be called at the load end. Then you send the query to server.

For not to write the same code many times, JsHttpRequest object has a special query() method, which realizes the logic described above. The usage example (md5_frontend.htm file).

Listing 12: test/JsHttpRequest/md5_frontend.htm
<script src="../../lib/JsHttpRequest/JsHttpRequest.js"></script>
<script language="JavaScript">
    // Function is called when we need to calculate MD5.
    function calculate_md5() {
        JsHttpRequest.query(
            'md5_backend.php', // backend
            {
                // pass a text value 
                'str': document.getElementById("mystr").value,  
                // path a file to be uploaded
                'upl': document.getElementById("myupl")
            },
            // Function is called when an answer arrives. 
            function(result, errors) {
                // Write errors to the debug div.
                document.getElementById("debug").innerHTML = errors; 
                // Write the answer.
                if (result) {
                    document.getElementById("ans").innerHTML = 
                        'MD5("' + result["str"] + '") = ' + result["md5"];
                }
            },
            false  // do not disable caching
        );
    }
</script>

<!-- Please note that we must specify enctype to multipart/form-data! -->
<form method="post" enctype="multipart/form-data" onsubmit="return false">
    Enter a text: <input type="text" id="mystr"><br>
    ...or upload a file: <input type="file" id="myupl"><br>
    <input type="button" value="Calculate MD5" onclick="calculate_md5()">
</form>

<div id="ans" style="border:1px solid #000; padding:2px">
    Structured results
</div>
<div id="debug" style="border:1px dashed red; padding:2px">
    Debug info
</div>

As you can see, this example does exactly the same that smpl_frontend.htm, but its code is shorter and more clear.

Work together with the Prototype library

Prototype is a very popular tool to simplify a JavaScript programmer's work. It includes AJAX support and other features. Library JsHttpRequest could be used as its server part in PHP (after inclusion of a small compatibility module JsHttpRequest-prototype.js). Even so, all additional features of JsHttpRequest (cross-browser compatibility, file uploading support, work with national charsets etc.) are still available.

Listing 13: Use typical Prototype functions
<script src="../../lib/JsHttpRequest/JsHttpRequest.js"></script>
<script src="../../lib/JsHttpRequest/JsHttpRequest-prototype.js></script>
<script language="JavaScript">
new Ajax.Request('your_ajax_script.php', {
  method: 'get',
  parameters: {
    name: 'Dmitry',  
    file: $("my_upload_file")
  },
  onFailure: function(tr) {
    alert('Error code: ' + tr.status + '\n');
    alert(tr.responseText);
  },
  onSuccess: function(tr) {
    $("result").innerHTML = tr.responseJS.hello; 
    if (tr.responseText) alert(tr.responseText);
  }
});
</script>

Of course you could use any other capabilities of the prototype for AJAX (e.g. functions for dynamic data loading into a HTML element).

Errors interception in PHP-loader

The key difference of JsHttpRequest library from analogs is that it's very easy to debug systems created using this library. Traditionally AJAX applications debugging is known as quite difficult task. The main problem is that data is passed from PHP part to JavaScript part using fixed (and quite strict) format, and any violation of this format (e.g., because of loader error) causes an error in JavaScript code.

JsHttpRequest library makes all "dirty work" of converting data from PHP-specific to JavaScript-specific format. It intercepts PHP script output stream (including syntax and other kind of error messages) and accumulates it in special buffer for later passing it to responseText property of JavaScript object. You also phisycally cannot fill $_RESULT in the manner which may violate the format of data exchange: PHP arrays may be identically translated into JavaScript arrays.

As all error messages (e.g. call to undefined function) PHP prints directly to the output (like with echo), it's rather logical to take all the contents of the output as the debug text. If you remember, this text is available through req.responseText property, which is empty if the loader finished it's work correctly. Thanks for the wrapping mechanism, no errors, even the most serious ones, will generate the incorrect Javascript code. The error text will go to req.responseText.

You can make sure, that errors handling works, if you run the sample script md5_frontend.htm above and enter the "error" string in the text box. Probably you already noticed that an error message (call to undefined function) is written to the debug page block, and it caused the backend script to terminate immediately:

Listing 14: sample of intercepted PHP error message
Fatal error: Call to undefined function 
error_demonstration__make_a_mistake_calling_undefined_function()
in load.php on line 15

To make everything completely clear, here is the result of md5_backend.php work, as browser sees it (just after translating it by JsHttpRequest into format, "understandable" by JavaScript part). This result looks the same even if fatal error appeared.

Listing 15: sample of a result format
JsHttpRequest.dataReady({
  "id": "123", // this ID is passed from JavaScript frontend
  "js": { 
      "str": "query", 
      "md5": "MD5-code of entered string." 
  },
  "text": "Here comes debug messages and errors."
})

The priciple of JsHttpRequest work

Depending on browser capabilities and transmitting data singularity JsHttpRequest uses 4 different ways for data loading ("loaders").

  1. For IE5 and IE6 ActiveX-component Microsoft.XMLHTTP Msxml2.XMLHTTP is used. But to work proper, you must enable ActiveX in the browser settings. Even though this feature is enabled by default, many users that are familiar with IE holes turn it off by hands. For Mozilla, Firefox and Opera 8 (and more recent), and also for IE7, XMLHttpRequest class is used. It has a little disadvantage: it is denied by default to load data from anywhere except our current domain. That means you cannot load any data from foreign site even if it has JsHttpRequest installed.
  2. If previous methods are unavailable, library tries to use dynamically created <SCRIPT> tag. This method works in all browsers and allow to send requests to different domains which are not equal to current, but it has a large disadvantage: you can send data only using GET method (POST is not avaliable), that means data size is strictly limited.

    Lyrical deviation 
    If this method is used, behaviour of JsHttpRequest library differs from standard XMLHttpRequest behaviour in two aspects. First, library supports only asynchronous data loading (wokr without waiting: script is never "frozen" if response from the server is not arrived yet). Second, in case of connection failure while data loading you cannot detect and handle this. These limitations cannot be removed, but, fortunately, they do not almost limit the area of library usage.

  3. At last, if you need to upload some file to the server, the one avaliable algorythm is used: dynamically created <IFRAME> and <FORM> elements. The main problems of this method: when you change src attribute of <IFRAME>, you hear the characteristic click and the record to "browser history" is added, so Back button begins to work incorrectly. Fortunately modern browsers do not have this problem, but old browsers still have it.

Lyrical deviation 
If you are not satisfied by JsHttpRequest built-in algorythm of automatic loader choosing, you may explicitly specify whet loader and what method (GET or POST) to use. It could be done by assigning values "SCRIPT", "XML" or "FORM" to the property loader of library object and also specifying the method ("GET" or "POST") in first argument of open() function.

Data exchange protocol

Let's consider library working as example of most cross-browsed data loading method. I'm talking about the dynamic creation and appending the <SCRIPT> tag. You can set the src attribute, that is the address of the server data loading script (which is written, e.g. in PHP).

For dummies 

Obviously, the loaded script should print the correct JavaScript code. You cannot load the plain text directly by that method. Library fully cares about correctness of this JavaScript code.

Let's assume that when you press the button, the JavaScript-program inserts the following tag to the page (DHTML is used):

Listing 16: calling of a backend
<script language="JavaScript" src="load.php?room=303&JsHttpRequest=101-script"></script>

What will happen? The browser will immediately call to server with the following address:

Listing 17: URL of a requested backend
load.php?room=303&JsHttpRequest=101-script

As the result, load.php script will be run on server, it will recieve the QUERY_STRING params load.php?room=303&JsHttpRequest=101-script (of course, the arguments can be arbitrary). The script will finish work (for example, after it has requested data from the database) and it will print the following text as the result:

Listing 18: sample of a backend results
JsHttpRequest.dataReady({
  "id": "101", // this ID is passed from JavaScript frontend
  "js": [
    "Here is some data.",
    "It can have any structure...",
    { "test": "...and nesting" }
  ],
  "text": "And here just comes the debug text."
})

For dummies 

Attention! The format above is only a sample of one of used ways for data transferring. If your need more details, please read the document Protocol specifications.

So, the PHP-script load.php printed to his standard output the text, that is also a correct JavaScript-program. It will be used as the source of data with any structure.

Lyrical deviation 
M-mmm... "The program programming another program"... "the Source"... Definitely, "Matrix has you".

As the result, the JavaScript code, generated by a PHP-script load.php, will be executed by browser! As you can see, the dataReady() method of JsHttpRequest object is called, and the following is passed:

  1. The unique load identifier (to prevent confuse with different data, as the page can fetch for data from different sources at a time). This identifier is always fetched by backend-code from GET-parameter with name "JsHttpRequest" (remember QUERY_STRING from above example: load.php?room=303&JsHttpRequest=101-script) and presented as natural number. (After hyphen which trails this number used loader type is specified: "xml" or "script" or "form".)
  2. Arbitrary data, recieved by load.php, e.g. from DB.
  3. Some text, which can be used for debug purposes (for example, you can point the errors, which appeared in PHP-program, i.e. contents of standard script output stream).

So, the function JsHttpRequest.dataReady() cares about the loaded data delivery to the end-user, and realizing the caching of the identical requests (if permitted).

For dummies 

Dynamic <SCRIPT> tag generation has one very important advantage: when using that method, the browser history is left untouched, and you do not hear the click sound, which many browsers utter when they jump to another page. It's also required to point that there is a little bug in Firefox, in the result of which the Loading... text in the status bar doesn't disappear after loading the <SCRIPT>-component. However, this bug doesn't affect anything and, probably, will be fixed soon by the developers.

Library components list

The JsHttpRequest library consists of two parts, which work together:

  • JsHttpRequest.js, 14 K: JavaScript-code, deifining the object JsHttpRequest. This is so-called frontend of the system. You should attach it with the following code:

    Listing 19: library including in JavaScript
    <script language="JavaScript" src="JsHttpRequest/JsHttpRequest.js">
    </script>

  • JsHttpRequest.php, 16 K: PHP-code, where the functions for loader scripts programming are present. This is so-called backend of the system. You should include it to the very beginning by the operator:

    Listing 20: library including in PHP
    require_once "JsHttpRequest/JsHttpRequest.php";

For now, PHP is only supported as the language for backend scripts, but in the future it is possible to create backend-modules using different languages. (Nowadays there are experimental versions for Perl and C++, but they are still too experimental and cannot be included in the distribution.)

To shrink the size and speed up loading of the main frontend module of the library its JavaScript-code is compressed using an utility from Dojo Toolkit. (Here you can watch how does it look after the compression.)

Module-based library structure

Distribution archive of the library also has two additional directories: lib/JsHttpRequest/debug and lib/JsHttpRequest/mini.

  • Directory debug contains original source codes of the frontend library, including all comments, tabs and spaces. If you think you have found a bug in the library, please first try to reproduce it using these debug versions to localise a problem, and after that - write down to the forum.
  • Directory mini contains "shrinked" versions of the library. E.g. you could found a version with support of SCRIPT loading method only (JsHttpRequest-script.js), which is very useful if you load data only from "foreign" hosts. Its size is only 8K.

Encoding problems solution

When you form a query to loader, you might need to pass it strings that contain non-english letters. Of course, you cannot pass them directly in URL, but you first need to URL-encode them — convert every simbol of your native language alphabet to smth like %XX, where XX is code of symbol.

There is function escape() in JavaScript, which URL-encodes the string with data. Unfortunately, it returns the result only in Unicode form. E.g. string "" is represented as "%u043F%u0440%u043E%u0431%u0430". In PHP there are no functions that can decode such format (urldecode() isn't very good, as it doesn't support %uXXXX format). escape() function allows us to encode absolutely any symbol, a russian letter, the greek letter or even chinese hieroglyph.

Lyrical deviation 
To say honestly, in last versions of Javascript there is a function encodeURIComponent(), which can encode data without Unicode. But it is not supported, e.g. in Internet Explorer 5.0 so we cannot use this function because we need our library to be crossbrowser.

Fortunately, the popular iconv extension for PHP supports the converting data in various encodings, so it's not so hard to recode data from Unicode to the popular encoding which is used for language isn't very hard for backend library.

Lyrical deviation 
Because of numerous requests, beginning with version 3.0 JsHttpRequest library can work without iconv, if the chosen encoding is windows-1251, koi8-r or UTF-8. Functions for converting data to that encodings are intergated in the library.

So, you can use send() method of JsHttpRequest object and not to worry about data encodings. You do not need to recode anything by hand neither in server nor in client code: the library takes all that work upon itself.

For dummies 

I will repeat again: if you want to use JsHttpRequest library with encodings that differ from windows-1251, koi8-r and UTF-8. (e.g. with UTF-8), you must have PHP extension iconv installed on the server. Most of popular hostings have that module, but if you will see that it's absent, it won't be very difficult for hosting to install that module.

Summary

Let's sum up that large article. At first, I will list the links for all the files used above.

Sample of library usage:

JsHttpRequest library is actively used at forum.dklab.ru forum. The following features are realized:

  • "Live search": on every page there is a text field where you can search the forum without reloading the page.
  • "Live search" in "new topic" form: the same as above, but it starts working when the message header is typed.
  • "Live preview": there is a small icon for every topic on forum, so you can see the first message of every topic.
  • "Live karma": users can change each other's karma (rating), without reloading a page.

Internet articles:

Author thanks Yuri Nasretdinov for English translation of this article and valuable remarks regarding the library.





Dmitry Koterov, Dk lab. ©1999-2017
GZip
Add to Del.icio.us   Digg It!   Reddit