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
8
* @requires OpenLayers/Filter/Spatial.js
9
* @requires OpenLayers/Filter/Comparison.js
10
* @requires OpenLayers/Filter/Logical.js
14
* Class: OpenLayers.Protocol.HTTP
15
* A basic HTTP protocol for vector layers. Create a new instance with the
16
* <OpenLayers.Protocol.HTTP> constructor.
19
* - <OpenLayers.Protocol>
21
OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
25
* {String} Service URL, read-only, set through the options
26
* passed to constructor.
32
* {Object} HTTP request headers, read-only, set through the options
33
* passed to the constructor,
34
* Example: {'Content-Type': 'plain/text'}
40
* {Object} Parameters of GET requests, read-only, set through the options
41
* passed to the constructor,
42
* Example: {'bbox': '5,5,5,5'}
48
* {Object} Function to be called when the <read>, <create>,
49
* <update>, <delete> or <commit> operation completes, read-only,
50
* set through the options passed to the constructor.
56
* {Object} Callback execution scope, read-only, set through the
57
* options passed to the constructor.
62
* Property: readWithPOST
63
* {Boolean} true if read operations are done with POST requests
64
* instead of GET, defaults to false.
69
* Property: wildcarded.
70
* {Boolean} If true percent signs are added around values
71
* read from LIKE filters, for example if the protocol
72
* read method is passed a LIKE filter whose property
73
* is "foo" and whose value is "bar" the string
74
* "foo__ilike=%bar%" will be sent in the query string;
80
* Constructor: OpenLayers.Protocol.HTTP
81
* A class for giving layers generic HTTP protocol.
84
* options - {Object} Optional object whose properties will be set on the
87
* Valid options include:
91
* format - {<OpenLayers.Format>}
92
* callback - {Function}
95
initialize: function(options) {
96
options = options || {};
99
OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
104
* Clean up the protocol.
106
destroy: function() {
109
OpenLayers.Protocol.prototype.destroy.apply(this);
114
* Construct a request for reading new features.
117
* options - {Object} Optional object for configuring the request.
118
* This object is modified and should not be reused.
121
* url - {String} Url for the request.
122
* params - {Object} Parameters to get serialized as a query string.
123
* headers - {Object} Headers to be set on the request.
124
* filter - {<OpenLayers.Filter>} Filter to get serialized as a
126
* readWithPOST - {Boolean} If the request should be done with POST.
129
* {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
130
* references the HTTP request, this object is also passed to the
131
* callback function when the request completes, its "features" property
132
* is then populated with the the features received from the server.
134
read: function(options) {
135
OpenLayers.Protocol.prototype.read.apply(this, arguments);
136
options = OpenLayers.Util.applyDefaults(options, this.options);
137
options.params = OpenLayers.Util.applyDefaults(
138
options.params, this.options.params);
140
options.params = this.filterToParams(
141
options.filter, options.params);
143
var readWithPOST = (options.readWithPOST !== undefined) ?
144
options.readWithPOST : this.readWithPOST;
145
var resp = new OpenLayers.Protocol.Response({requestType: "read"});
147
resp.priv = OpenLayers.Request.POST({
149
callback: this.createCallback(this.handleRead, resp, options),
150
data: OpenLayers.Util.getParameterString(options.params),
152
"Content-Type": "application/x-www-form-urlencoded"
156
resp.priv = OpenLayers.Request.GET({
158
callback: this.createCallback(this.handleRead, resp, options),
159
params: options.params,
160
headers: options.headers
168
* Individual callbacks are created for read, create and update, should
169
* a subclass need to override each one separately.
172
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
174
* options - {Object} The user options passed to the read call.
176
handleRead: function(resp, options) {
177
this.handleResponse(resp, options);
181
* Method: filterToParams
182
* Convert an <OpenLayers.Filter> object to parameters.
185
* filter - {OpenLayers.Filter} filter to convert.
186
* params - {Object} The parameters object.
189
* {Object} The resulting parameters object.
191
filterToParams: function(filter, params) {
192
params = params || {};
193
var className = filter.CLASS_NAME;
194
var filterType = className.substring(className.lastIndexOf(".") + 1);
197
switch(filter.type) {
198
case OpenLayers.Filter.Spatial.BBOX:
199
params.bbox = filter.value.toArray();
201
case OpenLayers.Filter.Spatial.DWITHIN:
202
params.tolerance = filter.distance;
204
case OpenLayers.Filter.Spatial.WITHIN:
205
params.lon = filter.value.x;
206
params.lat = filter.value.y;
209
OpenLayers.Console.warn(
210
"Unknown spatial filter type " + filter.type);
214
var op = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR[filter.type];
215
if(op !== undefined) {
216
var value = filter.value;
217
if(filter.type == OpenLayers.Filter.Comparison.LIKE) {
218
value = this.regex2value(value);
219
if(this.wildcarded) {
220
value = "%" + value + "%";
223
params[filter.property + "__" + op] = value;
224
params.queryable = params.queryable || [];
225
params.queryable.push(filter.property);
227
OpenLayers.Console.warn(
228
"Unknown comparison filter type " + filter.type);
232
if(filter.type === OpenLayers.Filter.Logical.AND) {
233
for(var i=0,len=filter.filters.length; i<len; i++) {
234
params = this.filterToParams(filter.filters[i], params);
237
OpenLayers.Console.warn(
238
"Unsupported logical filter type " + filter.type);
242
OpenLayers.Console.warn("Unknown filter type " + filterType);
248
* Method: regex2value
249
* Convert the value from a regular expression string to a LIKE/ILIKE
250
* string known to the web service.
253
* value - {String} The regex string.
256
* {String} The converted string.
258
regex2value: function(value) {
260
// highly sensitive!! Do not change this without running the
261
// Protocol/HTTP.html unit tests
264
value = value.replace(/%/g, "\\%");
266
// convert \\. to \\_ (\\.* occurences converted later)
267
value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
268
return $1 ? $0 : "\\\\_";
271
// convert \\.* to \\%
272
value = value.replace(/\\\\\.\*/g, "\\\\%");
274
// convert . to _ (\. and .* occurences converted later)
275
value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
276
return $1 || $2 ? $0 : "_";
279
// convert .* to % (\.* occurnces converted later)
280
value = value.replace(/(\\)?\.\*/g, function($0, $1) {
281
return $1 ? $0 : "%";
285
value = value.replace(/\\\./g, ".");
287
// replace \* with * (watching out for \\*)
288
value = value.replace(/(\\)?\\\*/g, function($0, $1) {
289
return $1 ? $0 : "*";
297
* Construct a request for writing newly created features.
300
* features - {Array({<OpenLayers.Feature.Vector>})} or
301
* {<OpenLayers.Feature.Vector>}
302
* options - {Object} Optional object for configuring the request.
303
* This object is modified and should not be reused.
306
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
307
* object, whose "priv" property references the HTTP request, this
308
* object is also passed to the callback function when the request
309
* completes, its "features" property is then populated with the
310
* the features received from the server.
312
create: function(features, options) {
313
options = OpenLayers.Util.applyDefaults(options, this.options);
315
var resp = new OpenLayers.Protocol.Response({
316
reqFeatures: features,
317
requestType: "create"
320
resp.priv = OpenLayers.Request.POST({
322
callback: this.createCallback(this.handleCreate, resp, options),
323
headers: options.headers,
324
data: this.format.write(features)
331
* Method: handleCreate
332
* Called the the request issued by <create> is complete. May be overridden
336
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
338
* options - {Object} The user options passed to the create call.
340
handleCreate: function(resp, options) {
341
this.handleResponse(resp, options);
346
* Construct a request updating modified feature.
349
* feature - {<OpenLayers.Feature.Vector>}
350
* options - {Object} Optional object for configuring the request.
351
* This object is modified and should not be reused.
354
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
355
* object, whose "priv" property references the HTTP request, this
356
* object is also passed to the callback function when the request
357
* completes, its "features" property is then populated with the
358
* the feature received from the server.
360
update: function(feature, options) {
361
options = options || {};
362
var url = options.url ||
364
this.options.url + "/" + feature.fid;
365
options = OpenLayers.Util.applyDefaults(options, this.options);
367
var resp = new OpenLayers.Protocol.Response({
368
reqFeatures: feature,
369
requestType: "update"
372
resp.priv = OpenLayers.Request.PUT({
374
callback: this.createCallback(this.handleUpdate, resp, options),
375
headers: options.headers,
376
data: this.format.write(feature)
383
* Method: handleUpdate
384
* Called the the request issued by <update> is complete. May be overridden
388
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
390
* options - {Object} The user options passed to the update call.
392
handleUpdate: function(resp, options) {
393
this.handleResponse(resp, options);
398
* Construct a request deleting a removed feature.
401
* feature - {<OpenLayers.Feature.Vector>}
402
* options - {Object} Optional object for configuring the request.
403
* This object is modified and should not be reused.
406
* {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
407
* object, whose "priv" property references the HTTP request, this
408
* object is also passed to the callback function when the request
411
"delete": function(feature, options) {
412
options = options || {};
413
var url = options.url ||
415
this.options.url + "/" + feature.fid;
416
options = OpenLayers.Util.applyDefaults(options, this.options);
418
var resp = new OpenLayers.Protocol.Response({
419
reqFeatures: feature,
420
requestType: "delete"
423
resp.priv = OpenLayers.Request.DELETE({
425
callback: this.createCallback(this.handleDelete, resp, options),
426
headers: options.headers
433
* Method: handleDelete
434
* Called the the request issued by <delete> is complete. May be overridden
438
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
440
* options - {Object} The user options passed to the delete call.
442
handleDelete: function(resp, options) {
443
this.handleResponse(resp, options);
447
* Method: handleResponse
448
* Called by CRUD specific handlers.
451
* resp - {<OpenLayers.Protocol.Response>} The response object to pass to
453
* options - {Object} The user options passed to the create, read, update,
456
handleResponse: function(resp, options) {
457
var request = resp.priv;
458
if(options.callback) {
459
if(request.status >= 200 && request.status < 300) {
461
if(resp.requestType != "delete") {
462
resp.features = this.parseFeatures(request);
464
resp.code = OpenLayers.Protocol.Response.SUCCESS;
467
resp.code = OpenLayers.Protocol.Response.FAILURE;
469
options.callback.call(options.scope, resp);
474
* Method: parseFeatures
475
* Read HTTP response body and return features.
478
* request - {XMLHttpRequest} The request object
481
* {Array({<OpenLayers.Feature.Vector>})} or
482
* {<OpenLayers.Feature.Vector>} Array of features or a single feature.
484
parseFeatures: function(request) {
485
var doc = request.responseXML;
486
if (!doc || !doc.documentElement) {
487
doc = request.responseText;
489
if (!doc || doc.length <= 0) {
492
return this.format.read(doc);
497
* Iterate over each feature and take action based on the feature state.
498
* Possible actions are create, update and delete.
501
* features - {Array({<OpenLayers.Feature.Vector>})}
502
* options - {Object} Optional object for setting up intermediate commit
506
* create - {Object} Optional object to be passed to the <create> method.
507
* update - {Object} Optional object to be passed to the <update> method.
508
* delete - {Object} Optional object to be passed to the <delete> method.
509
* callback - {Function} Optional function to be called when the commit
511
* scope - {Object} Optional object to be set as the scope of the callback.
514
* {Array(<OpenLayers.Protocol.Response>)} An array of response objects,
515
* one per request made to the server, each object's "priv" property
516
* references the corresponding HTTP request.
518
commit: function(features, options) {
519
options = OpenLayers.Util.applyDefaults(options, this.options);
520
var resp = [], nResponses = 0;
522
// Divide up features before issuing any requests. This properly
523
// counts requests in the event that any responses come in before
524
// all requests have been issued.
526
types[OpenLayers.State.INSERT] = [];
527
types[OpenLayers.State.UPDATE] = [];
528
types[OpenLayers.State.DELETE] = [];
529
var feature, list, requestFeatures = [];
530
for(var i=0, len=features.length; i<len; ++i) {
531
feature = features[i];
532
list = types[feature.state];
535
requestFeatures.push(feature);
538
// tally up number of requests
539
var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
540
types[OpenLayers.State.UPDATE].length +
541
types[OpenLayers.State.DELETE].length;
543
// This response will be sent to the final callback after all the others
546
var finalResponse = new OpenLayers.Protocol.Response({
547
reqFeatures: requestFeatures
550
function insertCallback(response) {
551
var len = response.features ? response.features.length : 0;
552
var fids = new Array(len);
553
for(var i=0; i<len; ++i) {
554
fids[i] = response.features[i].fid;
556
finalResponse.insertIds = fids;
557
callback.apply(this, [response]);
560
function callback(response) {
561
this.callUserCallback(response, options);
562
success = success && response.success();
564
if (nResponses >= nRequests) {
565
if (options.callback) {
566
finalResponse.code = success ?
567
OpenLayers.Protocol.Response.SUCCESS :
568
OpenLayers.Protocol.Response.FAILURE;
569
options.callback.apply(options.scope, [finalResponse]);
574
// start issuing requests
575
var queue = types[OpenLayers.State.INSERT];
576
if(queue.length > 0) {
577
resp.push(this.create(
578
queue, OpenLayers.Util.applyDefaults(
579
{callback: insertCallback, scope: this}, options.create
583
queue = types[OpenLayers.State.UPDATE];
584
for(var i=queue.length-1; i>=0; --i) {
585
resp.push(this.update(
586
queue[i], OpenLayers.Util.applyDefaults(
587
{callback: callback, scope: this}, options.update
591
queue = types[OpenLayers.State.DELETE];
592
for(var i=queue.length-1; i>=0; --i) {
593
resp.push(this["delete"](
594
queue[i], OpenLayers.Util.applyDefaults(
595
{callback: callback, scope: this}, options["delete"]
604
* Abort an ongoing request, the response object passed to
605
* this method must come from this HTTP protocol (as a result
606
* of a create, read, update, delete or commit operation).
609
* response - {<OpenLayers.Protocol.Response>}
611
abort: function(response) {
613
response.priv.abort();
618
* Method: callUserCallback
619
* This method is used from within the commit method each time an
620
* an HTTP response is received from the server, it is responsible
621
* for calling the user-supplied callbacks.
624
* resp - {<OpenLayers.Protocol.Response>}
625
* options - {Object} The map of options passed to the commit call.
627
callUserCallback: function(resp, options) {
628
var opt = options[resp.requestType];
629
if(opt && opt.callback) {
630
opt.callback.call(opt.scope, resp);
634
CLASS_NAME: "OpenLayers.Protocol.HTTP"
638
* Property: OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR
639
* {Object} A private class-level property mapping the
640
* OpenLayers.Filter.Comparison types to the operation
641
* strings of the protocol.
644
var o = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR = {};
645
o[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq";
646
o[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne";
647
o[OpenLayers.Filter.Comparison.LESS_THAN] = "lt";
648
o[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte";
649
o[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt";
650
o[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
651
o[OpenLayers.Filter.Comparison.LIKE] = "ilike";