/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the 2-clause BSD license. * See license.txt in the OpenLayers distribution or repository for the * full text of the license. */ /** * @requires OpenLayers/Protocol.js * @requires OpenLayers/Feature/Vector.js * @requires OpenLayers/Format/GeoJSON.js */ /** * if application uses the query string, for example, for BBOX parameters, * OpenLayers/Format/QueryStringFilter.js should be included in the build config file */ /** * Class: OpenLayers.Protocol.Script * A basic Script protocol for vector layers. Create a new instance with the * constructor. A script protocol is used to * get around the same origin policy. It works with services that return * JSONP - that is, JSON wrapped in a client-specified callback. The * protocol handles fetching and parsing of feature data and sends parsed * features to the configured with the protocol. The protocol * expects features serialized as GeoJSON by default, but can be configured * to work with other formats by setting the property. * * Inherits from: * - */ OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, { /** * APIProperty: url * {String} Service URL. The service is expected to return serialized * features wrapped in a named callback (where the callback name is * generated by this protocol). * Read-only, set through the options passed to the constructor. */ url: null, /** * APIProperty: params * {Object} Query string parameters to be appended to the URL. * Read-only, set through the options passed to the constructor. * Example: {maxFeatures: 50} */ params: null, /** * APIProperty: callback * {Object} Function to be called when the operation completes. */ callback: null, /** * APIProperty: callbackTemplate * {String} Template for creating a unique callback function name * for the registry. Should include ${id}. The ${id} variable will be * replaced with a string identifier prefixed with a "c" (e.g. c1, c2). * Default is "OpenLayers.Protocol.Script.registry.${id}". */ callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}", /** * APIProperty: callbackKey * {String} The name of the query string parameter that the service * recognizes as the callback identifier. Default is "callback". * This key is used to generate the URL for the script. For example * setting to "myCallback" would result in a URL like * http://example.com/?myCallback=... */ callbackKey: "callback", /** * APIProperty: callbackPrefix * {String} Where a service requires that the callback query string * parameter value is prefixed by some string, this value may be set. * For example, setting to "foo:" would result in a * URL like http://example.com/?callback=foo:... Default is "". */ callbackPrefix: "", /** * APIProperty: scope * {Object} Optional ``this`` object for the callback. Read-only, set * through the options passed to the constructor. */ scope: null, /** * APIProperty: format * {} Format for parsing features. Default is an * format. If an alternative is provided, * the format's read method must take an object and return an array * of features. */ format: null, /** * Property: pendingRequests * {Object} References all pending requests. Property names are script * identifiers and property values are script elements. */ pendingRequests: null, /** * APIProperty: srsInBBOX * {Boolean} Include the SRS identifier in BBOX query string parameter. * Setting this property has no effect if a custom filterToParams method * is provided. Default is false. If true and the layer has a * projection object set, any BBOX filter will be serialized with a * fifth item identifying the projection. * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 */ srsInBBOX: false, /** * Constructor: OpenLayers.Protocol.Script * A class for giving layers generic Script protocol. * * Parameters: * options - {Object} Optional object whose properties will be set on the * instance. * * Valid options include: * url - {String} * params - {Object} * callback - {Function} * scope - {Object} */ initialize: function(options) { options = options || {}; this.params = {}; this.pendingRequests = {}; OpenLayers.Protocol.prototype.initialize.apply(this, arguments); if (!this.format) { this.format = new OpenLayers.Format.GeoJSON(); } if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { var format = new OpenLayers.Format.QueryStringFilter({ srsInBBOX: this.srsInBBOX }); this.filterToParams = function(filter, params) { return format.write(filter, params); }; } }, /** * APIMethod: 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. * * Valid options: * url - {String} Url for the request. * params - {Object} Parameters to get serialized as a query string. * filter - {} Filter to get serialized as a * query string. * * Returns: * {} A response object, whose "priv" property * references the injected script. This object is also passed to the * callback function when the request completes, its "features" property * is then populated with the features received from the server. */ read: function(options) { OpenLayers.Protocol.prototype.read.apply(this, arguments); options = OpenLayers.Util.applyDefaults(options, this.options); options.params = OpenLayers.Util.applyDefaults( options.params, this.options.params ); if (options.filter && this.filterToParams) { options.params = this.filterToParams( options.filter, options.params ); } var response = new OpenLayers.Protocol.Response({requestType: "read"}); var request = this.createRequest( options.url, options.params, OpenLayers.Function.bind(function(data) { response.data = data; this.handleRead(response, options); }, this) ); response.priv = request; return response; }, /** * 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, any filter will not be serialized. * * Parameters: * filter - {} filter to convert. * params - {Object} The parameters object. * * Returns: * {Object} The resulting parameters object. */ /** * Method: createRequest * Issues a request for features by creating injecting a script in the * document head. * * Parameters: * url - {String} Service URL. * params - {Object} Query string parameters. * callback - {Function} Callback to be called with resulting data. * * Returns: * {HTMLScriptElement} The script pending execution. */ createRequest: function(url, params, callback) { var id = OpenLayers.Protocol.Script.register(callback); var name = OpenLayers.String.format(this.callbackTemplate, {id: id}); params = OpenLayers.Util.extend({}, params); params[this.callbackKey] = this.callbackPrefix + name; url = OpenLayers.Util.urlAppend( url, OpenLayers.Util.getParameterString(params) ); var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; script.id = "OpenLayers_Protocol_Script_" + id; this.pendingRequests[script.id] = script; var head = document.getElementsByTagName("head")[0]; head.appendChild(script); return script; }, /** * Method: destroyRequest * Remove a script node associated with a response from the document. Also * unregisters the callback and removes the script from the * object. * * Parameters: * script - {HTMLScriptElement} */ destroyRequest: function(script) { OpenLayers.Protocol.Script.unregister(script.id.split("_").pop()); delete this.pendingRequests[script.id]; if (script.parentNode) { script.parentNode.removeChild(script); } }, /** * Method: handleRead * Individual callbacks are created for read, create and update, should * a subclass need to override each one separately. * * Parameters: * response - {} The response object to pass to * the user callback. * options - {Object} The user options passed to the read call. */ handleRead: function(response, options) { this.handleResponse(response, options); }, /** * Method: handleResponse * Called by CRUD specific handlers. * * Parameters: * response - {} 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(response, options) { if (options.callback) { if (response.data) { response.features = this.parseFeatures(response.data); response.code = OpenLayers.Protocol.Response.SUCCESS; } else { response.code = OpenLayers.Protocol.Response.FAILURE; } this.destroyRequest(response.priv); options.callback.call(options.scope, response); } }, /** * Method: parseFeatures * Read Script response body and return features. * * Parameters: * data - {Object} The data sent to the callback function by the server. * * Returns: * {Array({})} or * {} Array of features or a single feature. */ parseFeatures: function(data) { return this.format.read(data); }, /** * APIMethod: abort * Abort an ongoing request. If no response is provided, all pending * requests will be aborted. * * Parameters: * response - {} The response object returned * from a request. */ abort: function(response) { if (response) { this.destroyRequest(response.priv); } else { for (var key in this.pendingRequests) { this.destroyRequest(this.pendingRequests[key]); } } }, /** * APIMethod: destroy * Clean up the protocol. */ destroy: function() { this.abort(); delete this.params; delete this.format; OpenLayers.Protocol.prototype.destroy.apply(this); }, CLASS_NAME: "OpenLayers.Protocol.Script" }); (function() { var o = OpenLayers.Protocol.Script; var counter = 0; o.registry = {}; /** * Function: OpenLayers.Protocol.Script.register * Register a callback for a newly created script. * * Parameters: * callback - {Function} The callback to be executed when the newly added * script loads. This callback will be called with a single argument * that is the JSON returned by the service. * * Returns: * {Number} An identifier for retrieving the registered callback. */ o.register = function(callback) { var id = "c"+(++counter); o.registry[id] = function() { callback.apply(this, arguments); }; return id; }; /** * Function: OpenLayers.Protocol.Script.unregister * Unregister a callback previously registered with the register function. * * Parameters: * id - {Number} The identifer returned by the register function. */ o.unregister = function(id) { delete o.registry[id]; }; })();