diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 9783430dc1..5afa2b4200 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -245,6 +245,7 @@ "OpenLayers/Filter/Spatial.js", "OpenLayers/Protocol.js", "OpenLayers/Protocol/HTTP.js", + "OpenLayers/Protocol/SimpleFilterSerializer.js", "OpenLayers/Protocol/SQL.js", "OpenLayers/Protocol/SQL/Gears.js", "OpenLayers/Protocol/WFS.js", diff --git a/lib/OpenLayers/Protocol/HTTP.js b/lib/OpenLayers/Protocol/HTTP.js index d03c7d6367..14e1db8dfc 100644 --- a/lib/OpenLayers/Protocol/HTTP.js +++ b/lib/OpenLayers/Protocol/HTTP.js @@ -13,6 +13,11 @@ * @requires OpenLayers/Request/XMLHttpRequest.js */ +/** + * TODO: remove this dependency in 3.0 + * @requires OpenLayers/Protocol/SimpleFilterSerializer.js + */ + /** * Class: OpenLayers.Protocol.HTTP * A basic HTTP protocol for vector layers. Create a new instance with the @@ -109,6 +114,12 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { this.params = {}; this.headers = {}; OpenLayers.Protocol.prototype.initialize.apply(this, arguments); + + if (!this.filterToParams && OpenLayers.Protocol.simpleFilterSerializer) { + this.filterToParams = OpenLayers.Function.bind( + OpenLayers.Protocol.simpleFilterSerializer, this + ); + } }, /** @@ -120,7 +131,22 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { this.headers = null; OpenLayers.Protocol.prototype.destroy.apply(this); }, - + + /** + * APIMethod: filterToParams + * Optional method to translate an object into an object + * that can be serialized as request query string provided. If a custom + * method is not provided, the filter will be serialized using the + * method. + * + * Parameters: + * filter - {} filter to convert. + * params - {Object} The parameters object. + * + * Returns: + * {Object} The resulting parameters object. + */ + /** * APIMethod: read * Construct a request for reading new features. @@ -148,9 +174,10 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { options = OpenLayers.Util.applyDefaults(options, this.options); options.params = OpenLayers.Util.applyDefaults( options.params, this.options.params); - if(options.filter) { + if (options.filter && this.filterToParams) { options.params = this.filterToParams( - options.filter, options.params); + options.filter, options.params + ); } var readWithPOST = (options.readWithPOST !== undefined) ? options.readWithPOST : this.readWithPOST; @@ -189,124 +216,6 @@ OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { this.handleResponse(resp, options); }, - /** - * Method: filterToParams - * Convert an object to parameters. - * - * Parameters: - * filter - {OpenLayers.Filter} filter to convert. - * params - {Object} The parameters object. - * - * Returns: - * {Object} The resulting parameters object. - */ - filterToParams: function(filter, params) { - params = params || {}; - var className = filter.CLASS_NAME; - var filterType = className.substring(className.lastIndexOf(".") + 1); - switch(filterType) { - case "Spatial": - switch(filter.type) { - case OpenLayers.Filter.Spatial.BBOX: - params.bbox = filter.value.toArray(); - if (this.srsInBBOX && filter.projection) { - params.bbox.push(filter.projection.getCode()); - } - break; - case OpenLayers.Filter.Spatial.DWITHIN: - params.tolerance = filter.distance; - // no break here - case OpenLayers.Filter.Spatial.WITHIN: - params.lon = filter.value.x; - params.lat = filter.value.y; - break; - default: - OpenLayers.Console.warn( - "Unknown spatial filter type " + filter.type); - } - break; - case "Comparison": - var op = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR[filter.type]; - if(op !== undefined) { - var value = filter.value; - if(filter.type == OpenLayers.Filter.Comparison.LIKE) { - value = this.regex2value(value); - if(this.wildcarded) { - value = "%" + value + "%"; - } - } - params[filter.property + "__" + op] = value; - params.queryable = params.queryable || []; - params.queryable.push(filter.property); - } else { - OpenLayers.Console.warn( - "Unknown comparison filter type " + filter.type); - } - break; - case "Logical": - if(filter.type === OpenLayers.Filter.Logical.AND) { - for(var i=0,len=filter.filters.length; i objects using the "simple" filter syntax for + * query string parameters. This function must be called as a method of + * a protocol instance. + * + * Parameters: + * filter - {} filter to convert. + * params - {Object} The parameters object. + * + * Returns: + * {Object} The resulting parameters object. + */ +OpenLayers.Protocol.simpleFilterSerializer = (function() { + + /** + * Map the OpenLayers.Filter.Comparison types to the operation strings of + * the protocol. + */ + var cmpToStr = {}; + cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq"; + cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne"; + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt"; + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte"; + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt"; + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte"; + cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike"; + + /** + * Function: regex2value + * Convert the value from a regular expression string to a LIKE/ILIKE + * string known to the web service. + * + * Parameters: + * value - {String} The regex string. + * + * Returns: + * {String} The converted string. + */ + function regex2value(value) { + + // highly sensitive!! Do not change this without running the + // Protocol/HTTP.html unit tests + + // convert % to \% + value = value.replace(/%/g, "\\%"); + + // convert \\. to \\_ (\\.* occurences converted later) + value = value.replace(/\\\\\.(\*)?/g, function($0, $1) { + return $1 ? $0 : "\\\\_"; + }); + + // convert \\.* to \\% + value = value.replace(/\\\\\.\*/g, "\\\\%"); + + // convert . to _ (\. and .* occurences converted later) + value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) { + return $1 || $2 ? $0 : "_"; + }); + + // convert .* to % (\.* occurnces converted later) + value = value.replace(/(\\)?\.\*/g, function($0, $1) { + return $1 ? $0 : "%"; + }); + + // convert \. to . + value = value.replace(/\\\./g, "."); + + // replace \* with * (watching out for \\*) + value = value.replace(/(\\)?\\\*/g, function($0, $1) { + return $1 ? $0 : "*"; + }); + + return value; + } + + return function simpleFilterSerializer(filter, params) { + params = params || {}; + var className = filter.CLASS_NAME; + var filterType = className.substring(className.lastIndexOf(".") + 1); + switch (filterType) { + case "Spatial": + switch (filter.type) { + case OpenLayers.Filter.Spatial.BBOX: + params.bbox = filter.value.toArray(); + if (this.srsInBBOX && filter.projection) { + params.bbox.push(filter.projection.getCode()); + } + break; + case OpenLayers.Filter.Spatial.DWITHIN: + params.tolerance = filter.distance; + // no break here + case OpenLayers.Filter.Spatial.WITHIN: + params.lon = filter.value.x; + params.lat = filter.value.y; + break; + default: + OpenLayers.Console.warn( + "Unknown spatial filter type " + filter.type); + } + break; + case "Comparison": + var op = cmpToStr[filter.type]; + if (op !== undefined) { + var value = filter.value; + if (filter.type == OpenLayers.Filter.Comparison.LIKE) { + value = regex2value(value); + if (this.wildcarded) { + value = "%" + value + "%"; + } + } + params[filter.property + "__" + op] = value; + params.queryable = params.queryable || []; + params.queryable.push(filter.property); + } else { + OpenLayers.Console.warn( + "Unknown comparison filter type " + filter.type); + } + break; + case "Logical": + if (filter.type === OpenLayers.Filter.Logical.AND) { + for (var i=0,len=filter.filters.length; i + + + + + + + diff --git a/tests/list-tests.html b/tests/list-tests.html index 920527f82f..7c61f19736 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -177,6 +177,7 @@
  • Projection.html
  • Protocol.html
  • Protocol/HTTP.html
  • +
  • Protocol/SimpleFilterSerializer.html
  • Protocol/SQL.html
  • Protocol/SQL/Gears.html
  • Protocol/WFS.html