diff --git a/externs/olx.js b/externs/olx.js index 64615ed687..6fe6e8f9c4 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -4193,6 +4193,7 @@ olx.source.ClusterOptions.prototype.wrapX; /** * @typedef {{preemptive: (boolean|undefined), + * jsonp: (boolean|undefined), * tileJSON: (TileJSON|undefined), * url: (string|undefined)}} * @api @@ -4200,6 +4201,15 @@ olx.source.ClusterOptions.prototype.wrapX; olx.source.TileUTFGridOptions; +/** + * Use JSONP with callback to load the TileJSON. Useful when the server + * does not support CORS. Default is `false`. + * @type {boolean|undefined} + * @api + */ +olx.source.TileUTFGridOptions.prototype.jsonp; + + /** * If `true` the TileUTFGrid source loads the tiles based on their "visibility". * This improves the speed of response, but increases traffic. diff --git a/src/ol/source/tileutfgridsource.js b/src/ol/source/tileutfgridsource.js index a074e381e2..96dbeb2b4e 100644 --- a/src/ol/source/tileutfgridsource.js +++ b/src/ol/source/tileutfgridsource.js @@ -49,8 +49,23 @@ ol.source.TileUTFGrid = function(options) { */ this.template_ = undefined; + /** + * @private + * @type {boolean} + */ + this.jsonp_ = options.jsonp || false; + if (options.url) { - ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this)); + if (this.jsonp_) { + ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this), + this.handleTileJSONError.bind(this)); + } else { + var client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', options.url); + client.send(); + } } else if (options.tileJSON) { this.handleTileJSONResponse(options.tileJSON); } else { @@ -60,6 +75,36 @@ ol.source.TileUTFGrid = function(options) { goog.inherits(ol.source.TileUTFGrid, ol.source.Tile); +/** + * @private + * @param {Event} event The load event. + */ +ol.source.TileUTFGrid.prototype.onXHRLoad_ = function(event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {TileJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleTileJSONError(); + return; + } + this.handleTileJSONResponse(response); + } else { + this.handleTileJSONError(); + } +}; + + +/** + * @private + * @param {Event} event The error event. + */ +ol.source.TileUTFGrid.prototype.onXHRError_ = function(event) { + this.handleTileJSONError(); +}; + + /** * Return the template from TileJSON. * @return {string|undefined} The template from TileJSON. @@ -103,6 +148,14 @@ ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( }; +/** + * @protected + */ +ol.source.TileUTFGrid.prototype.handleTileJSONError = function() { + this.setState(ol.source.State.ERROR); +}; + + /** * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse * @protected @@ -185,7 +238,8 @@ ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projecti tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, tileUrl !== undefined ? tileUrl : '', this.tileGrid.getTileCoordExtent(tileCoord), - this.preemptive_); + this.preemptive_, + this.jsonp_); this.tileCache.set(tileCoordKey, tile); return tile; } @@ -211,9 +265,10 @@ ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { * @param {string} src Image source URI. * @param {ol.Extent} extent Extent of the tile. * @param {boolean} preemptive Load the tile when visible (before it's needed). + * @param {boolean} jsonp Load the tile as a script. * @private */ -ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive) { +ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive, jsonp) { goog.base(this, tileCoord, state); @@ -252,6 +307,14 @@ ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive) * @type {Object.|undefined} */ this.data_ = null; + + + /** + * @private + * @type {boolean} + */ + this.jsonp_ = jsonp; + }; goog.inherits(ol.source.TileUTFGridTile_, ol.Tile); @@ -365,12 +428,50 @@ ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) { ol.source.TileUTFGridTile_.prototype.loadInternal_ = function() { if (this.state == ol.TileState.IDLE) { this.state = ol.TileState.LOADING; - ol.net.jsonp(this.src_, this.handleLoad_.bind(this), - this.handleError_.bind(this)); + if (this.jsonp_) { + ol.net.jsonp(this.src_, this.handleLoad_.bind(this), + this.handleError_.bind(this)); + } else { + var client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', this.src_); + client.send(); + } } }; +/** + * @private + * @param {Event} event The load event. + */ +ol.source.TileUTFGridTile_.prototype.onXHRLoad_ = function(event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {TileJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleError_(); + return; + } + this.handleLoad_(response); + } else { + this.handleError_(); + } +}; + + +/** + * @private + * @param {Event} event The error event. + */ +ol.source.TileUTFGridTile_.prototype.onXHRError_ = function() { + this.handleError_(); +}; + + /** * Load not yet loaded URI. */ diff --git a/test/spec/ol/source/tileutfgridsource.test.js b/test/spec/ol/source/tileutfgridsource.test.js index 97bf635428..d54ac5360b 100644 --- a/test/spec/ol/source/tileutfgridsource.test.js +++ b/test/spec/ol/source/tileutfgridsource.test.js @@ -63,19 +63,6 @@ describe('ol.source.TileUTFGrid', function() { expect(source).to.be.an(ol.source.TileUTFGrid); }); - it('immediately fetches the passed URL', function() { - // spy on the jsonp method - var jsonpSpy = sinon.spy(ol.net, 'jsonp'); - - getTileUTFGrid(); - expect(jsonpSpy.calledOnce).to.be(true); - expect(jsonpSpy.lastCall.calledWith(url)).to.be(true); - - // cleanup - ol.net.jsonp.restore(); - jsonpSpy = null; - }); - }); describe('#handleTileJSONResponse', function() {