CacheWrite.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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/Control.js
  7. * @requires OpenLayers/Request.js
  8. * @requires OpenLayers/Console.js
  9. */
  10. /**
  11. * Class: OpenLayers.Control.CacheWrite
  12. * A control for caching image tiles in the browser's local storage. The
  13. * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached
  14. * tile images.
  15. *
  16. * Note: Before using this control on any layer that is not your own, make sure
  17. * that the terms of service of the tile provider allow local storage of tiles.
  18. *
  19. * Inherits from:
  20. * - <OpenLayers.Control>
  21. */
  22. OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {
  23. /**
  24. * APIProperty: events
  25. * {<OpenLayers.Events>} Events instance for listeners and triggering
  26. * control specific events.
  27. *
  28. * To register events in the constructor, configure <eventListeners>.
  29. *
  30. * Register a listener for a particular event with the following syntax:
  31. * (code)
  32. * control.events.register(type, obj, listener);
  33. * (end)
  34. *
  35. * Supported event types (in addition to those from <OpenLayers.Control.events>):
  36. * cachefull - Triggered when the cache is full. Listeners receive an
  37. * object with a tile property as first argument. The tile references
  38. * the tile that couldn't be cached.
  39. */
  40. /**
  41. * APIProperty: eventListeners
  42. * {Object} Object with event listeners, keyed by event name. An optional
  43. * scope property defines the scope that listeners will be executed in.
  44. */
  45. /**
  46. * APIProperty: layers
  47. * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching
  48. * will be enabled for these layers only, otherwise for all cacheable
  49. * layers.
  50. */
  51. layers: null,
  52. /**
  53. * APIProperty: imageFormat
  54. * {String} The image format used for caching. The default is "image/png".
  55. * Supported formats depend on the user agent. If an unsupported
  56. * <imageFormat> is provided, "image/png" will be used. For aerial
  57. * imagery, "image/jpeg" is recommended.
  58. */
  59. imageFormat: "image/png",
  60. /**
  61. * Property: quotaRegEx
  62. * {RegExp}
  63. */
  64. quotaRegEx: (/quota/i),
  65. /**
  66. * Constructor: OpenLayers.Control.CacheWrite
  67. *
  68. * Parameters:
  69. * options - {Object} Object with API properties for this control.
  70. */
  71. /**
  72. * Method: setMap
  73. * Set the map property for the control.
  74. *
  75. * Parameters:
  76. * map - {<OpenLayers.Map>}
  77. */
  78. setMap: function(map) {
  79. OpenLayers.Control.prototype.setMap.apply(this, arguments);
  80. var i, layers = this.layers || map.layers;
  81. for (i=layers.length-1; i>=0; --i) {
  82. this.addLayer({layer: layers[i]});
  83. }
  84. if (!this.layers) {
  85. map.events.on({
  86. addlayer: this.addLayer,
  87. removeLayer: this.removeLayer,
  88. scope: this
  89. });
  90. }
  91. },
  92. /**
  93. * Method: addLayer
  94. * Adds a layer to the control. Once added, tiles requested for this layer
  95. * will be cached.
  96. *
  97. * Parameters:
  98. * evt - {Object} Object with a layer property referencing an
  99. * <OpenLayers.Layer> instance
  100. */
  101. addLayer: function(evt) {
  102. evt.layer.events.on({
  103. tileloadstart: this.makeSameOrigin,
  104. tileloaded: this.onTileLoaded,
  105. scope: this
  106. });
  107. },
  108. /**
  109. * Method: removeLayer
  110. * Removes a layer from the control. Once removed, tiles requested for this
  111. * layer will no longer be cached.
  112. *
  113. * Parameters:
  114. * evt - {Object} Object with a layer property referencing an
  115. * <OpenLayers.Layer> instance
  116. */
  117. removeLayer: function(evt) {
  118. evt.layer.events.un({
  119. tileloadstart: this.makeSameOrigin,
  120. tileloaded: this.onTileLoaded,
  121. scope: this
  122. });
  123. },
  124. /**
  125. * Method: makeSameOrigin
  126. * If the tile does not have CORS image loading enabled and is from a
  127. * different origin, use OpenLayers.ProxyHost to make it a same origin url.
  128. *
  129. * Parameters:
  130. * evt - {<OpenLayers.Event>}
  131. */
  132. makeSameOrigin: function(evt) {
  133. if (this.active) {
  134. var tile = evt.tile;
  135. if (tile instanceof OpenLayers.Tile.Image &&
  136. !tile.crossOriginKeyword &&
  137. tile.url.substr(0, 5) !== "data:") {
  138. var sameOriginUrl = OpenLayers.Request.makeSameOrigin(
  139. tile.url, OpenLayers.ProxyHost
  140. );
  141. OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url;
  142. tile.url = sameOriginUrl;
  143. }
  144. }
  145. },
  146. /**
  147. * Method: onTileLoaded
  148. * Decides whether a tile can be cached and calls the cache method.
  149. *
  150. * Parameters:
  151. * evt - {Event}
  152. */
  153. onTileLoaded: function(evt) {
  154. if (this.active && !evt.aborted &&
  155. evt.tile instanceof OpenLayers.Tile.Image &&
  156. evt.tile.url.substr(0, 5) !== 'data:') {
  157. this.cache({tile: evt.tile});
  158. delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url];
  159. }
  160. },
  161. /**
  162. * Method: cache
  163. * Adds a tile to the cache. When the cache is full, the "cachefull" event
  164. * is triggered.
  165. *
  166. * Parameters:
  167. * obj - {Object} Object with a tile property, tile being the
  168. * <OpenLayers.Tile.Image> with the data to add to the cache
  169. */
  170. cache: function(obj) {
  171. if (window.localStorage) {
  172. var tile = obj.tile;
  173. try {
  174. var canvasContext = tile.getCanvasContext();
  175. if (canvasContext) {
  176. var urlMap = OpenLayers.Control.CacheWrite.urlMap;
  177. var url = urlMap[tile.url] || tile.url;
  178. window.localStorage.setItem(
  179. "olCache_" + url,
  180. canvasContext.canvas.toDataURL(this.imageFormat)
  181. );
  182. }
  183. } catch(e) {
  184. // local storage full or CORS violation
  185. var reason = e.name || e.message;
  186. if (reason && this.quotaRegEx.test(reason)) {
  187. this.events.triggerEvent("cachefull", {tile: tile});
  188. } else {
  189. OpenLayers.Console.error(e.toString());
  190. }
  191. }
  192. }
  193. },
  194. /**
  195. * Method: destroy
  196. * The destroy method is used to perform any clean up before the control
  197. * is dereferenced. Typically this is where event listeners are removed
  198. * to prevent memory leaks.
  199. */
  200. destroy: function() {
  201. if (this.layers || this.map) {
  202. var i, layers = this.layers || this.map.layers;
  203. for (i=layers.length-1; i>=0; --i) {
  204. this.removeLayer({layer: layers[i]});
  205. }
  206. }
  207. if (this.map) {
  208. this.map.events.un({
  209. addlayer: this.addLayer,
  210. removeLayer: this.removeLayer,
  211. scope: this
  212. });
  213. }
  214. OpenLayers.Control.prototype.destroy.apply(this, arguments);
  215. },
  216. CLASS_NAME: "OpenLayers.Control.CacheWrite"
  217. });
  218. /**
  219. * APIFunction: OpenLayers.Control.CacheWrite.clearCache
  220. * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.
  221. */
  222. OpenLayers.Control.CacheWrite.clearCache = function() {
  223. if (!window.localStorage) { return; }
  224. var i, key;
  225. for (i=window.localStorage.length-1; i>=0; --i) {
  226. key = window.localStorage.key(i);
  227. if (key.substr(0, 8) === "olCache_") {
  228. window.localStorage.removeItem(key);
  229. }
  230. }
  231. };
  232. /**
  233. * Property: OpenLayers.Control.CacheWrite.urlMap
  234. * {Object} Mapping of same origin urls to cache url keys. Entries will be
  235. * deleted as soon as a tile was cached.
  236. */
  237. OpenLayers.Control.CacheWrite.urlMap = {};