diff --git a/examples/behavior-fixed-http-gml.html b/examples/behavior-fixed-http-gml.html new file mode 100644 index 0000000000..d5fb898923 --- /dev/null +++ b/examples/behavior-fixed-http-gml.html @@ -0,0 +1,49 @@ + + + OpenLayers Vector Behavior Example + + + + + + +

Vector Behavior Example (Fixed/HTTP/GML)

+

+ Vector layer with a Fixed strategy, HTTP protocol, and GML format. +

+
+
+ The vector layer shown uses the Fixed strategy, the HTTP protocol, + and the GML format. + The Fixed strategy is a simple strategy that fetches features once + and never re-requests new data. + The HTTP protocol makes requests using HTTP verbs. It should be + constructed with a url that corresponds to a collection of features + (a resource on some server). + The GML format is used to serialize features. +
+ + diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 5b890ab7f0..70e91bc64d 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -184,6 +184,7 @@ "OpenLayers/Strategy.js", "OpenLayers/Strategy/Fixed.js", "OpenLayers/Protocol.js", + "OpenLayers/Protocol/HTTP.js", "OpenLayers/Layer/PointTrack.js", "OpenLayers/Layer/GML.js", "OpenLayers/Style.js", diff --git a/lib/OpenLayers/Protocol/HTTP.js b/lib/OpenLayers/Protocol/HTTP.js new file mode 100644 index 0000000000..76efb06486 --- /dev/null +++ b/lib/OpenLayers/Protocol/HTTP.js @@ -0,0 +1,454 @@ +/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD + * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Protocol.js + * @requires OpenLayers/Feature/Vector.js + */ + +/** + * Class: OpenLayers.Protocol.HTTP + * A basic HTTP protocol for vector layers. Create a new instance with the + * constructor. + * + * Inherits from: + * - + */ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { + + /** + * Property: url + * {String} - Service URL, read-only, set through the options + * passed to constructor. + */ + url: null, + + /** + * Property: headers + * {Object} - HTTP request headers, read-only, set through the options + * passed to the constructor, + * Example: {'Content-Type': 'plain/text'} + */ + headers: null, + + /** + * Property: params + * {Object} - Parameters of GET requests, read-only, set through the options + * passed to the constructor, + * Example: {'bbox': '5,5,5,5'} + */ + params: null, + + /** + * Property: format + * {} Parser for reading and writing features. + */ + format: null, + + /** + * Property: callback + * {Object} - Function to be called when the , , + * , or operation completes, read-only, + * set through the options passed to the constructor. + */ + callback: null, + + /** + * Property: scope + * {Object} - Callback execution scope, read-only, set through the + * options passed to the constructor. + */ + scope: null, + + /** + * Constructor: OpenLayers.Protocol.HTTP + * A class for giving layers generic HTTP protocol. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on the + * instance. + * + * Valid options include: + * url - {String} + * headers - {Object} + * params - {Object} + * format - {} + * callback - {Function} + * scope - {Object} + */ + initialize: function(options) { + this.params = {}; + this.headers = {}; + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); + }, + + /** + * APIMethod: destroy + * Clean up the protocol. + */ + destroy: function() { + this.params = null; + this.headers = null; + OpenLayers.Protocol.prototype.destroy.apply(this); + }, + + /** + * Method: createCallback + * Returns a function that applies the given public method with resp and + * options arguments. + * + * Parameters: + * method - {Function} The method to be applied by the callback. + * response - {} The protocol response object. + * options - {Object} Options sent to the protocol method (read, create, + * update, or delete). + */ + createCallback: function(method, response, options) { + return OpenLayers.Function.bind(function() { + method.apply(this, [response, options]); + }, this); + }, + + /** + * Method: read + * Construct a request for reading new features. + * + * Parameters: + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} A response object, whose "priv" property + * references the HTTP request, this object is also passed to the + * callback function when the request completes, its "features" property + * is then populated with the the features received from the server. + */ + read: function(options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + var resp = new OpenLayers.Protocol.Response({requestType: "read"}); + + resp.priv = OpenLayers.Request.GET({ + url: options.url, + callback: this.createCallback(this.handleRead, resp, options), + params: options.params, + headers: options.headers + }); + + return resp; + }, + + /** + * Method: handleRead + * Individual callbacks are created for read, create and update, should + * a subclass need to override each one separately. + * + * Parameters: + * resp - {} The response object to pass to + * the user callback. + * options - {Object} The user options passed to the read call. + */ + handleRead: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: create + * Construct a request for writing newly created features. + * + * Parameters: + * features - {Array({})} or + * {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes, its "features" property is then populated with the + * the features received from the server. + */ + create: function(features, options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: features, + requestType: "create" + }); + + resp.priv = OpenLayers.Request.POST({ + url: options.url, + callback: this.createCallback(this.handleCreate, resp, options), + headers: options.headers, + data: this.format.write(features) + }); + + return resp; + }, + + /** + * Method: handleCreate + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the create call. + */ + handleCreate: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: update + * Construct a request updating modified feature. + * + * Parameters: + * feature - {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes, its "features" property is then populated with the + * the feature received from the server. + */ + update: function(feature, options) { + var url = options.url || feature.url || this.options.url; + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: feature, + requestType: "update" + }); + + resp.priv = OpenLayers.Request.PUT({ + url: url, + callback: this.createCallback(this.handleUpdate, resp, options), + headers: options.headers, + data: this.format.write(feature) + }); + + return resp; + }, + + /** + * Method: handleUpdate + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the update call. + */ + handleUpdate: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: delete + * Construct a request deleting a removed feature. + * + * Parameters: + * feature - {} + * options - {Object} Optional object for configuring the request. + * This object is modified and should not be reused. + * + * Returns: + * {} An + * object, whose "priv" property references the HTTP request, this + * object is also passed to the callback function when the request + * completes. + */ + "delete": function(feature, options) { + var url = options.url || feature.url || this.options.url; + options = OpenLayers.Util.applyDefaults(options, this.options); + + var resp = new OpenLayers.Protocol.Response({ + reqFeatures: feature, + requestType: "delete" + }); + + resp.priv = OpenLayers.Request.DELETE({ + url: url, + callback: this.createCallback(this.handleDelete, resp, options), + headers: options.headers + }); + + return resp; + }, + + /** + * Method: handleDelete + * Called the the request issued by is complete. May be overridden + * by subclasses. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the delete call. + */ + handleDelete: function(resp, options) { + this.handleResponse(resp, options); + }, + + /** + * Method: handleResponse + * Called by CRUD specific handlers. + * + * Parameters: + * resp - {} The response object to pass to + * any user callback. + * options - {Object} The user options passed to the create, read, update, + * or delete call. + */ + handleResponse: function(resp, options) { + var request = resp.priv; + if(options.callback) { + if(request.status >= 200 && request.status < 300) { + // success + if(resp.requestType != "delete") { + resp.features = this.parseFeatures(request); + } + resp.code = OpenLayers.Protocol.Response.SUCCESS; + } else { + // failure + resp.code = OpenLayers.Protocol.Response.FAILURE; + } + options.callback.call(options.scope, resp); + } + }, + + /** + * Method: parseFeatures + * Read HTTP response body and return features. + * + * Parameters: + * request - {XMLHttpRequest} The request object + * + * Returns: + * {Array({})} or + * {} Array of features or a single feature. + */ + parseFeatures: function(request) { + var doc = request.responseXML; + if (!doc || !doc.documentElement) { + doc = request.responseText; + } + if (!doc || doc.length <= 0) { + return null; + } + return this.format.read(doc); + }, + + /** + * Method: commit + * Iterate over each feature and take action based on the feature state. + * Possible actions are create, update and delete. + * + * Parameters: + * features - {Array({})} + * options - {Object} Optional object for setting up intermediate commit + * callbacks. + * + * Valid options: + * create - {Object} Optional object to be passed to the method. + * update - {Object} Optional object to be passed to the method. + * delete - {Object} Optional object to be passed to the method. + * callback - {Function} Optional function to be called when the commit + * is complete. + * scope - {Object} Optional object to be set as the scope of the callback. + * + * Returns: + * {Array()} An array of response objects, + * one per request made to the server, each object's "priv" property + * references the corresponding HTTP request. + */ + commit: function(features, options) { + options = OpenLayers.Util.applyDefaults(options, this.options); + var resp = [], nResponses = 0; + + // Divide up features before issuing any requests. This properly + // counts requests in the event that any responses come in before + // all requests have been issued. + var types = {}; + types[OpenLayers.State.INSERT] = []; + types[OpenLayers.State.UPDATE] = []; + types[OpenLayers.State.DELETE] = []; + var feature, list; + for(var i=0, len=features.length; i 0 ? 1 : 0) + + types[OpenLayers.State.UPDATE].length + + types[OpenLayers.State.DELETE].length; + + function callback(response) { + nResponses++; + response.last = (nResponses >= nRequests); + this.callUserCallback(response, options); + } + + // start issuing requests + var queue = types[OpenLayers.State.INSERT]; + if(queue.length > 0) { + resp.push(this.create( + queue, OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options.create || {} // remove || when #1716 is resolved + ) + )); + } + queue = types[OpenLayers.State.UPDATE]; + for(var i=queue.length-1; i>=0; --i) { + resp.push(this.update( + queue[i], OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options.update || {} // remove || when #1716 is resolved + )) + ); + } + queue = types[OpenLayers.State.DELETE]; + for(var i=queue.length-1; i>=0; --i) { + resp.push(this["delete"]( + queue[i], OpenLayers.Util.applyDefaults( + {callback: callback, scope: this}, + options["delete"] || {} // remove || when #1716 is resolved + )) + ); + } + return resp; + }, + + /** + * Method: callUserCallback + * This method is used from within the commit method each time an + * an HTTP response is received from the server, it is responsible + * for calling the user-supplied callbacks. + * + * Parameters: + * resp - {} + * options - {Object} The map of options passed to the commit call. + */ + callUserCallback: function(resp, options) { + var opt = options[resp.requestType]; + if(opt && opt.callback) { + opt.callback.call(opt.scope, resp); + } + if(resp.last && options.callback) { + options.callback.call(options.scope); + } + }, + + CLASS_NAME: "OpenLayers.Protocol.HTTP" +}); diff --git a/tests/Protocol/HTTP.html b/tests/Protocol/HTTP.html new file mode 100644 index 0000000000..56a27b4476 --- /dev/null +++ b/tests/Protocol/HTTP.html @@ -0,0 +1,669 @@ + + + + + + + + diff --git a/tests/list-tests.html b/tests/list-tests.html index 9c68c3dbe0..adf2aaf8e8 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -113,6 +113,7 @@
  • Popup/FramedCloud.html
  • Projection.html
  • Protocol.html
  • +
  • Protocol/HTTP.html
  • Renderer.html
  • Renderer/Canvas.html
  • Renderer/Elements.html