var NO_SESSION_EXCEPTION = 100; var MUST_LOGIN_EXCEPTION = 101; var ACCESS_DENIED_EXCEPTION = 102; var DATAFEEDER_EXPIRED_EXCEPTION = 103; var CONTAINER_IS_UNKNOWN = 104; var AR_LOGLEVEL = 0; var AXHRManager = Class.create({ initialize: function() { var oTT = this.GetTransport(); if (oTT) delete oTT; // delete test object AXHRManager.prototype.aRequests_ = new Array(); // TODO: Remove this ARRAY!!!! AXHRManager.prototype.nReqNum_ = 0; }, GetTransport: function() { var oTransport = null; // Try IE first to workaround the MSXML versions compatibility error if (Prototype.Browser.IE) { msVersions = ["Microsoft.XMLHTTP", "Msxml2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; for (var i = 0; i < msVersions.length; i++) { try { oTransport = new ActiveXObject(msVersions[i]); if (oTransport) return oTransport; } catch(e) { } } } // shiny browsers try { oTransport = new XMLHttpRequest(); if (oTransport) return oTransport; } catch (e) { oTransport = null; } throw new Error("AJAX is not supported by your browser, sorry!"); }, // Must be called on page destruction! // This method is a very important feature for IE7: if some requests // are not killed, IE will hang due to maximum parallel requests // limit is reached (2 for IE7, 6 for Fx3) DestroyRequests: function() { var nDCount = 0; for (var i = 0; i < AXHRManager.prototype.nReqNum_+1; ++i) { if (AXHRManager.prototype.aRequests_[i]) { AXHRManager.prototype.aRequests_[i].Kill(); nDCount++; } } //alert("Destroyed requests: "+nDCount); } }); var AjaxRequest = Class.create({ initialize: function(sMethod, sURL, sParameters, oOptions, bAsync) { if (typeof bAsync == "undefined") bAsync = true; this.sMethod_ = sMethod.toUpperCase(); if (this.sMethod_ != "GET" && this.sMethod_ != "POST" && this.sMethod_ != "POSTMULTIPART") { throw new Error("AjaxRequest(): Invalid method (proper values are 'GET', 'POST' and 'POSTMULTIPART')! "); } this.sURL_ = sURL; this.sParameters_ = sParameters; this.sExceptionText_ = ""; if (AR_LOGLEVEL) { LogL("$$ " + this.sMethod_ + " " + this.sURL_ + " $$"); } this.bAsync_ = bAsync; this.oTransport_ = GXHRManager.GetTransport(); if (!this.oTransport_) throw new Error("AjaxRequest: XHR creation failed! "); if (oOptions) { if (oOptions.onException) this.onException_ = oOptions.onException.bind(oOptions.thisObject); if (oOptions.onComplete) this.onComplete_ = oOptions.onComplete.bind(oOptions.thisObject); if (oOptions.onSuccess) this.onSuccess_ = oOptions.onSuccess.bind(oOptions.thisObject); if (oOptions.timeout) this.timeout = oOptions.timeout; } // request counting var nReqNum = AXHRManager.prototype.nReqNum_; this.nReqNum_ = nReqNum; AXHRManager.prototype.nReqNum_++; AXHRManager.prototype.aRequests_[nReqNum] = this; try { var m = this.sMethod_; if (m == 'POSTMULTIPART') m = 'POST'; this.oTransport_.open(m, this.makeURL(), this.bAsync_); } catch (exc) { this.raiseError("Error while opening a transport (method: '"+this.sMethod_+"', url: '"+ this.makeURL()+"', async: '"+this.bAsync_+"'). Message is ' "+exc.message+"' "); } if (this.oTransport_.readyState == 0) { LogE("AJAX transport opening failed (method: '"+this.sMethod_+"', url: '"+ this.makeURL()+"', async: '"+this.bAsync_+"'). "); return; } if (this.bAsync_) this.oTransport_.onreadystatechange = this.RequestCallback.bind(this); if (this.sMethod_ == "POST") { if (typeof(sParameters) == 'undefined' || sParameters === null) sParameters = ""; this.oTransport_.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); if (!Prototype.Browser.WebKit) { this.oTransport_.setRequestHeader("Content-length", sParameters.length); this.oTransport_.setRequestHeader("Connection", "close"); } if (oOptions.headers) { for (var key in oOptions.headers) { var val = oOptions.headers[key]; if (typeof(val) == "string") { this.oTransport_.setRequestHeader(key, val); } } } try { this.oTransport_.send(sParameters); } catch (exc) { this.raiseError("Web server is not available. Please retry your request later. "); return; } } else if (this.sMethod_ == "POSTMULTIPART") { this.postMultipart(); } else { // TODO: work out this !!! overrideMimeType for text/xsl (!!!) if (!this.bAsync_ && this.oTransport_.overrideMimeType) this.oTransport_.overrideMimeType('text/xml'); try { this.oTransport_.send(null); } catch (exc) { this.raiseError("AJAX GET Error: Send failed with message '"+ exc.message + "'. "); return; } } //LogL("AjaxRequest: Started "+this.sURL_); // in case of sync requests, set state flags and get data if (!this.bAsync_) { this.SetMessage("Completed", true); // drop from active requests AXHRManager.prototype.aRequests_[this.nReqNum_] = undefined; if (this.onComplete_) this.onComplete_(this); this.CompleteRequest(); } else { // async request if(!this.timeout) this.timeout=30; this.execTimer=this.timeIsOut.bind(this).delay(this.timeout); } }, timeIsOut: function() { this.execTimer=undefined; this.Kill(); this.raiseError("`Timeout exceeded. Please retry. `"+"\r\n"+"`Technical details: `method - '"+this.sMethod_+"', url - '"+ this.makeURL()+"', async - '"+this.bAsync_+"'. "); }, postMultipart: function() { if (!this.sParameters_) return; var boundary = ("" +Math.random() + Math.random() + Math.random() + Math.random()).replace(/0\./g, ''); this.oTransport_.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary); this.oTransport_.setRequestHeader("Connection", "close") boundary = "--" + boundary; var request = ""; for (var i = 0; i < this.sParameters_.length; ++i) { request += boundary + "\r\n"; for (var key in this.sParameters_[i]) { if (key == "data") continue; var v = this.sParameters_[i][key]; if (!v) continue; request += key + ': ' + v + "\r\n"; } request += "\r\n" + this.sParameters_[i].data + "\r\n"; } request += boundary + "--\r\n"; this.oTransport_.setRequestHeader("Content-Length", request.length); try { this.oTransport_.send(request); } catch (exc) { this.raiseError("AJAX POSTMULTIPART Error: Send failed with message '"+ exc.message + "'. "); return; } }, makeURL: function() { if (this.sMethod_ == "POST" || this.sMethod_ == "POSTMULTIPART" || !this.sParameters_) { return this.sURL_; } var nP = this.sURL_.indexOf('?'); if (nP != -1) { // ? already exists in sURL // TODO (FIXME): probably amp must not be added (as URL serializers glue it) return this.sURL_ + '&' + this.sParameters_; } return this.sURL_ + '?' + this.sParameters_; }, RequestCallback: function() { // 'this' bound to AjaxRequest object itself if(this.execTimer) window.clearTimeout(this.execTimer); if (!this.oTransport_) throw new Error("AjaxRequest::RequestCallback: Request not initialized! "); if (this.oTransport_.readyState != 4) return; // drop from active requests AXHRManager.prototype.aRequests_[this.nReqNum_] = undefined; this.SetMessage("Completed", true); if (this.onComplete_) this.onComplete_(this); // do not handle local calls if (this.oTransport_.status == 0) return; if (this.bAsync_ && ('onreadystatechange' in this.oTransport_)) { delete this.oTransport_.onreadystatechange; // IE memory leak fix } this.CompleteRequest(); }, CompleteRequest: function() { if (this.oTransport_.status == 200) { /* server call done */ } else if (this.oTransport_.status == 400) { /* server call done, got exception */ } else if (this.oTransport_.status == 404) { this.raiseError("Request URL '"+this.sURL_+"' does not exist. "); return; } else { if (this.bAsync) { this.raiseError("Unknown error: Status code is " + this.oTransport_.status + ", readyState is " + this.oTransport_.readyState+". "); return; } } this.substXml_ = undefined; this.bCompleted_ = true; var oXML = this.oTransport_.responseXML; if (!oXML || !oXML.documentElement || !("selectNodes" in oXML) ) { var doc = new AXML(); doc.Parse(this.oTransport_.responseText); this.substXml_ = doc.XML(); oXML = this.substXml_; if (!oXML) { var sResponse = this.oTransport_.responseText; this.raiseError("Ajax ERROR: Got text response instead of XML: '"+sResponse+"'. "); return; } } if (oXML.documentElement) { if(oXML.documentElement.nodeName == "parsererror") { // bad XML received (Mozilla) var sResponse = oXML.xml; this.raiseError("Ajax ERROR: Parser Error: "+AXML.EscapeEntities(sResponse)); return; } else if( oXML.documentElement.nodeName == "xmlstream") { var xR = oXML.documentElement; if (xR.firstChild.nodeName=="Exception") { try { var exc=XMLRestore(this.oTransport_.responseText); this.raiseError(exc.Message(), exc.ErrorCode()); return; } catch(err) { LogE("Unrecognized xmlstream/Exception: "+this.oTransport_.responseText); } } else if (this.oTransport_.status == 400 && xR.firstChild.nodeName=="APacket") { var oPacket; try { oPacket = XMLRestore(this.oTransport_.responseText); var oData = oPacket.Data(); var sErrorText = oData["ExceptionText"]; // unsigned int lies in ErrorCode var nErrorCode = (typeof(oData["ErrorCode"]) != 'undefined') ? oData["ErrorCode"].Value() : undefined; this.raiseError(sErrorText, nErrorCode); return; } catch (err) { LogE("Unrecognized xmlstream/APacket: "+this.oTransport_.responseText); } } } } else { this.raiseError("Web server is not available. Please retry your request later. "); // alert("AjaxRequest: oXML.documentElement is NULL! Text Content is: "+this.oTransport_.responseText); return; } //LogL("AjaxRequest: "+this.sURL_+" completed."); this.SetMessage("Success", true); if (AR_LOGLEVEL) { LogL("$$ DONE: " + this.sMethod_ + " " + this.sURL_ + " $$"); } if (this.onSuccess_) this.onSuccess_(this); }, XML: function() { if (!this.bCompleted_) throw new Error("AjaxRequest: Request not completed yet! "); if (this.substXml_) { return this.substXml_; } return this.oTransport_.responseXML; }, Text: function() { return this.oTransport_.responseText; }, Status: function() { return this.oTransport_.status; }, // TODO: remove this function, rewrite to exception handlers SetMessage: function(sMessage, bSuccess) { if (!sMessage) sMessage = ""; var sClassName = (!bSuccess) ? "ajaxerror" : "ajaxsuccess"; // TODO: Remove spike with AjaxError element var elAjaxError = $("AjaxError"); if (elAjaxError) elAjaxError.innerHTML = "Ajax Message: " + AXML.EscapeEntities(sMessage) + "

"; }, raiseError: function(sMessage, code) { // TODO: set error codes for typical AJAX errors this.sExceptionText_ = sMessage; this.ErrorCode_ = code; if (this.onException_) this.onException_(this); }, Error: function() { return this.sExceptionText_; }, ErrorCode: function() { return this.ErrorCode_; }, Kill: function() { // kill server request if (this.oTransport_) this.oTransport_.abort(); this.bCompleted_=true; }, URL: function(){ return this.sURL_; }, Parameters: function(){ return this.sParameters_; }, IsAsync: function() { return this.bAsync_; } }); var GXHRManager = new AXHRManager();