Script.js 13 KB

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