Script.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Protocol.js
  7. * @requires OpenLayers/Feature/Vector.js
  8. * @requires OpenLayers/Format/GeoJSON.js
  9. */
  10. /**
  11. * Class: OpenLayers.Protocol.Script
  12. * A basic Script protocol for vector layers. Create a new instance with the
  13. * <OpenLayers.Protocol.Script> constructor. A script protocol is used to
  14. * get around the same origin policy. It works with services that return
  15. * JSONP - that is, JSON wrapped in a client-specified callback. The
  16. * protocol handles fetching and parsing of feature data and sends parsed
  17. * features to the <callback> configured with the protocol. The protocol
  18. * expects features serialized as GeoJSON by default, but can be configured
  19. * to work with other formats by setting the <format> property.
  20. *
  21. * Inherits from:
  22. * - <OpenLayers.Protocol>
  23. */
  24. OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {
  25. /**
  26. * APIProperty: url
  27. * {String} Service URL. The service is expected to return serialized
  28. * features wrapped in a named callback (where the callback name is
  29. * generated by this protocol).
  30. * Read-only, set through the options passed to the constructor.
  31. */
  32. url: null,
  33. /**
  34. * APIProperty: params
  35. * {Object} Query string parameters to be appended to the URL.
  36. * Read-only, set through the options passed to the constructor.
  37. * Example: {maxFeatures: 50}
  38. */
  39. params: null,
  40. /**
  41. * APIProperty: callback
  42. * {Object} Function to be called when the <read> operation completes.
  43. */
  44. callback: null,
  45. /**
  46. * APIProperty: scope
  47. * {Object} Optional ``this`` object for the callback. Read-only, set
  48. * through the options passed to the constructor.
  49. */
  50. scope: null,
  51. /**
  52. * APIProperty: format
  53. * {<OpenLayers.Format>} Format for parsing features. Default is an
  54. * <OpenLayers.Format.GeoJSON> format. If an alternative is provided,
  55. * the format's read method must take an object and return an array
  56. * of features.
  57. */
  58. format: null,
  59. /**
  60. * APIProperty: callbackKey
  61. * {String} The name of the query string parameter that the service
  62. * recognizes as the callback identifier. Default is "callback".
  63. * This key is used to generate the URL for the script. For example
  64. * setting <callbackKey> to "myCallback" would result in a URL like
  65. * http://example.com/?myCallback=...
  66. */
  67. callbackKey: "callback",
  68. /**
  69. * APIProperty: callbackPrefix
  70. * {String} Where a service requires that the callback query string
  71. * parameter value is prefixed by some string, this value may be set.
  72. * For example, setting <callbackPrefix> to "foo:" would result in a
  73. * URL like http://example.com/?callback=foo:... Default is "".
  74. */
  75. callbackPrefix: "",
  76. /**
  77. * Property: pendingRequests
  78. * {Object} References all pending requests. Property names are script
  79. * identifiers and property values are script elements.
  80. */
  81. pendingRequests: null,
  82. /**
  83. * APIProperty: srsInBBOX
  84. * {Boolean} Include the SRS identifier in BBOX query string parameter.
  85. * Setting this property has no effect if a custom filterToParams method
  86. * is provided. Default is false. If true and the layer has a
  87. * projection object set, any BBOX filter will be serialized with a
  88. * fifth item identifying the projection.
  89. * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
  90. */
  91. srsInBBOX: false,
  92. /**
  93. * Constructor: OpenLayers.Protocol.Script
  94. * A class for giving layers generic Script protocol.
  95. *
  96. * Parameters:
  97. * options - {Object} Optional object whose properties will be set on the
  98. * instance.
  99. *
  100. * Valid options include:
  101. * url - {String}
  102. * params - {Object}
  103. * callback - {Function}
  104. * scope - {Object}
  105. */
  106. initialize: function(options) {
  107. options = options || {};
  108. this.params = {};
  109. this.pendingRequests = {};
  110. OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
  111. if (!this.format) {
  112. this.format = new OpenLayers.Format.GeoJSON();
  113. }
  114. if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
  115. var format = new OpenLayers.Format.QueryStringFilter({
  116. srsInBBOX: this.srsInBBOX
  117. });
  118. this.filterToParams = function(filter, params) {
  119. return format.write(filter, params);
  120. }
  121. }
  122. },
  123. /**
  124. * APIMethod: read
  125. * Construct a request for reading new features.
  126. *
  127. * Parameters:
  128. * options - {Object} Optional object for configuring the request.
  129. * This object is modified and should not be reused.
  130. *
  131. * Valid options:
  132. * url - {String} Url for the request.
  133. * params - {Object} Parameters to get serialized as a query string.
  134. * filter - {<OpenLayers.Filter>} Filter to get serialized as a
  135. * query string.
  136. *
  137. * Returns:
  138. * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
  139. * references the injected script. This object is also passed to the
  140. * callback function when the request completes, its "features" property
  141. * is then populated with the features received from the server.
  142. */
  143. read: function(options) {
  144. OpenLayers.Protocol.prototype.read.apply(this, arguments);
  145. options = OpenLayers.Util.applyDefaults(options, this.options);
  146. options.params = OpenLayers.Util.applyDefaults(
  147. options.params, this.options.params
  148. );
  149. if (options.filter && this.filterToParams) {
  150. options.params = this.filterToParams(
  151. options.filter, options.params
  152. );
  153. }
  154. var response = new OpenLayers.Protocol.Response({requestType: "read"});
  155. var request = this.createRequest(
  156. options.url,
  157. options.params,
  158. OpenLayers.Function.bind(function(data) {
  159. response.data = data;
  160. this.handleRead(response, options);
  161. }, this)
  162. );
  163. response.priv = request;
  164. return response;
  165. },
  166. /**
  167. * APIMethod: filterToParams
  168. * Optional method to translate an <OpenLayers.Filter> object into an object
  169. * that can be serialized as request query string provided. If a custom
  170. * method is not provided, any filter will not be serialized.
  171. *
  172. * Parameters:
  173. * filter - {<OpenLayers.Filter>} filter to convert.
  174. * params - {Object} The parameters object.
  175. *
  176. * Returns:
  177. * {Object} The resulting parameters object.
  178. */
  179. /**
  180. * Method: createRequest
  181. * Issues a request for features by creating injecting a script in the
  182. * document head.
  183. *
  184. * Parameters:
  185. * url - {String} Service URL.
  186. * params - {Object} Query string parameters.
  187. * callback - {Function} Callback to be called with resulting data.
  188. *
  189. * Returns:
  190. * {HTMLScriptElement} The script pending execution.
  191. */
  192. createRequest: function(url, params, callback) {
  193. var id = OpenLayers.Protocol.Script.register(callback);
  194. var name = "OpenLayers.Protocol.Script.registry[" + id + "]";
  195. params = OpenLayers.Util.extend({}, params);
  196. params[this.callbackKey] = this.callbackPrefix + name;
  197. url = OpenLayers.Util.urlAppend(
  198. url, OpenLayers.Util.getParameterString(params)
  199. );
  200. var script = document.createElement("script");
  201. script.type = "text/javascript";
  202. script.src = url;
  203. script.id = "OpenLayers_Protocol_Script_" + id;
  204. this.pendingRequests[script.id] = script;
  205. var head = document.getElementsByTagName("head")[0];
  206. head.appendChild(script);
  207. return script;
  208. },
  209. /**
  210. * Method: destroyRequest
  211. * Remove a script node associated with a response from the document. Also
  212. * unregisters the callback and removes the script from the
  213. * <pendingRequests> object.
  214. *
  215. * Parameters:
  216. * script - {HTMLScriptElement}
  217. */
  218. destroyRequest: function(script) {
  219. OpenLayers.Protocol.Script.unregister(script.id.split("_").pop());
  220. delete this.pendingRequests[script.id];
  221. if (script.parentNode) {
  222. script.parentNode.removeChild(script);
  223. }
  224. },
  225. /**
  226. * Method: handleRead
  227. * Individual callbacks are created for read, create and update, should
  228. * a subclass need to override each one separately.
  229. *
  230. * Parameters:
  231. * response - {<OpenLayers.Protocol.Response>} The response object to pass to
  232. * the user callback.
  233. * options - {Object} The user options passed to the read call.
  234. */
  235. handleRead: function(response, options) {
  236. this.handleResponse(response, options);
  237. },
  238. /**
  239. * Method: handleResponse
  240. * Called by CRUD specific handlers.
  241. *
  242. * Parameters:
  243. * response - {<OpenLayers.Protocol.Response>} The response object to pass to
  244. * any user callback.
  245. * options - {Object} The user options passed to the create, read, update,
  246. * or delete call.
  247. */
  248. handleResponse: function(response, options) {
  249. if (options.callback) {
  250. if (response.data) {
  251. response.features = this.parseFeatures(response.data);
  252. response.code = OpenLayers.Protocol.Response.SUCCESS;
  253. } else {
  254. response.code = OpenLayers.Protocol.Response.FAILURE;
  255. }
  256. this.destroyRequest(response.priv);
  257. options.callback.call(options.scope, response);
  258. }
  259. },
  260. /**
  261. * Method: parseFeatures
  262. * Read Script response body and return features.
  263. *
  264. * Parameters:
  265. * data - {Object} The data sent to the callback function by the server.
  266. *
  267. * Returns:
  268. * {Array({<OpenLayers.Feature.Vector>})} or
  269. * {<OpenLayers.Feature.Vector>} Array of features or a single feature.
  270. */
  271. parseFeatures: function(data) {
  272. return this.format.read(data);
  273. },
  274. /**
  275. * APIMethod: abort
  276. * Abort an ongoing request. If no response is provided, all pending
  277. * requests will be aborted.
  278. *
  279. * Parameters:
  280. * response - {<OpenLayers.Protocol.Response>} The response object returned
  281. * from a <read> request.
  282. */
  283. abort: function(response) {
  284. if (response) {
  285. this.destroyRequest(response.priv);
  286. } else {
  287. for (var key in this.pendingRequests) {
  288. this.destroyRequest(this.pendingRequests[key]);
  289. }
  290. }
  291. },
  292. /**
  293. * APIMethod: destroy
  294. * Clean up the protocol.
  295. */
  296. destroy: function() {
  297. this.abort();
  298. delete this.params;
  299. delete this.format;
  300. OpenLayers.Protocol.prototype.destroy.apply(this);
  301. },
  302. CLASS_NAME: "OpenLayers.Protocol.Script"
  303. });
  304. (function() {
  305. var o = OpenLayers.Protocol.Script;
  306. var counter = 0;
  307. o.registry = [];
  308. /**
  309. * Function: OpenLayers.Protocol.Script.register
  310. * Register a callback for a newly created script.
  311. *
  312. * Parameters:
  313. * callback: {Function} The callback to be executed when the newly added
  314. * script loads. This callback will be called with a single argument
  315. * that is the JSON returned by the service.
  316. *
  317. * Returns:
  318. * {Number} An identifier for retreiving the registered callback.
  319. */
  320. o.register = function(callback) {
  321. var id = ++counter;
  322. o.registry[id] = function() {
  323. o.unregister(id);
  324. callback.apply(this, arguments);
  325. };
  326. return id;
  327. };
  328. /**
  329. * Function: OpenLayers.Protocol.Script.unregister
  330. * Unregister a callback previously registered with the register function.
  331. *
  332. * Parameters:
  333. * id: {Number} The identifer returned by the register function.
  334. */
  335. o.unregister = function(id) {
  336. delete o.registry[id];
  337. };
  338. })();