
/* AjaxDataCommand class scripts --- */ 

var AjaxDataCommand_IsRedirecting = false; // This variable specify whether current page will be redirected soon.


function AjaxDataCommand(objName, antiDOSTimeout){
    if(!objName){
        alert('JS-class AjaxDataCommand initialization error! Parameter \'objName\' cannot be empty!');
        return;
    }
    // Name of this object instance (as string). Using for codegeneration for setTimeout(...).
    var _objName = objName;

    // Properties
    var _ErrMess = null;
    this.get_ErrorMessage = function(){ return _ErrMess; }
    this.set_ErrorMessage = function(val){ _ErrMess = val; }

    var _AsyncMode = true;
    this.get_AsyncMode = function(){ return _AsyncMode; }
    this.set_AsyncMode = function(val){ _AsyncMode = val; }

    var _SuppressEvents = false;
    this.get_SuppressEvents = function(){ return _SuppressEvents; }
    this.set_SuppressEvents = function(val){ _SuppressEvents = val; }

    this.EnforceNoCache = true;

    // Validation
    this.ValidateXmlResponse = function(xhttp){
        return _ValidateXmlResponse(xhttp);
    }

    var _customErrorHandler =null;

    var _ValidateXmlResponse = function(xhttp){
        sw_Debug('Ajax Response: \n' + xhttp.responseText.replace(/></g,'>\n<'));

        var isValid = false; // returned value

        var errType = '';
        var errorHandled = false;
        var mess = '';
        var fullMess = '';

        var responseXML = xhttp.responseXML;
        if(responseXML.documentElement){
            var xmlRoot = responseXML.documentElement;
            var errNode =  sw_Xml_GetChildNode(xmlRoot, 'AjaxError');
            if(!errNode){
                isValid = true;
            }else{
                isValid = false;
                errType =  sw_GetText(errNode, 'Type');
                mess =  sw_GetText(errNode, 'Message');
                var stackTrace =  sw_GetText(errNode, 'StackTrace');
                _customErrorHandler = _this.CustomErrorH[errType];
                if(_customErrorHandler){ 
                    // Custom error handler
                    //_customErrorHandler(); // CustomError handler must invoke after common errors handler!
                    errorHandled = true;
                }else{ 
                    // No specific error handler found
                    if(mess){
                        fullMess += 'ERROR MESSAGE:\n     '+mess;
                    }
                    if(stackTrace){
                        fullMess += '\n\nERROR DETAILS:\n     '+stackTrace;
                    }
                }
                //sw_Debug(fullMess);
            }
        }else if(xhttp.status!=200){
            isValid = false;
            mess = xhttp.statusText;
            fullMess = 'AjaxDataCommand XmlHttpRequest error:';
            if(xhttp.status==400){ mess = 'Unable to connect web server!'; }
            fullMess += '  ' + mess;
            fullMess += '\n\n'+'Request status code:  ' + xhttp.status;
            fullMess += '\n\n'+'Request status:  ' + xhttp.statusText;
            fullMess += '\n\n'+'Request message:\n  '+xhttp.responseText.replace('<h1>', '').replace('</h1>', '');
            //sw_Debug(fullMess);
        }else{
            isValid = false;
            mess = 'Invalid format of Ajax-response (not XML format). Response doesn\'t contains documentElement!';
            fullMess = mess + '\n\n';
            fullMess += 'Ajax-response text:\n\n' + xhttp.responseText;
        }
        
        _ErrMess = mess;
        // May be we should ignore 'Unknown' errors?
        // Such errors can occur while multiple XmlHttpRequest was started at very short time. 
        // Ignoring this error we don't harm functionality!

        // When no error handling code exists we must somehow show error message!
        if(!isValid 
           && !_this.OnErrorH 
           && !errorHandled)
        { 
            _this.ShErr(); 
        }
        _ErrMess = mess;
        
        return isValid;
    }


    // Auxiliary methods ---
    this.ShErr = function(){
        var mess = this.get_ErrorMessage();
        if(mess)
            alert(mess);
    }

    // Variables ---
    var _this = this;
    this.xmlHttp = null;
    
    // 'Protected' fields ---
    this.RequestMethod = 'GET';
    this.SessionExpiredRedirectUrl = null;
    this.SessionExpiredRedirectTarget = '_self';
    this.SessionExpiredMessage = 'Session is expired!';

    // Delegates ---
    this.ReadOutParameters = function(node){ }
    this.ReadProps = function(node){ alert('ReadProps must be initialized in ClientInstanceScripts'); }
    this.BuildQuery = function(){ alert('BuildQuery must be initialized in ClientInstanceScripts'); }
    this.OnErrorH = null;
    this.OnSuccessH = null;
    this.OnExecutedH = null;
    this.OnUpdatedH = null;    

    // Custom error handling
    this.CustomErrorH = new Object(); // Custom error handlers collection
    this.CustomErrorH['SWsoft.Ajax.AjaxCore.SessionExpiredException'] = function(){
        if( !AjaxDataCommand_IsRedirecting ){
            var url = _this.SessionExpiredRedirectUrl;
            var target = _this.SessionExpiredRedirectTarget;
            if(url){
                AjaxDataCommand_IsRedirecting = true;
                alert(_this.SessionExpiredMessage);
                sw_Navigate(url, target);
            }
        }
    }

    // Response handling ---
    var _TimeStartRequest = new Date(0); //Performance counter
    var _TimeResponseReceived = new Date(); //Performance counter
    var _TimeProcessingComplete = new Date(); //Performance counter

    this.xmlResponseHandler = function(){
        if( sw_IsAsyncRequestDone(_this.xmlHttp) ){
            var suppEvents = _SuppressEvents;
            _TimeResponseReceived = new Date(); //Performance counter
            var responseXML = _this.xmlHttp.responseXML;
            var validationResult = _ValidateXmlResponse(_this.xmlHttp);

            if( !validationResult ){ 
                // Error must not be suppressed!
                if(_this.OnErrorH!=null){ 
                    _this.OnErrorH(); 
                }
                if(_customErrorHandler!=null){
                    // CustomError handler must invoke after common errors handler!
                    _customErrorHandler();
                }
            }else{
                var nodes = responseXML.documentElement.childNodes;
                _this.ProcessNodes(nodes); // ProcessNodes Must be implemented in ClientDerivedClassScripts
                if(!suppEvents && _this.OnSuccessH!=null){ _this.OnSuccessH(); }
            }
            if(!suppEvents && _this.OnExecutedH!=null){ _this.OnExecutedH(); }
            if(!suppEvents && _this.OnUpdatedH!=null){ _this.OnUpdatedH(); }

            _isBusy = false;

            _TryDoRequest();
            
            return validationResult;
        }
    }

    // Perform request ---
    this.DoRequest = function(){
        _AjaxQuery = this.BuildQuery();
        
        // New Strategy: Abort previous request and start new request (when AntiDOS timeout is expired).
        this.AbortRequest();
        if(!_TimeoutOK()){
            _needRetry = true;
            _TryDoRequest();
        }else{
            this.DoRequestNow();
        }

        // Old Strategy: Wait previous request done then start new request (when AntiDOS timeout is expired).
        //if(_isBusy){
        //    _needRetry = true;
        //}else if(!_isBusy && !_TimeoutOK()){
        //    _needRetry = true;
        //    _TryDoRequest();
        //}else{
        //    this.DoRequestNow();
        //}
    }

    this.DoRequestNow = function(){
        //_this.xmlHttp = null;
        _isBusy = true;
        _needRetry = false;
        _TimeStartRequest = new Date(); //Performance counter
        _this.xmlHttp = sw_CreateXmlHttpRequest();
        if(_AsyncMode){
            sw_DoAsyncRequest(_this.xmlHttp, _this.xmlResponseHandler, _AjaxQuery, '/AjaxHandler.ashx', _this.RequestMethod, true, _this.EnforceNoCache)
        }else{
            sw_DoAsyncRequest(_this.xmlHttp, null, _AjaxQuery, '/AjaxHandler.ashx', _this.RequestMethod, false, _this.EnforceNoCache)
            _this.xmlResponseHandler();
        }
    }

    this.AbortRequest = function(){
        if(!_isBusy | !_this.xmlHttp)
            return;
        try{
            var oldReq = _this.xmlHttp;
            _isBusy = false;
            _this.xmlHttp = null;
            oldReq.onreadystatechange = function(){};
            oldReq.abort();
        }catch(ex){
            //do nothing
        }
    }

    // DOS-defence ---
    var _antiDOSTimeout = (antiDOSTimeout==null ? 2000 : antiDOSTimeout);
    var _isBusy = false; // Wait response from server
    var _needRetry = false;
    var _AjaxQuery = '';

    // Show whether _antiDOSTimeout was complete.
    var _TimeoutOK = function(){
        if(_antiDOSTimeout==0)
            return true;
        var _now = new Date();
        var timeSpan = (_now.valueOf() - _TimeStartRequest.valueOf());
        return  (timeSpan > _antiDOSTimeout);
    }

    // Do new request if it is nesessary
    var _TryDoRequest = function(){
        if(_needRetry==true){
            if(_TimeoutOK()){
                _needRetry = false;
                _this.DoRequestNow();
            }else if(window.setTimeout){
                var _now = new Date();
                var timeSpan = (_now.valueOf() - _TimeStartRequest.valueOf());
                var timeout = _antiDOSTimeout - timeSpan + 1;
                window.setTimeout( objName+'.DoRequest();', timeout)
            }
        }
    }

    
    // Delegates
    this.OnExecutingH = null;

    // Main methods ---
    this.ClientExecute = function(){
        if(_this.OnExecutingH && typeof(_this.OnExecutingH)=='function')
           _this.OnExecutingH(); 
        _this.DoRequest();
    }

    this.ProcessNodes = function(nodes){
        for (var i=0; i<nodes.length; i++){
            var node = nodes[i];
            switch(node.nodeName){
                case 'Parameters':{
                    _this.ReadOutParameters(node);
                    break;
                }
                case 'Extended':{
                    _this.ReadProps(node);
                    break;
                }
            }
        }
        _TimeProcessingComplete = new Date(); //Performance counter

        // Performance counting
        var str = '';
        str += 'AjaxDataCommand overall execution time = '+(_TimeProcessingComplete.valueOf()-_TimeStartRequest.valueOf()) + ' ms.\n';
        str += 'XmlHttpRequest request time = '+(_TimeResponseReceived.valueOf()-_TimeStartRequest.valueOf()) + ' ms.\n';
        str += 'Xml parsing time = '+(_TimeProcessingComplete.valueOf()-_TimeResponseReceived.valueOf()) + ' ms.\n';
        sw_Trace(str);
    }


    
}
        

/* --- */