1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2 * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3 * full text of the license. */
6 * @requires OpenLayers/Protocol.js
7 * @requires OpenLayers/Feature/Vector.js
11 * Class: OpenLayers.Protocol.HTTP
12 * A basic HTTP protocol for vector layers. Create a new instance with the
13 * <OpenLayers.Protocol.HTTP> constructor.
16 * - <OpenLayers.Protocol>
18 OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
22 * {String} Service URL, read-only, set through the options
23 * passed to constructor.
29 * {Object} HTTP request headers, read-only, set through the options
30 * passed to the constructor,
31 * Example: {'Content-Type': 'plain/text'}
37 * {Object} Parameters of GET requests, read-only, set through the options
38 * passed to the constructor,
39 * Example: {'bbox': '5,5,5,5'}
45 * {Object} Function to be called when the <read>, <create>,
46 * <update>, <delete> or <commit> operation completes, read-only,
47 * set through the options passed to the constructor.
53 * {Object} Callback execution scope, read-only, set through the
54 * options passed to the constructor.
59 * Property: readWithPOST
60 * {Boolean} true if read operations are done with POST requests
61 * instead of GET, defaults to false.
66 * Constructor: OpenLayers.Protocol.HTTP
67 * A class for giving layers generic HTTP protocol.
70 * options - {Object} Optional object whose properties will be set on the
73 * Valid options include:
77 * format - {<OpenLayers.Format>}
78 * callback - {Function}
81 initialize: function(options) {
84 OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
89 * Clean up the protocol.
94 OpenLayers.Protocol.prototype.destroy.apply(this);
98 * Method: createCallback
99 * Returns a function that applies the given public method with resp and
103 * method - {Function} The method to be applied by the callback.
104 * response - {<OpenLayers.Protocol.Response>} The protocol response object.
105 * options - {Object} Options sent to the protocol method (read, create,
106 * update, or delete).
108 createCallback: function(method, response, options) {
109 return OpenLayers.Function.bind(function() {
110 method.apply(this, [response, options]);
116 * Construct a request for reading new features.
119 * options - {Object} Optional object for configuring the request.
120 * This object is modified and should not be reused.
123 * url - {String} Url for the request.
124 * params - {Object} Parameters to get serialized as a query string.
125 * headers - {Object} Headers to be set on the request.
126 * filter - {<OpenLayers.Filter.BBOX>} If a bbox filter is sent, it will be
127 * serialized according to the OpenSearch Geo extension
128 * (bbox=minx,miny,maxx,maxy). Note that a BBOX filter as the child
129 * of a logical filter will not be serialized.
130 * readWithPOST - {Boolean} If the request should be done with POST.
133 * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
134 * references the HTTP request, this object is also passed to the
135 * callback function when the request completes, its "features" property
136 * is then populated with the the features received from the server.
138 read: function(options) {
139 options = OpenLayers.Util.applyDefaults(options, this.options);
140 var readWithPOST = (options.readWithPOST !== undefined) ?
141 options.readWithPOST : this.readWithPOST;
142 var resp = new OpenLayers.Protocol.Response({requestType: "read"});
144 if(options.filter && options.filter instanceof OpenLayers.Filter.Spatial) {
145 if(options.filter.type == OpenLayers.Filter.Spatial.BBOX) {
146 options.params = OpenLayers.Util.extend(options.params, {
147 bbox: options.filter.value.toArray()
153 resp.priv = OpenLayers.Request.POST({
155 callback: this.createCallback(this.handleRead, resp, options),
156 data: OpenLayers.Util.getParameterString(options.params),
158 "Content-Type": "application/x-www-form-urlencoded"
162 resp.priv = OpenLayers.Request.GET({
164 callback: this.createCallback(this.handleRead, resp, options),
165 params: options.params,
166 headers: options.headers
175 * Individual callbacks are created for read, create and update, should
176 * a subclass need to override each one separately.
179 * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
181 * options - {Object} The user options passed to the read call.
183 handleRead: function(resp, options) {
184 this.handleResponse(resp, options);
189 * Construct a request for writing newly created features.
192 * features - {Array({<OpenLayers.Feature.Vector>})} or
193 * {<OpenLayers.Feature.Vector>}
194 * options - {Object} Optional object for configuring the request.
195 * This object is modified and should not be reused.
198 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
199 * object, whose "priv" property references the HTTP request, this
200 * object is also passed to the callback function when the request
201 * completes, its "features" property is then populated with the
202 * the features received from the server.
204 create: function(features, options) {
205 options = OpenLayers.Util.applyDefaults(options, this.options);
207 var resp = new OpenLayers.Protocol.Response({
208 reqFeatures: features,
209 requestType: "create"
212 resp.priv = OpenLayers.Request.POST({
214 callback: this.createCallback(this.handleCreate, resp, options),
215 headers: options.headers,
216 data: this.format.write(features)
223 * Method: handleCreate
224 * Called the the request issued by <create> is complete. May be overridden
228 * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
230 * options - {Object} The user options passed to the create call.
232 handleCreate: function(resp, options) {
233 this.handleResponse(resp, options);
238 * Construct a request updating modified feature.
241 * feature - {<OpenLayers.Feature.Vector>}
242 * options - {Object} Optional object for configuring the request.
243 * This object is modified and should not be reused.
246 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
247 * object, whose "priv" property references the HTTP request, this
248 * object is also passed to the callback function when the request
249 * completes, its "features" property is then populated with the
250 * the feature received from the server.
252 update: function(feature, options) {
253 var url = options.url || feature.url || this.options.url;
254 options = OpenLayers.Util.applyDefaults(options, this.options);
256 var resp = new OpenLayers.Protocol.Response({
257 reqFeatures: feature,
258 requestType: "update"
261 resp.priv = OpenLayers.Request.PUT({
263 callback: this.createCallback(this.handleUpdate, resp, options),
264 headers: options.headers,
265 data: this.format.write(feature)
272 * Method: handleUpdate
273 * Called the the request issued by <update> is complete. May be overridden
277 * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
279 * options - {Object} The user options passed to the update call.
281 handleUpdate: function(resp, options) {
282 this.handleResponse(resp, options);
287 * Construct a request deleting a removed feature.
290 * feature - {<OpenLayers.Feature.Vector>}
291 * options - {Object} Optional object for configuring the request.
292 * This object is modified and should not be reused.
295 * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
296 * object, whose "priv" property references the HTTP request, this
297 * object is also passed to the callback function when the request
300 "delete": function(feature, options) {
301 var url = options.url || feature.url || this.options.url;
302 options = OpenLayers.Util.applyDefaults(options, this.options);
304 var resp = new OpenLayers.Protocol.Response({
305 reqFeatures: feature,
306 requestType: "delete"
309 resp.priv = OpenLayers.Request.DELETE({
311 callback: this.createCallback(this.handleDelete, resp, options),
312 headers: options.headers
319 * Method: handleDelete
320 * Called the the request issued by <delete> is complete. May be overridden
324 * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
326 * options - {Object} The user options passed to the delete call.
328 handleDelete: function(resp, options) {
329 this.handleResponse(resp, options);
333 * Method: handleResponse
334 * Called by CRUD specific handlers.
337 * resp - {<OpenLayers.Protocol.Response>} The response object to pass to
339 * options - {Object} The user options passed to the create, read, update,
342 handleResponse: function(resp, options) {
343 var request = resp.priv;
344 if(options.callback) {
345 if(request.status >= 200 && request.status < 300) {
347 if(resp.requestType != "delete") {
348 resp.features = this.parseFeatures(request);
350 resp.code = OpenLayers.Protocol.Response.SUCCESS;
353 resp.code = OpenLayers.Protocol.Response.FAILURE;
355 options.callback.call(options.scope, resp);
360 * Method: parseFeatures
361 * Read HTTP response body and return features.
364 * request - {XMLHttpRequest} The request object
367 * {Array({<OpenLayers.Feature.Vector>})} or
368 * {<OpenLayers.Feature.Vector>} Array of features or a single feature.
370 parseFeatures: function(request) {
371 var doc = request.responseXML;
372 if (!doc || !doc.documentElement) {
373 doc = request.responseText;
375 if (!doc || doc.length <= 0) {
378 return this.format.read(doc);
383 * Iterate over each feature and take action based on the feature state.
384 * Possible actions are create, update and delete.
387 * features - {Array({<OpenLayers.Feature.Vector>})}
388 * options - {Object} Optional object for setting up intermediate commit
392 * create - {Object} Optional object to be passed to the <create> method.
393 * update - {Object} Optional object to be passed to the <update> method.
394 * delete - {Object} Optional object to be passed to the <delete> method.
395 * callback - {Function} Optional function to be called when the commit
397 * scope - {Object} Optional object to be set as the scope of the callback.
400 * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
401 * one per request made to the server, each object's "priv" property
402 * references the corresponding HTTP request.
404 commit: function(features, options) {
405 options = OpenLayers.Util.applyDefaults(options, this.options);
406 var resp = [], nResponses = 0;
408 // Divide up features before issuing any requests. This properly
409 // counts requests in the event that any responses come in before
410 // all requests have been issued.
412 types[OpenLayers.State.INSERT] = [];
413 types[OpenLayers.State.UPDATE] = [];
414 types[OpenLayers.State.DELETE] = [];
415 var feature, list, requestFeatures = [];
416 for(var i=0, len=features.length; i<len; ++i) {
417 feature = features[i];
418 list = types[feature.state];
421 requestFeatures.push(feature);
424 // tally up number of requests
425 var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
426 types[OpenLayers.State.UPDATE].length +
427 types[OpenLayers.State.DELETE].length;
429 // This response will be sent to the final callback after all the others
432 var finalResponse = new OpenLayers.Protocol.Response({
433 reqFeatures: requestFeatures
436 function insertCallback(response) {
437 var len = response.features ? response.features.length : 0;
438 var fids = new Array(len);
439 for(var i=0; i<len; ++i) {
440 fids[i] = response.features[i].fid;
442 finalResponse.insertIds = fids;
443 callback.apply(this, [response]);
446 function callback(response) {
447 this.callUserCallback(response, options);
448 success = success && response.success();
450 if (nResponses >= nRequests) {
451 if (options.callback) {
452 finalResponse.code = success ?
453 OpenLayers.Protocol.Response.SUCCESS :
454 OpenLayers.Protocol.Response.FAILURE;
455 options.callback.apply(options.scope, [finalResponse]);
460 // start issuing requests
461 var queue = types[OpenLayers.State.INSERT];
462 if(queue.length > 0) {
463 resp.push(this.create(
464 queue, OpenLayers.Util.applyDefaults(
465 {callback: insertCallback, scope: this}, options.create
469 queue = types[OpenLayers.State.UPDATE];
470 for(var i=queue.length-1; i>=0; --i) {
471 resp.push(this.update(
472 queue[i], OpenLayers.Util.applyDefaults(
473 {callback: callback, scope: this}, options.update
477 queue = types[OpenLayers.State.DELETE];
478 for(var i=queue.length-1; i>=0; --i) {
479 resp.push(this["delete"](
480 queue[i], OpenLayers.Util.applyDefaults(
481 {callback: callback, scope: this}, options["delete"]
490 * Abort an ongoing request, the response object passed to
491 * this method must come from this HTTP protocol (as a result
492 * of a create, read, update, delete or commit operation).
495 * response - {<OpenLayers.Protocol.Response>}
497 abort: function(response) {
499 response.priv.abort();
504 * Method: callUserCallback
505 * This method is used from within the commit method each time an
506 * an HTTP response is received from the server, it is responsible
507 * for calling the user-supplied callbacks.
510 * resp - {<OpenLayers.Protocol.Response>}
511 * options - {Object} The map of options passed to the commit call.
513 callUserCallback: function(resp, options) {
514 var opt = options[resp.requestType];
515 if(opt && opt.callback) {
516 opt.callback.call(opt.scope, resp);
520 CLASS_NAME: "OpenLayers.Protocol.HTTP"