diff --git a/js/source/geojson_source.js b/js/source/geojson_source.js index d946ddee1b5..a7532309813 100644 --- a/js/source/geojson_source.js +++ b/js/source/geojson_source.js @@ -70,6 +70,7 @@ class GeoJSONSource extends Evented { this.reparseOverscaled = true; this.dispatcher = dispatcher; + this.setEventedParent(eventedParent); this._data = options.data; @@ -98,8 +99,9 @@ class GeoJSONSource extends Evented { log: false } }, options.workerOptions); + } - this.setEventedParent(eventedParent); + load() { this.fire('dataloading', {dataType: 'source'}); this._updateWorkerData((err) => { if (err) { @@ -112,6 +114,7 @@ class GeoJSONSource extends Evented { } onAdd(map) { + this.load(); this.map = map; } diff --git a/js/source/image_source.js b/js/source/image_source.js index 02e8792fdce..332a080aa79 100644 --- a/js/source/image_source.js +++ b/js/source/image_source.js @@ -55,15 +55,16 @@ class ImageSource extends Evented { this.tileSize = 512; this.setEventedParent(eventedParent); - this.fire('dataloading', {dataType: 'source'}); - this._load(options); + this.options = options; } - _load(options) { - this.url = options.url; + load() { + this.fire('dataloading', {dataType: 'source'}); + + this.url = this.options.url; - ajax.getImage(options.url, (err, image) => { + ajax.getImage(this.options.url, (err, image) => { if (err) return this.fire('error', {error: err}); this.image = image; @@ -81,6 +82,7 @@ class ImageSource extends Evented { } onAdd(map) { + this.load(); this.map = map; if (this.image) { this.setCoordinates(this.coordinates); diff --git a/js/source/raster_tile_source.js b/js/source/raster_tile_source.js index c6e8cbafab2..325d87880a3 100644 --- a/js/source/raster_tile_source.js +++ b/js/source/raster_tile_source.js @@ -12,6 +12,7 @@ class RasterTileSource extends Evented { super(); this.id = id; this.dispatcher = dispatcher; + this.setEventedParent(eventedParent); this.minzoom = 0; this.maxzoom = 22; @@ -19,11 +20,13 @@ class RasterTileSource extends Evented { this.scheme = 'xyz'; this.tileSize = 512; this._loaded = false; + this.options = options; util.extend(this, util.pick(options, ['url', 'scheme', 'tileSize'])); + } - this.setEventedParent(eventedParent); + load() { this.fire('dataloading', {dataType: 'source'}); - loadTileJSON(options, (err, tileJSON) => { + loadTileJSON(this.options, (err, tileJSON) => { if (err) { return this.fire('error', err); } @@ -34,6 +37,7 @@ class RasterTileSource extends Evented { } onAdd(map) { + this.load(); this.map = map; } diff --git a/js/source/source.js b/js/source/source.js index 9117f84be39..e4a01bad7ce 100644 --- a/js/source/source.js +++ b/js/source/source.js @@ -21,7 +21,6 @@ const sourceTypes = { */ exports.create = function(id, source, dispatcher, eventedParent) { source = new sourceTypes[source.type](id, source, dispatcher, eventedParent); - source.setEventedParent(eventedParent); if (source.id !== id) { throw new Error(`Expected Source id to be ${id} instead of ${source.id}`); diff --git a/js/source/source_cache.js b/js/source/source_cache.js index 2908e85895f..af64999d44c 100644 --- a/js/source/source_cache.js +++ b/js/source/source_cache.js @@ -27,10 +27,7 @@ class SourceCache extends Evented { this.id = id; this.dispatcher = dispatcher; - this._source = Source.create(id, options, dispatcher, this); - this.on('source.load', function() { - if (this.map && this._source.onAdd) { this._source.onAdd(this.map); } this._sourceLoaded = true; }); @@ -47,6 +44,8 @@ class SourceCache extends Evented { } }); + this._source = Source.create(id, options, dispatcher, this); + this._tiles = {}; this._cache = new Cache(0, this.unloadTile.bind(this)); diff --git a/js/source/vector_tile_source.js b/js/source/vector_tile_source.js index 1dda7066702..3bb9f0bd5c5 100644 --- a/js/source/vector_tile_source.js +++ b/js/source/vector_tile_source.js @@ -28,9 +28,12 @@ class VectorTileSource extends Evented { } this.setEventedParent(eventedParent); + } + + load() { this.fire('dataloading', {dataType: 'source'}); - loadTileJSON(options, (err, tileJSON) => { + loadTileJSON(this._options, (err, tileJSON) => { if (err) { this.fire('error', err); return; @@ -42,6 +45,7 @@ class VectorTileSource extends Evented { } onAdd(map) { + this.load(); this.map = map; } diff --git a/js/source/video_source.js b/js/source/video_source.js index ff4a5410772..6854ef9932c 100644 --- a/js/source/video_source.js +++ b/js/source/video_source.js @@ -39,9 +39,11 @@ class VideoSource extends ImageSource { constructor(id, options, dispatcher, eventedParent) { super(id, options, dispatcher, eventedParent); this.roundZoom = true; + this.options = options; } - _load(options) { + load() { + const options = this.options; this.urls = options.urls; ajax.getVideo(options.urls, (err, video) => { @@ -82,6 +84,7 @@ class VideoSource extends ImageSource { onAdd(map) { if (this.map) return; + this.load(); this.map = map; if (this.video) { this.video.play(); diff --git a/js/style/image_sprite.js b/js/style/image_sprite.js index 7a2cecf34e0..948dd94716d 100644 --- a/js/style/image_sprite.js +++ b/js/style/image_sprite.js @@ -18,10 +18,11 @@ class SpritePosition { class ImageSprite extends Evented { - constructor(base) { + constructor(base, eventedParent) { super(); this.base = base; this.retina = browser.devicePixelRatio > 1; + this.setEventedParent(eventedParent); const format = this.retina ? '@2x' : ''; diff --git a/js/style/style.js b/js/style/style.js index 9251e34acc7..7c545000c47 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -94,8 +94,7 @@ class Style extends Evented { } if (stylesheet.sprite) { - this.sprite = new ImageSprite(stylesheet.sprite); - this.sprite.setEventedParent(this); + this.sprite = new ImageSprite(stylesheet.sprite, this); } this.glyphSource = new GlyphSource(stylesheet.glyphs); diff --git a/test/js/source/geojson_source.test.js b/test/js/source/geojson_source.test.js index 788eb457ec2..55b55c4b4f7 100644 --- a/test/js/source/geojson_source.test.js +++ b/test/js/source/geojson_source.test.js @@ -66,6 +66,7 @@ test('GeoJSONSource#setData', (t) => { source.on('data', t.end); source.setData({}); }); + source.load(); }); t.test('fires "dataloading" event', (t) => { @@ -74,6 +75,7 @@ test('GeoJSONSource#setData', (t) => { source.on('dataloading', t.end); source.setData({}); }); + source.load(); }); t.end(); @@ -115,7 +117,7 @@ test('GeoJSONSource#update', (t) => { }; /* eslint-disable no-new */ - new GeoJSONSource('id', {data: {}}, mockDispatcher); + new GeoJSONSource('id', {data: {}}, mockDispatcher).load(); }); t.test('forwards geojson-vt options with worker request', (t) => { @@ -137,7 +139,7 @@ test('GeoJSONSource#update', (t) => { maxzoom: 10, tolerance: 0.25, buffer: 16 - }, mockDispatcher); + }, mockDispatcher).load(); }); t.test('fires "source.load"', (t) => { @@ -152,6 +154,8 @@ test('GeoJSONSource#update', (t) => { source.on('source.load', () => { t.end(); }); + + source.load(); }); t.test('fires "error"', (t) => { @@ -167,6 +171,8 @@ test('GeoJSONSource#update', (t) => { t.equal(err.error, 'error'); t.end(); }); + + source.load(); }); t.test('sends loadData request to dispatcher after data update', (t) => { @@ -189,6 +195,8 @@ test('GeoJSONSource#update', (t) => { source.setData({}); source.loadTile(new Tile(new TileCoord(0, 0, 0), 512), () => {}); }); + + source.load(); }); t.end(); @@ -198,6 +206,7 @@ test('GeoJSONSource#serialize', (t) => { t.test('serialize source with inline data', (t) => { const source = new GeoJSONSource('id', {data: hawkHill}, mockDispatcher); + source.load(); t.deepEqual(source.serialize(), { type: 'geojson', data: hawkHill @@ -207,6 +216,7 @@ test('GeoJSONSource#serialize', (t) => { t.test('serialize source with url', (t) => { const source = new GeoJSONSource('id', {data: 'local://data.json'}, mockDispatcher); + source.load(); t.deepEqual(source.serialize(), { type: 'geojson', data: 'local://data.json' @@ -216,6 +226,7 @@ test('GeoJSONSource#serialize', (t) => { t.test('serialize source with updated data', (t) => { const source = new GeoJSONSource('id', {data: {}}, mockDispatcher); + source.load(); source.setData(hawkHill); t.deepEqual(source.serialize(), { type: 'geojson', diff --git a/test/js/source/source_cache.test.js b/test/js/source/source_cache.test.js index 9c0a275a37b..3e4884cc004 100644 --- a/test/js/source/source_cache.test.js +++ b/test/js/source/source_cache.test.js @@ -13,7 +13,7 @@ const Evented = require('../../../js/util/evented'); const util = require('../../../js/util/util'); // Add a mocked source type for use in these tests -function MockSourceType(id, sourceOptions) { +function MockSourceType(id, sourceOptions, _dispatcher, eventedParent) { // allow tests to override mocked methods/properties by providing // them in the source definition object that's given to Source.create() class SourceMock extends Evented { @@ -23,24 +23,25 @@ function MockSourceType(id, sourceOptions) { this.minzoom = 0; this.maxzoom = 22; util.extend(this, sourceOptions); + this.setEventedParent(eventedParent); } loadTile(tile, callback) { setTimeout(callback, 0); } + onAdd() { + if (sourceOptions.noLoad) return; + if (sourceOptions.error) { + this.fire('error', { error: sourceOptions.error }); + } else { + this.fire('source.load'); + } + } abortTile() {} unloadTile() {} serialize() {} } const source = new SourceMock(); - if (sourceOptions.noLoad) { return source; } - setTimeout(() => { - if (sourceOptions.error) { - source.fire('error', { error: sourceOptions.error }); - } else { - source.fire('source.load'); - } - }, 0); return source; } @@ -67,6 +68,7 @@ test('SourceCache#addTile', (t) => { t.end(); } }); + sourceCache.onAdd(); sourceCache.addTile(coord); }); @@ -78,6 +80,7 @@ test('SourceCache#addTile', (t) => { t.equal(data.tile.uses, 1); t.end(); }); + sourceCache.onAdd(); sourceCache.addTile(coord); }); @@ -201,27 +204,31 @@ test('SourceCache#removeTile', (t) => { test('SourceCache / Source lifecycle', (t) => { t.test('does not fire load or change before source load event', (t) => { - createSourceCache({noLoad: true}) + const sourceCache = createSourceCache({noLoad: true}) .on('source.load', t.fail) .on('data', t.fail); + sourceCache.onAdd(); setTimeout(t.end, 1); }); t.test('forward load event', (t) => { - createSourceCache({}).on('source.load', t.end); + const sourceCache = createSourceCache({}).on('source.load', t.end); + sourceCache.onAdd(); }); t.test('forward change event', (t) => { const sourceCache = createSourceCache().on('data', t.end); + sourceCache.onAdd(); sourceCache.getSource().fire('data'); }); t.test('forward error event', (t) => { - createSourceCache({ error: 'Error loading source' }) + const sourceCache = createSourceCache({ error: 'Error loading source' }) .on('error', (err) => { t.equal(err.error, 'Error loading source'); t.end(); }); + sourceCache.onAdd(); }); t.test('loaded() true after error', (t) => { @@ -230,6 +237,7 @@ test('SourceCache / Source lifecycle', (t) => { t.ok(sourceCache.loaded()); t.end(); }); + sourceCache.onAdd(); }); t.test('reloads tiles after a "source" data event', (t) => { @@ -252,6 +260,8 @@ test('SourceCache / Source lifecycle', (t) => { sourceCache.update(transform); sourceCache.getSource().fire('data', {dataType: 'source'}); }); + + sourceCache.onAdd(); }); t.end(); @@ -270,6 +280,7 @@ test('SourceCache#update', (t) => { t.deepEqual(sourceCache.getIds(), []); t.end(); }); + sourceCache.onAdd(); }); t.test('loads covering tiles', (t) => { @@ -283,6 +294,7 @@ test('SourceCache#update', (t) => { t.deepEqual(sourceCache.getIds(), [new TileCoord(0, 0, 0).id]); t.end(); }); + sourceCache.onAdd(); }); t.test('removes unused tiles', (t) => { @@ -290,11 +302,7 @@ test('SourceCache#update', (t) => { transform.resize(511, 511); transform.zoom = 0; - const sourceCache = createSourceCache({ - load: function(tile) { - tile.state = 'loaded'; - } - }); + const sourceCache = createSourceCache({}); sourceCache.on('source.load', () => { sourceCache.update(transform); @@ -311,6 +319,8 @@ test('SourceCache#update', (t) => { ]); t.end(); }); + + sourceCache.onAdd(); }); @@ -343,6 +353,7 @@ test('SourceCache#update', (t) => { ]); t.end(); }); + sourceCache.onAdd(); }); t.test('retains parent tiles for pending children (wrapped)', (t) => { @@ -374,6 +385,7 @@ test('SourceCache#update', (t) => { ]); t.end(); }); + sourceCache.onAdd(); }); t.test('includes partially covered tiles in rendered tiles', (t) => { @@ -406,6 +418,7 @@ test('SourceCache#update', (t) => { t.deepEqual(sourceCache.getRenderableIds().length, 5); t.end(); }); + sourceCache.onAdd(); }); t.test('retains a parent tile for fading even if a tile is partially covered by children', (t) => { @@ -436,6 +449,7 @@ test('SourceCache#update', (t) => { t.equal(sourceCache._coveredTiles[(new TileCoord(0, 0, 0).id)], true); t.end(); }); + sourceCache.onAdd(); }); @@ -474,6 +488,7 @@ test('SourceCache#update', (t) => { ]); t.end(); }); + sourceCache.onAdd(); }); t.end(); @@ -495,6 +510,7 @@ test('SourceCache#clearTiles', (t) => { unload++; } }); + sourceCache.onAdd(); sourceCache.addTile(coord); sourceCache.clearTiles(); @@ -511,6 +527,7 @@ test('SourceCache#clearTiles', (t) => { test('SourceCache#tilesIn', (t) => { t.test('graceful response before source loaded', (t) => { const sourceCache = createSourceCache({ noLoad: true }); + sourceCache.onAdd(); t.same(sourceCache.tilesIn([ new Coordinate(0.5, 0.25, 1), new Coordinate(1.5, 0.75, 1) @@ -561,6 +578,7 @@ test('SourceCache#tilesIn', (t) => { t.end(); }); + sourceCache.onAdd(); }); t.test('reparsed overscaled tiles', (t) => { @@ -605,6 +623,7 @@ test('SourceCache#tilesIn', (t) => { t.end(); }); + sourceCache.onAdd(); }); t.test('overscaled tiles', (t) => { @@ -625,6 +644,7 @@ test('SourceCache#tilesIn', (t) => { t.end(); }); + sourceCache.onAdd(); }); t.end(); @@ -645,6 +665,7 @@ test('SourceCache#loaded (no errors)', (t) => { t.ok(sourceCache.loaded()); t.end(); }); + sourceCache.onAdd(); }); test('SourceCache#loaded (with errors)', (t) => { @@ -661,6 +682,7 @@ test('SourceCache#loaded (with errors)', (t) => { t.ok(sourceCache.loaded()); t.end(); }); + sourceCache.onAdd(); }); test('SourceCache#getIds (ascending order by zoom level)', (t) => { @@ -682,6 +704,7 @@ test('SourceCache#getIds (ascending order by zoom level)', (t) => { new TileCoord(3, 0, 0).id ]); t.end(); + sourceCache.onAdd(); }); @@ -689,6 +712,7 @@ test('SourceCache#findLoadedParent', (t) => { t.test('adds from previously used tiles (sourceCache._tiles)', (t) => { const sourceCache = createSourceCache({}); + sourceCache.onAdd(); const tr = new Transform(); tr.width = 512; tr.height = 512; @@ -713,6 +737,7 @@ test('SourceCache#findLoadedParent', (t) => { t.test('adds from cache', (t) => { const sourceCache = createSourceCache({}); + sourceCache.onAdd(); const tr = new Transform(); tr.width = 512; tr.height = 512; @@ -740,6 +765,7 @@ test('SourceCache#findLoadedParent', (t) => { test('SourceCache#reload', (t) => { t.test('before loaded', (t) => { const sourceCache = createSourceCache({ noLoad: true }); + sourceCache.onAdd(); t.doesNotThrow(() => { sourceCache.reload(); diff --git a/test/js/source/vector_tile_source.test.js b/test/js/source/vector_tile_source.test.js index 0dc2e15e43f..ba2e4043883 100644 --- a/test/js/source/vector_tile_source.test.js +++ b/test/js/source/vector_tile_source.test.js @@ -8,15 +8,14 @@ const Evented = require('../../../js/util/evented'); function createSource(options) { const source = new VectorTileSource('id', options, { send: function() {} }, options.eventedParent); + source.onAdd({ + transform: { angle: 0, pitch: 0, showCollisionBoxes: false } + }); source.on('error', (e) => { throw e.error; }); - source.map = { - transform: { angle: 0, pitch: 0, showCollisionBoxes: false } - }; - return source; }