var Ajax = new function()
{
    this.sendRequest = function(uri, parameters, onCompleteCallback)
    {
        try
        {
            // try initialize request object
            initializeRequest();

            // if request is not initialized then abort sending
            if (request == null)
                return;

            // store onComplete callback
            onComplete = onCompleteCallback;

            // prepare the message which will be sent to the server
            var message = '';

            for (var name in parameters)
            {
                if (message != '')
                    message += '&';

                if (parameters[name].constructor.toString().indexOf('Array') != -1)
                {
                    // array parameter
                    for (var i = 0; i < parameters[name].length; i++)
                    {
                        if (message != '')
                            message += '&';

                        message += encodeURIComponent(name + '[]') +
                                   '=' +
                                   encodeURIComponent(parameters[name][i]);
                    }
                }
                else
                {
                    // regular parameter
                    message += encodeURIComponent(name) +
                               '=' +
                               encodeURIComponent(parameters[name]);
                }
            }

            // send asynchronous HTTP request using POST method
            request.open('POST', uri, true);
            request.onreadystatechange = onReadyStateChange;
            request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            request.setRequestHeader('Content-length', message.length);
            request.setRequestHeader('Connection', 'close');
            request.send(message);
        }
        catch (exception)
        {
            // in case of exception display error message
            alert('Error during sending your request.');
            return;
        }
    }

    this.uploadFile = function(uri, fileId, onCompleteCallback)
    {
        try
        {
            // get file object
            var fileObject = document.getElementById(fileId);

            // set file objects's additional attributes
            fileObject.name = fileId;

            // create a div containing a form
            // and move the file object to the form
            var formDivObject;
            formDivObject = document.createElement('div');
            formDivObject.id = fileId + '_form_div';
            formDivObject.innerHTML = '<form id="' + fileId + '_form" ' +
                                             'action="' + uri + '" ' +
                                             'method="post" ' +
                                             'enctype="multipart/form-data" ' +
                                             'target="' + fileId + '_iframe" ' +
                                             'style="margin: 0; padding: 0"' +
                                        '>';
            fileObject.parentNode.insertBefore(formDivObject, fileObject);
            formDivObject.parentNode.removeChild(fileObject);
            document.getElementById(fileId + '_form').appendChild(fileObject);

            // create a div containing an iframe (form's target)
            // and set onComplete callback for the iframe
            var iframeDivObject;
            iframeDivObject = document.createElement('div');
            iframeDivObject.id = fileId + '_iframe_div';
            iframeDivObject.style.display = 'none';
            iframeDivObject.innerHTML = '<iframe id="' + fileId + '_iframe" ' +
                                                 'name="' + fileId + '_iframe" ' +
                                                 'src="about:blank" ' +
                                                 'onload="Ajax.uploadComplete(\'' + fileId + '\');"' +
                                         '>' +
                                         '</iframe>';
            document.body.appendChild(iframeDivObject);
            document.getElementById(fileId + '_iframe').onComplete = onCompleteCallback;

            // submit the form
            document.getElementById(fileId + "_form").submit();
        }
        catch (exception)
        {
            // in case of exception display error message
            alert('Error during sending your request.');
            return;
        }
    }

    function initializeRequest()
    {
        try
        {
            // if request is already initialized then do not recreate it
            if (request != null)
                return;

            // create XMLHttpRequest object (Opera 8.0+, Firefox, Safari)
            request = new XMLHttpRequest();
        }
        catch (exception)
        {
            // create XMLHttpRequest object (Internet Explorer)
            try
            {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (exception)
            {
                try
                {
                    request = new ActiveXObject('Microsoft.XMLHTTP');
                }
                catch (exception)
                {
                    alert('Your browser does not support AJAX.');
                    return;
                }
            }
        }
    }

    function onReadyStateChange()
    {
        try
        {
            // when response downloading is finished (readyState == 4)
            if (request.readyState == 4)
            {
                // if an error occured (status != 200, responseText == null)
                // then throw an exception
                if (request.status != 200 || request.responseText == null)
                    throw 'Invalid response.';

                // call onComplete callback
                if (onComplete != null)
                    onComplete(decodeURIComponent(request.responseText));
            }
        }
        catch (exception)
        {
            // in case of exception display error message
            alert('Error during processing your request.');
            return;
        }
    }

    this.uploadComplete = function(fileId)
    {
        try
        {
            // get iframe object
            var iframeObject = document.getElementById(fileId + '_iframe');

            // if iframe does not exists then return
            if (iframeObject == null)
                return;

            // get iframe's content document
            var contentDocument;

            if (iframeObject.contentDocument != null)
                contentDocument = iframeObject.contentDocument;
            else if (iframeObject.contentWindow != null)
                contentDocument = iframeObject.contentWindow.document;
            else
                contentDocument = window.frames[fileId + '_iframe'].document;

            // if iframe contains 'about:blank' then return
            if (contentDocument.location.href == 'about:blank')
                return;

            // get the response text
            var responseText = contentDocument.body.innerHTML;

            // get onComplete callback
            var onComplete = iframeObject.onComplete;

            // remove the div containing the iframe
            iframeObject.src = 'about:blank';
            document.body.removeChild(document.getElementById(fileId + '_iframe_div'));

            // get file object
            var fileObject = document.getElementById(fileId);

            // remove file object's additional attributes
            fileObject.name = null;

            // move the file object outside the form
            // and remove the div containing the form
            var formDivObject = document.getElementById(fileId + '_form_div');
            var formObject = document.getElementById(fileId + '_form');
            formObject.removeChild(fileObject);
            formDivObject.parentNode.insertBefore(fileObject, formDivObject);
            fileObject.parentNode.removeChild(formDivObject);

            // call onComplete callback
            if (onComplete != null)
                onComplete(decodeURIComponent(responseText));
        }
        catch (exception)
        {
            // in case of exception display error message
            alert('Error during processing your request.');
            return;
        }
    }

    var request;
    var onComplete;
}