/* 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/Control.js * @requires OpenLayers/Request.js * @requires OpenLayers/Console.js */ /** * Class: OpenLayers.Control.CacheWrite * A control for caching image tiles in the browser's local storage. The * control is used to fetch and use the cached * tile images. * * Note: Before using this control on any layer that is not your own, make sure * that the terms of service of the tile provider allow local storage of tiles. * * Inherits from: * - */ OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { /** * APIProperty: events * {} Events instance for listeners and triggering * control specific events. * * To register events in the constructor, configure . * * Register a listener for a particular event with the following syntax: * (code) * control.events.register(type, obj, listener); * (end) * * Supported event types (in addition to those from ): * cachefull - Triggered when the cache is full. Listeners receive an * object with a tile property as first argument. The tile references * the tile that couldn't be cached. */ /** * APIProperty: eventListeners * {Object} Object with event listeners, keyed by event name. An optional * scope property defines the scope that listeners will be executed in. */ /** * APIProperty: layers * {Array()}. Optional. If provided, caching * will be enabled for these layers only, otherwise for all cacheable * layers. */ layers: null, /** * APIProperty: imageFormat * {String} The image format used for caching. The default is "image/png". * Supported formats depend on the user agent. If an unsupported * is provided, "image/png" will be used. For aerial * imagery, "image/jpeg" is recommended. */ imageFormat: "image/png", /** * Property: quotaRegEx * {RegExp} */ quotaRegEx: (/quota/i), /** * Constructor: OpenLayers.Control.CacheWrite * * Parameters: * options - {Object} Object with API properties for this control. */ /** * Method: setMap * Set the map property for the control. * * Parameters: * map - {} */ setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); var i, layers = this.layers || map.layers; for (i=layers.length-1; i>=0; --i) { this.addLayer({layer: layers[i]}); } if (!this.layers) { map.events.on({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } }, /** * Method: addLayer * Adds a layer to the control. Once added, tiles requested for this layer * will be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * instance */ addLayer: function(evt) { evt.layer.events.on({ tileloadstart: this.makeSameOrigin, tileloaded: this.onTileLoaded, scope: this }); }, /** * Method: removeLayer * Removes a layer from the control. Once removed, tiles requested for this * layer will no longer be cached. * * Parameters: * evt - {Object} Object with a layer property referencing an * instance */ removeLayer: function(evt) { evt.layer.events.un({ tileloadstart: this.makeSameOrigin, tileloaded: this.onTileLoaded, scope: this }); }, /** * Method: makeSameOrigin * If the tile does not have CORS image loading enabled and is from a * different origin, use OpenLayers.ProxyHost to make it a same origin url. * * Parameters: * evt - {} */ makeSameOrigin: function(evt) { if (this.active) { var tile = evt.tile; if (tile instanceof OpenLayers.Tile.Image && !tile.crossOriginKeyword && tile.url.substr(0, 5) !== "data:") { var sameOriginUrl = OpenLayers.Request.makeSameOrigin( tile.url, OpenLayers.ProxyHost ); OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; tile.url = sameOriginUrl; } } }, /** * Method: onTileLoaded * Decides whether a tile can be cached and calls the cache method. * * Parameters: * evt - {Event} */ onTileLoaded: function(evt) { if (this.active && !evt.aborted && evt.tile instanceof OpenLayers.Tile.Image && evt.tile.url.substr(0, 5) !== 'data:') { this.cache({tile: evt.tile}); delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url]; } }, /** * Method: cache * Adds a tile to the cache. When the cache is full, the "cachefull" event * is triggered. * * Parameters: * obj - {Object} Object with a tile property, tile being the * with the data to add to the cache */ cache: function(obj) { if (window.localStorage) { var tile = obj.tile; try { var canvasContext = tile.getCanvasContext(); if (canvasContext) { var urlMap = OpenLayers.Control.CacheWrite.urlMap; var url = urlMap[tile.url] || tile.url; window.localStorage.setItem( "olCache_" + url, canvasContext.canvas.toDataURL(this.imageFormat) ); } } catch(e) { // local storage full or CORS violation var reason = e.name || e.message; if (reason && this.quotaRegEx.test(reason)) { this.events.triggerEvent("cachefull", {tile: tile}); } else { OpenLayers.Console.error(e.toString()); } } } }, /** * Method: destroy * The destroy method is used to perform any clean up before the control * is dereferenced. Typically this is where event listeners are removed * to prevent memory leaks. */ destroy: function() { if (this.layers || this.map) { var i, layers = this.layers || this.map.layers; for (i=layers.length-1; i>=0; --i) { this.removeLayer({layer: layers[i]}); } } if (this.map) { this.map.events.un({ addlayer: this.addLayer, removeLayer: this.removeLayer, scope: this }); } OpenLayers.Control.prototype.destroy.apply(this, arguments); }, CLASS_NAME: "OpenLayers.Control.CacheWrite" }); /** * APIFunction: OpenLayers.Control.CacheWrite.clearCache * Clears all tiles cached with from the cache. */ OpenLayers.Control.CacheWrite.clearCache = function() { if (!window.localStorage) { return; } var i, key; for (i=window.localStorage.length-1; i>=0; --i) { key = window.localStorage.key(i); if (key.substr(0, 8) === "olCache_") { window.localStorage.removeItem(key); } } }; /** * Property: OpenLayers.Control.CacheWrite.urlMap * {Object} Mapping of same origin urls to cache url keys. Entries will be * deleted as soon as a tile was cached. */ OpenLayers.Control.CacheWrite.urlMap = {};