﻿/*=============================================================================================================================================
                                                                JUNE.Web    v 2.0                                                              
=============================================================================================================================================*/

/*==================================================================================
    Copyright (c) 2008 Costin Trifan                        http://www.june-js.com/ 
                                                                                    
    Permission is hereby granted, free of charge, to any person obtaining a copy    
    of this software and associated documentation files (the "Software"), to deal   
    in the Software without restriction, including without limitation the rights    
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       
    copies of the Software, and to permit persons to whom the Software is           
    furnished to do so, subject to the following conditions:                        
                                                                                    
    The above copyright notice and this permission notice shall be included in      
    all copies or substantial portions of the Software.                             
                                                                                    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN       
    THE SOFTWARE.                                                                   
                                                                                    
==================================================================================*/

/*==================================================================
        Description:                                                
            The JUNE.Web namespace contains methods to create       
            synchronous or asynchronous AJAX calls.                 
        version 2.0                                                 
        Package: JUNE FRAMEWORK                                     
        Last Edited: Nov 05, 2008                                   
==================================================================*/


JUNE.Web = (function()
{
///<summary>The JUNE.Web Namespace contains methods to create AJAX requests.</summary>

    /*== CONSTANTS ::  XMLHttpRequest readyState ==*/
    var UNINITIALIZED = 0,
        LOADING = 1,
        LOADED = 2,
        INTERACTIVE = 3,
        COMPLETE = 4,
        /*== CONSTANTS ==  XMLHttpRequest status ==*/
        RESPONSE_OK = 200 || 304,
        NOT_MODIFIED = 304,
        BAD_REQUEST = 400,
        ACCESS_DENIED = 401,
        FORBIDDEN = 403,
        FILE_NOT_FOUND = 404;


/*== ERROR METHODS ==*/
var abortRequest = function(request) {
///<summary>Interrupts the active AJAX request.</summary>
///<param name="request" type="String" optional="false">(Required). The reference to the request object.</param>
    request.abort();
    request = null;
};
var onRequestTimeout = function(request)
{
///<summary>Displays an error message if the server times out while making the request.</summary>
    abortRequest(request);
    throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestTimeout", "The server timed out while making the request."));
};
var onRequestFailure = function(request)
{
///<summary>Callback. Displays an error message depending on which error the request encounters.</summary>
///<param name="request" type="Object" optional="false">(Required). The reference to the request object.</param>
    if (request.status == BAD_REQUEST)
    {
        abortRequest(request);
        throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestFailure", "Server error: 400 - Bad Request.\n The request had a bad syntax or was inherently impossible to be satisfied."));
    }
    else if (request.status == ACCESS_DENIED)
    {
        abortRequest(request);
        throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestFailure", "Server error: 401 - Access Denied.\n The client should retry the request with a suitable Authorization header"));
    }
    else if (request.status == FORBIDDEN)
    {
        abortRequest(request);
        throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestFailure", "Server error: 403 - Forbidden.\n The server understood the request, but is refusing to fulfill it."));
    }
    else if (request.status == FILE_NOT_FOUND)
    {
        abortRequest(request);
        throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestFailure", "Server error: 404 - File Not Found.\n The requested file was not found."));
    }
    else {
        abortRequest(request);
        throw new Error(JUNE.Exception.generalError("JUNE.Web.onRequestFailure", "Message returned, but with error status. \n" + "Server error: " + request.status));
    }
};
var xmlHttpNotImplemented = function()
{
///<summary>Displays an error message if the xmlHttpRequest object is not supported by the client browser.</summary>
    throw new Error(JUNE.Exception.generalError("JUNE.Web.xmlHttpNotImplemented", "Your browser does not supports the XMLHttpRequest Object.\nPlease upgrade to a modern browser!"));
};



/*== FACTORY METHODS ==*/

var createXmlHttp = function()
{
///<summary>Creates an XMLHttpRequest object.</summary>
///<returns type="Object" />
    try { return new XMLHttpRequest(); } catch(ex){}
    try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch(ex){}
    try { return new ActiveXObject("Microsoft.XMLHTTP"); }catch(ex){ return xmlHttpNotImplemented(); }

    return null;
};



/*== PRIVATE METHODS ==*/

var getData = function(fileLocation, onCompleted, onFailure, requestTimeout, getFromCache, isAsync)
{
///<summary>Requests a document asynchronously.</summary>
///<param name="fileLocation" type="String" optional="false">(Required). The location of the file to request.</param>
///<param name="onCompleted" type="String" optional="false">(Required). The callback to handle the response.</param>
///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped.</param>
///<param name="getFromCache"type="Boolean" optional="true">(Optional). Whether the result should be requested from the browser cache or not. Default: false.</param>
///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default: async</param>

    var x_request = createXmlHttp();

    if (requestTimeout == null) {requestTimeout = 5;}
    if (getFromCache == null) {getFromCache = false;}
    if (isAsync == null) { isAsync = true; }

    if (x_request)
    {
        if (isAsync)
        {
            var _timer = setTimeout(function(){return onRequestTimeout(x_request);}, requestTimeout*1000 );

                x_request.onreadystatechange = function() 
                {
                    if (x_request.readyState == COMPLETE)
                    {
                        clearTimeout(_timer);
                        if (x_request.status == RESPONSE_OK)
                        {
                            return onCompleted(x_request);
                        }
                        else { return (onFailure) ? onFailure(x_request) : onRequestFailure(x_request); }
                    }
                };

            if (getFromCache) {
                x_request.open("GET", fileLocation, true);
                x_request.send(null);
            }
            else {
                x_request.open("GET", fileLocation, true);
                x_request.setRequestHeader("If-Modified-Since", "Mon, 05 Jun 1978 00:00:00 GMT");
                x_request.send(null);
            }
        }
        else
        {
            requestTimeout = null;
            onFailure = null;

            try {
                if (getFromCache)
                {
                    x_request.open("GET", fileLocation, false);
                    x_request.send(null);
                    return onCompleted(x_request);
                }
                else
                {
                    x_request.open("GET", fileLocation, false);
                    x_request.setRequestHeader("If-Modified-Since", "Mon, 05 Jun 1978 00:00:00 GMT");
                    x_request.send(null);
                    return onCompleted(x_request);
                }
            }
            catch(ex) { throw new Error(onRequestFailure(x_request)); }
        }
    }
};

var requestHeaders = function(fileLocation, headerLabel, onCompleted, onFailure, isAsync, requestTimeout)
{
///<summary>Requests the headers of a document synchronously or asynchronously.</summary>
///<param name="fileLocation" type="String" optional="false">(Required). The location of the file to request.</param>
///<param name="headerLabel" type="String" optional="true">(Optional). The header label to be requested.</param>
///<param name="onCompleted" type="Function" optional="false">(Required). The callback to handle the response.</param>
///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default: async</param>
///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped.</param>

    var x_request = createXmlHttp();

    if (isAsync == null) {isAsync = true;}
    if (requestTimeout == null) {requestTimeout = 5;}

    if (x_request)
    {
        x_request.open("HEAD", fileLocation, isAsync);

        if (isAsync)
        {
            var _timer = setTimeout(function(){return onRequestTimeout(x_request);}, requestTimeout*1000 );

                x_request.onreadystatechange = function()
                {
                    if (x_request.readyState == COMPLETE)
                    {
                        clearTimeout(_timer);
                        if (x_request.status == RESPONSE_OK)
                        {
                            if (headerLabel) {
                                var response = x_request.getResponseHeader(headerLabel);
                                return onCompleted(response);
                            }
                            else {
                                response = x_request.getAllResponseHeaders();
                                return onCompleted(response);
                            }
                        }
                        else { return (onFailure) ? onFailure(x_request) : onRequestFailure(x_request); }
                    }
                };
            x_request.send(null);
        }
        else
        {
            requestTimeout = null;
            onFailure = null;

            x_request.send(null);
            var response = '';

            try {
                if (headerLabel) {
                    response = x_request.getResponseHeader(headerLabel);
                    return onCompleted(response);
                }
                else {
                    response = x_request.getAllResponseHeaders();
                    return onCompleted(response);
                }
            }
            catch(ex) { throw new Error(onRequestFailure(x_request)); }
        }
    }
};

var postData = function(fileLocation, postVars, onCompleted, onFailure, isAsync, requestTimeout)
{
///<summary>Sends a document to server synchronously or asynchronously.</summary>
///<param name="fileLocation" type="String" optional="false">(Required). The page location where to send the data.</param>
///<param name="postVars" type="String" optional="false">(Required). The data to send.</param>
///<param name="onCompleted" type="Function" optional="false">(Optional). The callback to handle the response.</param>
///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default: async</param>
///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped.</param>

    var x_request = createXmlHttp();

    if (isAsync == null) {isAsync = true;}
    if (requestTimeout == null) {requestTimeout = 5;}

    var content = fileLocation+postVars;

    if (x_request)
    {
        x_request.open("POST", content, isAsync);

        if (isAsync)
        {
            var _timer = setTimeout(function(){return onRequestTimeout(x_request);}, requestTimeout*1000 );

                x_request.onreadystatechange = function()
                {
                    if (x_request.readyState == COMPLETE)
                    {
                        clearTimeout(_timer);
                        if (x_request.status == RESPONSE_OK) {
                            return onCompleted(x_request);
                        }
                        else { return (onFailure) ? onFailure(x_request) : onRequestFailure(x_request); }
                    }
                };

            postVars += (new Date()).getTime();
            x_request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            x_request.setRequestHeader("Content-Length", postVars.length);
            x_request.send(postVars);
        }
        else
        {
            requestTimeout = null;
            onFailure = null;

            postVars += (new Date()).getTime();
            x_request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            x_request.setRequestHeader("Content-Length", postVars.length);

            try {
                x_request.send(postVars);
                return onCompleted(x_request);
            }
            catch(ex) { throw new Error(onRequestFailure(x_request)); }
        }
    }
};


/*== PUBLIC AJAX METHODS ==*/

    return {
        createXmlHttpObject : function()
        {
        ///<summary>Creates a new XMLHttpRequest object.</summary>
        ///<returns type="Object" />
            return createXmlHttp();
        },

        getData: function(fileLocation, onCompleted, onFailure, requestTimeout, getFromCache, isAsync)
        {
        ///<summary>Requests a document asynchronously.</summary>
        ///<param name="fileLocation" type="String" optional="false">(Required). The location of the file to request.</param>
        ///<param name="onCompleted" type="String" optional="false">(Required). The callback to handle the response.</param>
        ///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
        ///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped. Default timeout: 5.</param>
        ///<param name="getFromCache"type="Boolean" optional="true">(Optional). Whether the result should be requested from the browser cache or not. Default value: false.</param>
        ///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default value: true.</param>
        ///<returns type="String;XML" />

            return getData(fileLocation, onCompleted, onFailure, requestTimeout, getFromCache, isAsync);
        },

        getHeaders: function(fileLocation, headerLabel, onCompleted, onFailure, isAsync, requestTimeout)
        {
        ///<summary>Requests the headers of a document synchronously or asynchronously.</summary>
        ///<param name="fileLocation" type="String" optional="false">(Required). The location of the file to request.</param>
        ///<param name="headerLabel" type="String" optional="true">(Optional). The header label to be requested.</param>
        ///<param name="onCompleted" type="Function" optional="false">(Required). The callback to handle the response.</param>
        ///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
        ///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default value: true.</param>
        ///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped. Default timeout: 5.</param>
        ///<returns type="String" />

            return requestHeaders(fileLocation, headerLabel, onCompleted, onFailure, isAsync, requestTimeout);
        },

        postData: function(fileLocation, postVars, onCompleted, onFailure, isAsync, requestTimeout)
        {
        ///<summary>Sends a document to server synchronously or asynchronously.</summary>
        ///<param name="fileLocation" type="String" optional="false">(Required). The page location where to send the data.</param>
        ///<param name="postVars" type="String" optional="false">(Required). The data to send.</param>
        ///<param name="onCompleted" type="Function" optional="false">(Optional). The callback to handle the response.</param>
        ///<param name="onFailure" type="Function" optional="true">(Optional). The callback to handle the error response.</param>
        ///<param name="isAsync" type="Boolean" optional="true">(Optional). Whether the request should be made asynchronously or synchronously. Default value: true.</param>
        ///<param name="requestTimeout" type="Number" optional="true">(Optional). The timespan, in seconds, before the request will be dropped. Default timeout: 5</param>
        ///<returns type="String" />

            return postData(fileLocation, postVars, onCompleted, onFailure, isAsync, requestTimeout);
        }
    } /* End return */
})();