From 3268e53ed1dcdcf08da8fb64688401ee9c973e6c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 20 Dec 2007 09:26:53 +0000 Subject: [PATCH] Serious rewrite in the Ajax namespace by pgiraud to keep up with prototype.js 1.6. Thanks for the effort and for suffering numerous revisions. Now, let's cross our fingers... r=crschmidt,me (closes #1170) git-svn-id: http://svn.openlayers.org/trunk/openlayers@5535 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- lib/OpenLayers/Ajax.js | 434 +++++++++++++++++++++++++---------------- tests/manual/ajax.html | 70 +++++++ tests/manual/ajax.txt | 1 + 3 files changed, 338 insertions(+), 167 deletions(-) create mode 100644 tests/manual/ajax.html create mode 100644 tests/manual/ajax.txt diff --git a/lib/OpenLayers/Ajax.js b/lib/OpenLayers/Ajax.js index d22443f9e5..c86f8eaeb8 100644 --- a/lib/OpenLayers/Ajax.js +++ b/lib/OpenLayers/Ajax.js @@ -135,9 +135,9 @@ OpenLayers.Ajax = { */ getTransport: function() { return OpenLayers.Util.Try( + function() {return new XMLHttpRequest();}, function() {return new ActiveXObject('Msxml2.XMLHTTP');}, - function() {return new ActiveXObject('Microsoft.XMLHTTP');}, - function() {return new XMLHttpRequest();} + function() {return new ActiveXObject('Microsoft.XMLHTTP');} ) || false; }, @@ -167,12 +167,22 @@ OpenLayers.Ajax.Responders = { * responderToAdd - {?} */ register: function(responderToAdd) { - for (var i = 0; i < this.responders.length; i++){ - if (responderToAdd == this.responders[i]){ - return; - } - } - this.responders.push(responderToAdd); + for (var i = 0; i < this.responders.length; i++){ + if (responderToAdd == this.responders[i]){ + return; + } + } + this.responders.push(responderToAdd); + }, + + /** + * Method: unregister + * + * Parameters: + * responderToRemove - {?} + */ + unregister: function(responderToRemove) { + OpenLayers.Util.removeItem(this.reponders, responderToRemove); }, /** @@ -182,9 +192,8 @@ OpenLayers.Ajax.Responders = { * callback - {?} * request - {?} * transport - {?} - * json - {?} */ - dispatch: function(callback, request, transport, json) { + dispatch: function(callback, request, transport) { var responder; for (var i = 0; i < this.responders.length; i++) { responder = this.responders[i]; @@ -193,7 +202,7 @@ OpenLayers.Ajax.Responders = { typeof responder[callback] == 'function') { try { responder[callback].apply(responder, - [request, transport, json]); + [request, transport]); } catch (e) {} } } @@ -217,50 +226,34 @@ OpenLayers.Ajax.Responders.register({ }); /** - * Namespace: OpenLayers.Ajax.Base - * {Object} + * Class: OpenLayers.Ajax.Base */ -OpenLayers.Ajax.Base = function() {}; -OpenLayers.Ajax.Base.prototype = { - +OpenLayers.Ajax.Base = OpenLayers.Class({ + /** - * Function: setOptions + * Constructor: OpenLayers.Ajax.Base * - * Parameters: + * Parameters: * options - {Object} */ - setOptions: function(options) { + initialize: function(options) { this.options = { - 'method': 'post', - 'asynchronous': true, - 'parameters': '' + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '' }; OpenLayers.Util.extend(this.options, options || {}); - }, - - /** - * Function: responseIsSuccess - * - * Returns: - * {Boolean} - */ - responseIsSuccess: function() { - return this.transport.status == undefined || - this.transport.status == 0 || - (this.transport.status >= 200 && this.transport.status < 300); - }, - - /** - * Function: responseIsFailure - * - * Returns: - * {Boolean} - */ - responseIsFailure: function() { - return !this.responseIsSuccess(); + + this.options.method = this.options.method.toLowerCase(); + + if (typeof this.options.parameters == 'string') { + this.options.parameters = + OpenLayers.Util.getParameters(this.options.parameters); + } } -}; - +}); /** * Class: OpenLayers.Ajax.Request @@ -269,17 +262,25 @@ OpenLayers.Ajax.Base.prototype = { * - */ OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, { + + /** + * Property: _complete + * + * {Boolean} + */ + _complete: false, - /** - * Constructor: OpenLayers.Ajax.Request - * - * Parameters: - * url - {String} - * options - {Object} - */ + /** + * Constructor: OpenLayers.Ajax.Request + * + * Parameters: + * url - {String} + * options - {Object} + */ initialize: function(url, options) { + OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]); + this.transport = OpenLayers.Ajax.getTransport(); - this.setOptions(options); this.request(url); }, @@ -290,129 +291,139 @@ OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, { * url - {String} */ request: function(url) { - var parameters = this.options.parameters || ''; - if (parameters.length > 0) { - parameters += '&_='; + this.url = url; + this.method = this.options.method; + var params = OpenLayers.Util.extend({}, this.options.parameters); + + if (this.method != 'get' && this.method != 'post') { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = OpenLayers.Util.getParameterString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') { + this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params; + } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + params += '&_='; + } } try { - this.url = url; - if (this.options.method == 'get' && parameters.length > 0) { - this.url += (this.url.match(/\?/) ? '&' : '?') + parameters; + var response = new OpenLayers.Ajax.Response(this); + if (this.options.onCreate) { + this.options.onCreate(response); } OpenLayers.Ajax.Responders.dispatch('onCreate', this, - this.transport); + response); - this.transport.open(this.options.method, + this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous); if (this.options.asynchronous) { - setTimeout(OpenLayers.Function.bind( - (function() {this.respondToReadyState(1);}),this), 10 - ); + window.setTimeout( + OpenLayers.Function.bind(this.respondToReadyState, this, 1), + 10); } this.transport.onreadystatechange = OpenLayers.Function.bind(this.onStateChange, this); this.setRequestHeaders(); - var body = this.options.postBody ? this.options.postBody - : parameters; - this.transport.send(this.options.method == 'post' ? body : null); + this.body = this.method == 'post' ? + (this.options.postBody || params) : null; + this.transport.send(this.body); // Force Firefox to handle ready state 4 for synchronous requests if (!this.options.asynchronous && this.transport.overrideMimeType) { - this.onStateChange(); } - } catch (e) { this.dispatchException(e); } }, - - /** - * Method: setRequestHeaders - */ - setRequestHeaders: function() { - var requestHeaders = [ - 'X-Requested-With', - 'XMLHttpRequest', - 'X-Prototype-Version', - 'OpenLayers' - ]; - - if (this.options.method == 'post' && !this.options.postBody) { - requestHeaders.push('Content-type', - 'application/x-www-form-urlencoded'); - - // Force "Connection: close" for Mozilla browsers to work around - // a bug where XMLHttpReqeuest sends an incorrect Content-length - // header. See Mozilla Bugzilla #246651. - if (this.transport.overrideMimeType) { - requestHeaders.push('Connection', 'close'); - } - } - - if (this.options.requestHeaders) { - requestHeaders.push.apply(requestHeaders, - this.options.requestHeaders); - } - - for (var i = 0; i < requestHeaders.length; i += 2) { - this.transport.setRequestHeader(requestHeaders[i], - requestHeaders[i+1]); - } - }, /** * Method: onStateChange */ onStateChange: function() { var readyState = this.transport.readyState; - if (readyState != 1) { - this.respondToReadyState(this.transport.readyState); + if (readyState > 1 && !((readyState == 4) && this._complete)) { + this.respondToReadyState(this.transport.readyState); } }, - - /** - * Method: header - * - * Returns: - * {?} - */ - header: function(name) { - try { - return this.transport.getResponseHeader(name); - } catch (e) {} - }, - - /** - * Method: evalJSON - * - * Returns: - * {?} - */ - evalJSON: function() { - try { - return eval(this.header('X-JSON')); - } catch (e) {} - }, - + /** - * Method: evalResponse - * - * Returns: - * {?} + * Method: setRequestHeaders */ - evalResponse: function() { + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*', + 'OpenLayers': true + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) { + headers['Connection'] = 'close'; + } + } + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (typeof extra.push == 'function') { + for (var i = 0, length = extras.length; i < length; i += 2) { + headers[extras[i]] = extras[i+1]; + } + } else { + for (var i in extras) { + headers[i] = pair[i]; + } + } + } + + for (var name in headers) { + this.transport.setRequestHeader(name, headers[name]); + } + }, + + /** + * Method: success + * + * Returns: + * {Boolean} - + */ + success: function() { + var status = this.getStatus(); + return !status || (status >=200 && status < 300); + }, + + /** + * Method: getStatus + * + * Returns: + * {Integer} - Status + */ + getStatus: function() { try { - return eval(this.transport.responseText); + return this.transport.status || 0; } catch (e) { - this.dispatchException(e); + return 0 } }, @@ -423,43 +434,54 @@ OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, { * readyState - {?} */ respondToReadyState: function(readyState) { - var event = OpenLayers.Ajax.Request.Events[readyState]; - var transport = this.transport, json = this.evalJSON(); + var state = OpenLayers.Ajax.Request.Events[readyState]; + var response = new OpenLayers.Ajax.Response(this); - if (event == 'Complete') { + if (state == 'Complete') { try { - var responseSuccess = this.responseIsSuccess() ? 'Success' - : 'Failure'; - - (this.options['on' + this.transport.status] || - this.options['on' + responseSuccess] || - OpenLayers.Ajax.emptyFunction)(transport, json); + this._complete = true; + (this.options['on' + response.status] || + this.options['on' + (this.success() ? 'Success' : 'Failure')] || + OpenLayers.Ajax.emptyFunction)(response); } catch (e) { this.dispatchException(e); } - var contentType = this.header('Content-type') || ''; - if (contentType.match(/^text\/javascript/i)) { - this.evalResponse(); - } + var contentType = response.getHeader('Content-type'); } try { - (this.options['on' + event] || - OpenLayers.Ajax.emptyFunction)(transport, json); - OpenLayers.Ajax.Responders.dispatch('on' + event, + (this.options['on' + state] || + OpenLayers.Ajax.emptyFunction)(response); + OpenLayers.Ajax.Responders.dispatch('on' + state, this, - transport, - json); + response); } catch (e) { this.dispatchException(e); } - // Avoid memory leak in MSIE: clean up the oncomplete event handler - if (event == 'Complete') { + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction; } }, + + /** + * Method: getHeader + * + * Parameters: + * name - {String} Header name + * + * Returns: + * {?} - response header for the given name + */ + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) { + return null + } + }, /** * Method: dispatchException @@ -468,17 +490,10 @@ OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, { * exception - {?} */ dispatchException: function(exception) { - if (this.options.onException) { - this.options.onException(this, exception); - } else { - // if we get here, Responders.dispatch('onException') will never - // be called. too bad. we should probably take out the Responders - // stuff anyway. - throw exception; - } + (this.options.onException || + OpenLayers.Ajax.emptyFunction)(this, exception); OpenLayers.Ajax.Responders.dispatch('onException', this, exception); } - }); /** @@ -488,6 +503,91 @@ OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, { OpenLayers.Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; +/** + * Class: OpenLayers.Ajax.Request + * + * Inherit: + * - + */ +OpenLayers.Ajax.Response = OpenLayers.Class({ + + /** + * Property: status + * + * {Integer} + */ + status: 0, + + + /** + * Property: statusText + * + * {String} + */ + statusText: '', + + /** + * Constructor: OpenLayers.Ajax.Response + * + * Parameters: + * request - {Object} + */ + initialize: function(request) { + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if ((readyState > 2 && + !(!!(window.attachEvent && !window.opera))) || + readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = transport.responseText == null ? + '' : String(transport.responseText); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = xml === undefined ? null : xml; + } + }, + + /** + * Method: getStatus + */ + getStatus: OpenLayers.Ajax.Request.prototype.getStatus, + + /** + * Method: getStatustext + * + * Returns: + * {String} - statusText + */ + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { + return ''; + } + }, + + /** + * Method: getHeader + */ + getHeader: OpenLayers.Ajax.Request.prototype.getHeader, + + /** + * Method: getResponseHeader + * + * Returns: + * {?} - response header for given name + */ + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + } +}); + + /** * Function: getElementsByTagNameNS * diff --git a/tests/manual/ajax.html b/tests/manual/ajax.html new file mode 100644 index 0000000000..ff1c8f41c2 --- /dev/null +++ b/tests/manual/ajax.html @@ -0,0 +1,70 @@ + + + Draw Feature Acceptance Test + + + + + + +
+
+
+
+
+ +
+

+

Clicking on the different buttons should give the following results in the textarea below :

+
    +
  • "other processing" then "request completed"
  • +
  • "request completed" then "other processing"
  • +
  • "request aborted" then "other processing"
  • +
+ + diff --git a/tests/manual/ajax.txt b/tests/manual/ajax.txt new file mode 100644 index 0000000000..b10a4275f5 --- /dev/null +++ b/tests/manual/ajax.txt @@ -0,0 +1 @@ +one fake text file \ No newline at end of file