UNPKG

sku-pg

Version:

Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!

908 lines (772 loc) 28.2 kB
// Copyright 2006 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Wrapper class for handling XmlHttpRequests. * * One off requests can be sent through goog.net.XhrIo.send() or an * instance can be created to send multiple requests. Each request uses its * own XmlHttpRequest object and handles clearing of the event callback to * ensure no leaks. * * XhrIo is event based, it dispatches events when a request finishes, fails or * succeeds or when the ready-state changes. The ready-state event fires first, * followed by a generic completed event, and lastly the error or success event * is fired as appropriate. * * The error event may also be called before completed and * ready-state-change if the XmlHttpRequest.open() or .send() methods throw. * * This class does not support multiple requests, queuing, or prioritization. * * Tested = IE6, FF1.5, Safari, Opera 8.5 * * TODO(user): Error cases aren't playing nicely in Safari. * * */ goog.provide('goog.net.XhrIo'); goog.require('goog.Timer'); goog.require('goog.debug.Logger'); goog.require('goog.debug.errorHandlerWeakDep'); goog.require('goog.events.EventTarget'); goog.require('goog.json'); goog.require('goog.net.ErrorCode'); goog.require('goog.net.EventType'); goog.require('goog.net.XmlHttp'); goog.require('goog.net.xhrMonitor'); goog.require('goog.structs'); goog.require('goog.structs.Map'); /** * Basic class for handling XMLHttpRequests. * @param {goog.net.XmlHttpFactory=} opt_xmlHttpFactory Factory to use when * creating XMLHttpRequest objects. * @constructor * @extends {goog.events.EventTarget} */ goog.net.XhrIo = function(opt_xmlHttpFactory) { goog.events.EventTarget.call(this); /** * Map of default headers to add to every request, use: * XhrIo.headers.set(name, value) * @type {goog.structs.Map} */ this.headers = new goog.structs.Map(); /** * Optional XmlHttpFactory * @type {goog.net.XmlHttpFactory} * @private */ this.xmlHttpFactory_ = opt_xmlHttpFactory || null; }; goog.inherits(goog.net.XhrIo, goog.events.EventTarget); /** * A reference to the XhrIo logger * @type {goog.debug.Logger} * @private */ goog.net.XhrIo.prototype.logger_ = goog.debug.Logger.getLogger('goog.net.XhrIo'); /** * The Content-Type HTTP header name * @type {string} */ goog.net.XhrIo.CONTENT_TYPE_HEADER = 'Content-Type'; /** * The Content-Type HTTP header value for a url-encoded form * @type {string} */ goog.net.XhrIo.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8'; /** * All non-disposed instances of goog.net.XhrIo created * by {@link goog.net.XhrIo.send} are in this Array. * @see goog.net.XhrIo.cleanupAllPendingStaticSends * @type {Array.<goog.net.XhrIo>} * @private */ goog.net.XhrIo.sendInstances_ = []; /** * Static send that creates a short lived instance of XhrIo to send the * request. * @see goog.net.XhrIo.cleanupAllPendingStaticSends * @param {string|goog.Uri} url Uri to make request to. * @param {Function=} opt_callback Callback function for when request is * complete. * @param {string=} opt_method Send method, default: GET. * @param {string|GearsBlob=} opt_content Post data. This can be a Gears blob * if the underlying HTTP request object is a Gears HTTP request. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the * request. * @param {number=} opt_timeoutInterval Number of milliseconds after which an * incomplete request will be aborted; 0 means no timeout is set. */ goog.net.XhrIo.send = function(url, opt_callback, opt_method, opt_content, opt_headers, opt_timeoutInterval) { var x = new goog.net.XhrIo(); goog.net.XhrIo.sendInstances_.push(x); if (opt_callback) { goog.events.listen(x, goog.net.EventType.COMPLETE, opt_callback); } goog.events.listen(x, goog.net.EventType.READY, goog.partial(goog.net.XhrIo.cleanupSend_, x)); if (opt_timeoutInterval) { x.setTimeoutInterval(opt_timeoutInterval); } x.send(url, opt_method, opt_content, opt_headers); }; /** * Disposes all non-disposed instances of goog.net.XhrIo created by * {@link goog.net.XhrIo.send}. * {@link goog.net.XhrIo.send} cleans up the goog.net.XhrIo instance * it creates when the request completes or fails. However, if * the request never completes, then the goog.net.XhrIo is not disposed. * This can occur if the window is unloaded before the request completes. * We could have {@link goog.net.XhrIo.send} return the goog.net.XhrIo * it creates and make the client of {@link goog.net.XhrIo.send} be * responsible for disposing it in this case. However, this makes things * significantly more complicated for the client, and the whole point * of {@link goog.net.XhrIo.send} is that it's simple and easy to use. * Clients of {@link goog.net.XhrIo.send} should call * {@link goog.net.XhrIo.cleanupAllPendingStaticSends} when doing final * cleanup on window unload. */ goog.net.XhrIo.cleanup = function() { var instances = goog.net.XhrIo.sendInstances_; while (instances.length) { instances.pop().dispose(); } }; /** * Installs exception protection for all entry point introduced by * goog.net.XhrIo instances which are not protected by * {@link goog.debug.ErrorHandler#protectWindowSetTimeout}, * {@link goog.debug.ErrorHandler#protectWindowSetInterval}, or * {@link goog.events.protectBrowserEventEntryPoint}. * * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to * protect the entry point(s). * @param {boolean=} opt_tracers Whether to install tracers around the entry * point. */ goog.net.XhrIo.protectEntryPoints = function( errorHandler, opt_tracers) { goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = errorHandler.protectEntryPoint( goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_, opt_tracers); }; /** * Disposes of the specified goog.net.XhrIo created by * {@link goog.net.XhrIo.send} and removes it from * {@link goog.net.XhrIo.pendingStaticSendInstances_}. * @param {goog.net.XhrIo} XhrIo An XhrIo created by * {@link goog.net.XhrIo.send}. * @private */ goog.net.XhrIo.cleanupSend_ = function(XhrIo) { XhrIo.dispose(); goog.array.remove(goog.net.XhrIo.sendInstances_, XhrIo); }; /** * Whether XMLHttpRequest is active. A request is active from the time send() * is called until onReadyStateChange() is complete, or error() or abort() * is called. * @type {boolean} * @private */ goog.net.XhrIo.prototype.active_ = false; /** * Reference to an XMLHttpRequest object that is being used for the transfer. * @type {XMLHttpRequest|GearsHttpRequest} * @private */ goog.net.XhrIo.prototype.xhr_ = null; /** * The options to use with the current XMLHttpRequest object. * @type {Object} * @private */ goog.net.XhrIo.prototype.xhrOptions_ = null; /** * Last URL that was requested. * @type {string|goog.Uri} * @private */ goog.net.XhrIo.prototype.lastUri_ = ''; /** * Method for the last request. * @type {string} * @private */ goog.net.XhrIo.prototype.lastMethod_ = ''; /** * Last error code. * @type {goog.net.ErrorCode} * @private */ goog.net.XhrIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; /** * Last error message. * @type {Error|string} * @private */ goog.net.XhrIo.prototype.lastError_ = ''; /** * This is used to ensure that we don't dispatch an multiple ERROR events. This * can happen in IE when it does a synchronous load and one error is handled in * the ready statte change and one is handled due to send() throwing an * exception. * @type {boolean} * @private */ goog.net.XhrIo.prototype.errorDispatched_ = false; /** * Used to make sure we don't fire the complete event from inside a send call. * @type {boolean} * @private */ goog.net.XhrIo.prototype.inSend_ = false; /** * Used in determining if a call to {@link #onReadyStateChange_} is from within * a call to this.xhr_.open. * @type {boolean} * @private */ goog.net.XhrIo.prototype.inOpen_ = false; /** * Used in determining if a call to {@link #onReadyStateChange_} is from within * a call to this.xhr_.abort. * @type {boolean} * @private */ goog.net.XhrIo.prototype.inAbort_ = false; /** * Number of milliseconds after which an incomplete request will be aborted and * a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set. * @type {number} * @private */ goog.net.XhrIo.prototype.timeoutInterval_ = 0; /** * Window timeout ID used to cancel the timeout event handler if the request * completes successfully. * @type {Object} * @private */ goog.net.XhrIo.prototype.timeoutId_ = null; /** * Returns the number of milliseconds after which an incomplete request will be * aborted, or 0 if no timeout is set. * @return {number} Timeout interval in milliseconds. */ goog.net.XhrIo.prototype.getTimeoutInterval = function() { return this.timeoutInterval_; }; /** * Sets the number of milliseconds after which an incomplete request will be * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no * timeout is set. * @param {number} ms Timeout interval in milliseconds; 0 means none. */ goog.net.XhrIo.prototype.setTimeoutInterval = function(ms) { this.timeoutInterval_ = Math.max(0, ms); }; /** * Instance send that actually uses XMLHttpRequest to make a server call. * @param {string|goog.Uri} url Uri to make request too. * @param {string=} opt_method Send method, default: GET. * @param {string|GearsBlob=} opt_content Post data. This can be a Gears blob * if the underlying HTTP request object is a Gears HTTP request. * @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the * request. */ goog.net.XhrIo.prototype.send = function(url, opt_method, opt_content, opt_headers) { if (this.active_) { throw Error('[goog.net.XhrIo] Object is active with another request'); } var method = opt_method || 'GET'; this.lastUri_ = url; this.lastError_ = ''; this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; this.lastMethod_ = method; this.errorDispatched_ = false; this.active_ = true; // Use the factory to create the XHR object and options this.xhr_ = this.createXhr(); this.xhrOptions_ = this.xmlHttpFactory_ ? this.xmlHttpFactory_.getOptions() : goog.net.XmlHttp.getOptions(); // We tell the Xhr Monitor that we are opening an XMLHttpRequest. This stops // IframeIo from destroying iframes that may have been the source of the // execution context, which can result in an error in FF. See xhrmonitor.js // for more details. goog.net.xhrMonitor.markXhrOpen(this.xhr_); // Set up the onreadystatechange callback this.xhr_.onreadystatechange = goog.bind(this.onReadyStateChange_, this); /** * Try to open the XMLHttpRequest (always async), if an error occurs here it * is generally permission denied * @preserveTry */ try { this.logger_.fine(this.formatMsg_('Opening Xhr')); this.inOpen_ = true; this.xhr_.open(method, url, true); // Always async! this.inOpen_ = false; } catch (err) { this.logger_.fine(this.formatMsg_('Error opening Xhr: ' + err.message)); this.error_(goog.net.ErrorCode.EXCEPTION, err); return; } // We can't use null since this won't allow POSTs to have a content length // specified which will cause some proxies to return a 411 error. var content = opt_content || ''; var headers = this.headers.clone(); // Add headers specific to this request if (opt_headers) { goog.structs.forEach(opt_headers, function(value, key) { headers.set(key, value); }); } if (method == 'POST' && !headers.containsKey(goog.net.XhrIo.CONTENT_TYPE_HEADER)) { // For POST requests, default to the url-encoded form content type. headers.set(goog.net.XhrIo.CONTENT_TYPE_HEADER, goog.net.XhrIo.FORM_CONTENT_TYPE); } // Add the headers to the Xhr object goog.structs.forEach(headers, function(value, key) { this.xhr_.setRequestHeader(key, value); }, this); /** * Try to send the request, or other wise report an error (404 not found). * @preserveTry */ try { if (this.timeoutId_) { // This should never happen, since the if (this.active_) above shouldn't // let execution reach this point if there is a request in progress... goog.Timer.defaultTimerObject.clearTimeout(this.timeoutId_); this.timeoutId_ = null; } if (this.timeoutInterval_ > 0) { this.logger_.fine(this.formatMsg_('Will abort after ' + this.timeoutInterval_ + 'ms if incomplete')); this.timeoutId_ = goog.Timer.defaultTimerObject.setTimeout( goog.bind(this.timeout_, this), this.timeoutInterval_); } this.logger_.fine(this.formatMsg_('Sending request')); this.inSend_ = true; this.xhr_.send(content); this.inSend_ = false; } catch (err) { this.logger_.fine(this.formatMsg_('Send error: ' + err.message)); this.error_(goog.net.ErrorCode.EXCEPTION, err); } }; /** * Creates a new XHR object. * @return {XMLHttpRequest|GearsHttpRequest} The newly created XHR object. * @protected */ goog.net.XhrIo.prototype.createXhr = function() { return this.xmlHttpFactory_ ? this.xmlHttpFactory_.createInstance() : new goog.net.XmlHttp(); }; /** * Override of dispatchEvent. We need to keep track if an XMLHttpRequest is * being sent from the context of another requests' repsonse. If it is then, we * make the XHR send async. * @param {goog.events.Event|string} e Event to dispatch. * @return {boolean} Whether the dispatch completed without a handler calling * preventDefault. */ goog.net.XhrIo.prototype.dispatchEvent = function(e) { if (this.xhr_) { goog.net.xhrMonitor.pushContext(this.xhr_); try { return goog.net.XhrIo.superClass_.dispatchEvent.call(this, e); } finally { goog.net.xhrMonitor.popContext(); } } else { return goog.net.XhrIo.superClass_.dispatchEvent.call(this, e); } }; /** * The request didn't complete after {@link goog.net.XhrIo#timeoutInterval_} * milliseconds; raises a {@link goog.net.EventType.TIMEOUT} event and aborts * the request. * @private */ goog.net.XhrIo.prototype.timeout_ = function() { if (typeof goog == 'undefined') { // If goog is undefined then the callback has occurred as the application // is unloading and will error. Thus we let it silently fail. } else if (this.xhr_) { this.lastError_ = 'Timed out after ' + this.timeoutInterval_ + 'ms, aborting'; this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT; this.logger_.fine(this.formatMsg_(this.lastError_)); this.dispatchEvent(goog.net.EventType.TIMEOUT); this.abort(goog.net.ErrorCode.TIMEOUT); } }; /** * Something errorred, so inactivate, fire error callback and clean up * @param {goog.net.ErrorCode} errorCode The error code. * @param {Error} err The error object. * @private */ goog.net.XhrIo.prototype.error_ = function(errorCode, err) { this.active_ = false; if (this.xhr_) { this.inAbort_ = true; this.xhr_.abort(); // Ensures XHR isn't hung (FF) this.inAbort_ = false; } this.lastError_ = err; this.lastErrorCode_ = errorCode; this.dispatchErrors_(); this.cleanUpXhr_() }; /** * Dispatches COMPLETE and ERROR in case of an error. This ensures that we do * not dispatch multiple error events. * @private */ goog.net.XhrIo.prototype.dispatchErrors_ = function() { if (!this.errorDispatched_) { this.errorDispatched_ = true; this.dispatchEvent(goog.net.EventType.COMPLETE); this.dispatchEvent(goog.net.EventType.ERROR); } }; /** * Abort the current XMLHttpRequest * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use - * defaults to ABORT. */ goog.net.XhrIo.prototype.abort = function(opt_failureCode) { if (this.xhr_) { this.logger_.fine(this.formatMsg_('Aborting')); this.active_ = false; this.inAbort_ = true; this.xhr_.abort(); this.inAbort_ = false; this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT; this.dispatchEvent(goog.net.EventType.COMPLETE); this.dispatchEvent(goog.net.EventType.ABORT); this.cleanUpXhr_(); } }; /** * Nullifies all callbacks to reduce risks of leaks. */ goog.net.XhrIo.prototype.disposeInternal = function() { if (this.xhr_) { // We explicitly do not call xhr_.abort() unless active_ is still true. // This is to avoid unnecessarily aborting a successful request when // disposeInternal() is called in a callback triggered by a complete // response, but in which browser cleanup has not yet finished. // (See http://b/issue?id=1684217.) if (this.active_) { this.active_ = false; this.inAbort_ = true; this.xhr_.abort(); this.inAbort_ = false; } this.cleanUpXhr_(true); } goog.net.XhrIo.superClass_.disposeInternal.call(this); }; /** * Internal handler for the XHR object's readystatechange event. This method * checks the status and the readystate and fires the correct callbacks. * If the request has ended, the handlers are cleaned up and the XHR object is * nullified. * @private */ goog.net.XhrIo.prototype.onReadyStateChange_ = function() { if (!this.inOpen_ && !this.inSend_ && !this.inAbort_) { // Were not being called from within a call to this.xhr_.send // this.xhr_.abort, or this.xhr_.open, so this is an entry point this.onReadyStateChangeEntryPoint_(); } else { this.onReadyStateChangeHelper_(); } }; /** * Used to protect the onreadystatechange handler entry point. Necessary * as {#onReadyStateChange_} maybe called from within send or abort, this * method is only called when {#onReadyStateChange_} is called as an * entry point. * {@see #protectEntryPoints} * @private */ goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_ = function() { this.onReadyStateChangeHelper_(); }; /** * Helper for {@link #onReadyStateChange_}. This is used so that * entry point calls to {@link #onReadyStateChange_} can be routed through * {@link #onReadyStateChangeEntryPoint_}. * @private */ goog.net.XhrIo.prototype.onReadyStateChangeHelper_ = function() { if (!this.active_) { // can get called inside abort call return; } if (typeof goog == 'undefined') { // NOTE(user): If goog is undefined then the callback has occurred as the // application is unloading and will error. Thus we let it silently fail. } else if ( this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] && this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE && this.getStatus() == 2) { // NOTE(user): In IE if send() errors on a *local* request the readystate // is still changed to COMPLETE. We need to ignore it and allow the // try/catch around send() to pick up the error. this.logger_.fine(this.formatMsg_( 'Local request error detected and ignored')); } else { // In IE when the response has been cached we sometimes get the callback // from inside the send call and this usually breaks code that assumes that // XhrIo is asynchronous. If that is the case we delay the callback // using a timer. if (this.inSend_ && this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE) { goog.Timer.defaultTimerObject.setTimeout( goog.bind(this.onReadyStateChange_, this), 0); return; } this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE); // readyState indicates the transfer has finished if (this.isComplete()) { this.logger_.fine(this.formatMsg_('Request complete')); this.active_ = false; // Call the specific callbacks for success or failure. Only call the // success if the status is 200 (HTTP_OK) or 304 (HTTP_CACHED) if (this.isSuccess()) { this.dispatchEvent(goog.net.EventType.COMPLETE); this.dispatchEvent(goog.net.EventType.SUCCESS); } else { this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR; this.lastError_ = this.getStatusText() + ' [' + this.getStatus() + ']'; this.dispatchErrors_(); } this.cleanUpXhr_(); } } }; /** * Remove the listener to protect against leaks, and nullify the XMLHttpRequest * object. * @param {boolean=} opt_fromDispose If this is from the dispose (don't want to * fire any events). * @private */ goog.net.XhrIo.prototype.cleanUpXhr_ = function(opt_fromDispose) { if (this.xhr_) { // Save reference so we can mark it as closed after the READY event. The // READY event may trigger another request, thus we must nullify this.xhr_ var xhr = this.xhr_; var clearedOnReadyStateChange = this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] ? goog.nullFunction : null; this.xhr_ = null; this.xhrOptions_ = null; if (this.timeoutId_) { // Cancel any pending timeout event handler. goog.Timer.defaultTimerObject.clearTimeout(this.timeoutId_); this.timeoutId_ = null; } if (!opt_fromDispose) { goog.net.xhrMonitor.pushContext(xhr); this.dispatchEvent(goog.net.EventType.READY); goog.net.xhrMonitor.popContext(); } // Mark the request as having completed. goog.net.xhrMonitor.markXhrClosed(xhr); try { // NOTE(user): Not nullifying in FireFox can still leak if the callbacks // are defined in the same scope as the instance of XhrIo. But, IE doesn't // allow you to set the onreadystatechange to NULL so nullFunction is // used. xhr.onreadystatechange = clearedOnReadyStateChange; } catch (e) { // This seems to occur with a Gears HTTP request. Delayed the setting of // this onreadystatechange until after READY is sent out and catching the // error to see if we can track down the problem. this.logger_.severe('Problem encountered resetting onreadystatechange: ' + e.message); } } }; /** * @return {boolean} Whether there is an active request. */ goog.net.XhrIo.prototype.isActive = function() { return this.active_; }; /** * @return {boolean} Whether the request has completed. */ goog.net.XhrIo.prototype.isComplete = function() { return this.getReadyState() == goog.net.XmlHttp.ReadyState.COMPLETE; }; /** * @return {boolean} Whether the request completed with a success. */ goog.net.XhrIo.prototype.isSuccess = function() { switch (this.getStatus()) { case 0: // Used for local XHR requests case 200: // Http Success case 204: // Http Success - no content case 304: // Http Cache return true; default: return false; } }; /** * Get the readystate from the Xhr object * Will only return correct result when called from the context of a callback * @return {goog.net.XmlHttp.ReadyState} goog.net.XmlHttp.ReadyState.*. */ goog.net.XhrIo.prototype.getReadyState = function() { return this.xhr_ ? /** @type {goog.net.XmlHttp.ReadyState} */ (this.xhr_.readyState) : goog.net.XmlHttp.ReadyState.UNINITIALIZED; }; /** * Get the status from the Xhr object * Will only return correct result when called from the context of a callback * @return {number} Http status. */ goog.net.XhrIo.prototype.getStatus = function() { /** * IE doesn't like you checking status until the readystate is greater than 2 * (i.e. it is recieving or complete). The try/catch is used for when the * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_. * @preserveTry */ try { return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ? this.xhr_.status : -1; } catch (e) { this.logger_.warning('Can not get status: ' + e.message); return -1; } }; /** * Get the status text from the Xhr object * Will only return correct result when called from the context of a callback * @return {string} Status text. */ goog.net.XhrIo.prototype.getStatusText = function() { /** * IE doesn't like you checking status until the readystate is greater than 2 * (i.e. it is recieving or complete). The try/catch is used for when the * page is unloading and an ERROR_NOT_AVAILABLE may occur when accessing xhr_. * @preserveTry */ try { return this.getReadyState() > goog.net.XmlHttp.ReadyState.LOADED ? this.xhr_.statusText : ''; } catch (e) { this.logger_.fine('Can not get status: ' + e.message); return ''; } }; /** * Get the last Uri that was requested * @return {string} Last Uri. */ goog.net.XhrIo.prototype.getLastUri = function() { return String(this.lastUri_); }; /** * Get the response text from the Xhr object * Will only return correct result when called from the context of a callback * @return {string} Result from the server. */ goog.net.XhrIo.prototype.getResponseText = function() { return this.xhr_ ? this.xhr_.responseText : ''; }; /** * Get the response XML from the Xhr object * Will only return correct result when called from the context of a callback * @return {Document} The DOM Document representing the XML file. */ goog.net.XhrIo.prototype.getResponseXml = function() { return this.xhr_ ? this.xhr_.responseXML : null; }; /** * Get the response and evaluates it as JSON from the Xhr object * Will only return correct result when called from the context of a callback * @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for * stripping of the response before parsing. This needs to be set only if * your backend server prepends the same prefix string to the JSON response. * @return {Object|undefined} JavaScript object. */ goog.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) { if (!this.xhr_) { return undefined; } var responseText = this.xhr_.responseText; if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) { responseText = responseText.substring(opt_xssiPrefix.length); } return goog.json.parse(responseText); }; /** * Get the value of the response-header with the given name from the Xhr object * Will only return correct result when called from the context of a callback * and the request has completed * @param {string} key The name of the response-header to retrieve. * @return {string|undefined} The value of the response-header named key. */ goog.net.XhrIo.prototype.getResponseHeader = function(key) { return this.xhr_ && this.isComplete() ? this.xhr_.getResponseHeader(key) : undefined; }; /** * Get the last error message * @return {goog.net.ErrorCode} Last error code. */ goog.net.XhrIo.prototype.getLastErrorCode = function() { return this.lastErrorCode_; }; /** * Get the last error message * @return {string} Last error message. */ goog.net.XhrIo.prototype.getLastError = function() { return goog.isString(this.lastError_) ? this.lastError_ : String(this.lastError_); }; /** * Adds the last method, status and URI to the message. This is used to add * this information to the logging calls. * @param {string} msg The message text that we want to add the extra text to. * @return {string} The message with the extra text appended. * @private */ goog.net.XhrIo.prototype.formatMsg_ = function(msg) { return msg + ' [' + this.lastMethod_ + ' ' + this.lastUri_ + ' ' + this.getStatus() + ']'; };