+
+
+
+
+
+
+
+
+
diff --git a/js/mapbox-gl.js b/js/mapbox-gl.js
index 9f223c0a001..2f3bfe95d34 100644
--- a/js/mapbox-gl.js
+++ b/js/mapbox-gl.js
@@ -20,6 +20,8 @@ mapboxgl.Marker = require('./ui/marker');
mapboxgl.Style = require('./style/style');
+mapboxgl.addSourceType = require('./source/source').addType;
+
mapboxgl.LngLat = require('./geo/lng_lat');
mapboxgl.LngLatBounds = require('./geo/lng_lat_bounds');
mapboxgl.Point = require('point-geometry');
diff --git a/js/source/source.js b/js/source/source.js
index 7d98b4d7fad..3f20518446e 100644
--- a/js/source/source.js
+++ b/js/source/source.js
@@ -1,6 +1,8 @@
'use strict';
var util = require('../util/util');
+var Dispatcher = require('../util/dispatcher');
+var getWorkerPool = require('../global_worker_pool');
var sourceTypes = {
'vector': require('../source/vector_tile_source'),
@@ -10,6 +12,8 @@ var sourceTypes = {
'image': require('../source/image_source')
};
+var Source = module.exports = {};
+
/*
* Creates a tiled data source instance given an options object.
*
@@ -19,7 +23,7 @@ var sourceTypes = {
* @param {Dispatcher} dispatcher
* @returns {Source}
*/
-exports.create = function(id, source, dispatcher) {
+Source.create = function(id, source, dispatcher) {
source = new sourceTypes[source.type](id, source, dispatcher);
if (source.id !== id) {
@@ -30,14 +34,60 @@ exports.create = function(id, source, dispatcher) {
return source;
};
-exports.getType = function (name) {
+Source.getType = function (name) {
return sourceTypes[name];
};
-exports.setType = function (name, type) {
+Source.setType = function (name, type) {
sourceTypes[name] = type;
};
+/**
+ * Adds a [custom source type](#Custom Sources), making it available for use with
+ * {@link Map#addSource}.
+ *
+ * @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
+ * @param {Function} SourceType A {@link Source} constructor.
+ * @param {Function} callback called after SourceType has been added and, if relevant, its worker code has been sent to the workers.
+ * @private
+ */
+Source.addType = function (name, SourceType, callback) {
+ if (Source.getType(name)) {
+ throw new Error('A source type named ' + name + ' already exists.');
+ }
+
+ Source.setType(name, SourceType);
+
+ if (SourceType.workerSourceURL) {
+ getDispatcher().broadcast('load worker source', {
+ name: name,
+ url: SourceType.workerSourceURL
+ }, function (err) {
+ callback(err);
+ });
+ } else {
+ callback();
+ }
+};
+
+/*
+ * A Dispatcher instance for use in registering custom WorkerSources.
+ *
+ * Note that it is created on demand, and once created, this dispatcher
+ * instance prevents the Workers in the global pool from being destroyed even
+ * when the the last map instance is destroyed. This is intended behavior, as
+ * it ensures that any custom WorkerSources will be registered on the workers
+ * used by future map instances.
+ */
+var dispatcher;
+function getDispatcher () {
+ if (!dispatcher) {
+ dispatcher = new Dispatcher(getWorkerPool(), {});
+ }
+ return dispatcher;
+}
+
+
/**
* The `Source` interface must be implemented by each source type, including "core" types (`vector`, `raster`, `video`, etc.) and all custom, third-party types.
*
@@ -46,7 +96,7 @@ exports.setType = function (name, type) {
*
* @param {string} id The id for the source. Must not be used by any existing source.
* @param {Object} options Source options, specific to the source type (except for `options.type`, which is always required).
- * @param {string} options.type The source type, matching the value of `name` used in {@link Style#addSourceType}.
+ * @param {string} options.type The source type, matching the value of `name` used in {@link Source.addType}.
* @param {Dispatcher} dispatcher A {@link Dispatcher} instance, which can be used to send messages to the workers.
*
* @fires load to indicate source data has been loaded, so that it's okay to call `loadTile`
@@ -116,7 +166,7 @@ exports.setType = function (name, type) {
* implementation may also be targeted by the {@link Source} via
* `dispatcher.send('source-type.methodname', params, callback)`.
*
- * @see {@link Map#addSourceType}
+ * @see {@link Source.addType}
* @private
*
* @class WorkerSource
diff --git a/js/source/worker.js b/js/source/worker.js
index 9f98eb7fdf5..175967fa7ee 100644
--- a/js/source/worker.js
+++ b/js/source/worker.js
@@ -29,7 +29,7 @@ function Worker(self) {
this.self.registerWorkerSource = function (name, WorkerSource) {
if (this.workerSourceTypes[name]) {
- throw new Error('Worker source with name "' + name + '" already registered.');
+ util.warnOnce('Worker source named "' + name + '" already registered.');
}
this.workerSourceTypes[name] = WorkerSource;
}.bind(this);
diff --git a/js/style/style.js b/js/style/style.js
index d38666169dc..455a660a6ab 100644
--- a/js/style/style.js
+++ b/js/style/style.js
@@ -13,7 +13,6 @@ var browser = require('../util/browser');
var Dispatcher = require('../util/dispatcher');
var AnimationLoop = require('./animation_loop');
var validateStyle = require('./validate_style');
-var Source = require('../source/source');
var QueryFeatures = require('../source/query_features');
var SourceCache = require('../source/source_cache');
var styleSpec = require('./style_spec');
@@ -660,23 +659,6 @@ Style.prototype = util.inherit(Evented, {
return source ? QueryFeatures.source(source, params) : [];
},
- addSourceType: function (name, SourceType, callback) {
- if (Source.getType(name)) {
- return callback(new Error('A source type called "' + name + '" already exists.'));
- }
-
- Source.setType(name, SourceType);
-
- if (!SourceType.workerSourceURL) {
- return callback(null, null);
- }
-
- this.dispatcher.broadcast('load worker source', {
- name: name,
- url: SourceType.workerSourceURL
- }, callback);
- },
-
_validate: function(validate, key, value, props, options) {
if (options && options.validate === false) {
return false;
diff --git a/js/ui/map.js b/js/ui/map.js
index 1c3c316f784..555f74f7fd0 100755
--- a/js/ui/map.js
+++ b/js/ui/map.js
@@ -656,7 +656,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
* @param {string} id The ID of the source to add. Must not conflict with existing sources.
* @param {Object} source The source object, conforming to the
* Mapbox Style Specification's [source definition](https://www.mapbox.com/mapbox-gl-style-spec/#sources).
- * @param {string} source.type The source type, which must be either one of the core Mapbox GL source types defined in the style specification or a custom type that has been added to the map with {@link Map#addSourceType}.
+ * @param {string} source.type The source type, which must be one of the core Mapbox GL source types defined in the style specification.
* @fires source.add
* @returns {Map} `this`
*/
@@ -666,18 +666,6 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
return this;
},
- /**
- * Adds a [custom source type](#Custom Sources), making it available for use with
- * {@link Map#addSource}.
- * @private
- * @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
- * @param {Function} SourceType A {@link Source} constructor.
- * @param {Function} callback Called when the source type is ready or with an error argument if there is an error.
- */
- addSourceType: function (name, SourceType, callback) {
- return this.style.addSourceType(name, SourceType, callback);
- },
-
/**
* Removes a source from the map's style.
*
diff --git a/test/js/source/source.test.js b/test/js/source/source.test.js
new file mode 100644
index 00000000000..5beeeee5ef2
--- /dev/null
+++ b/test/js/source/source.test.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var test = require('tap').test;
+var Source = require('../../../js/source/source');
+var proxyquire = require('proxyquire');
+
+test('Source#addType', function (t) {
+ t.test('adds source type', function (t) {
+ // expect no call to load worker source
+ var Source = proxyquire('../../../js/source/source', {
+ '../util/dispatcher': function () {
+ t.fail();
+ }
+ });
+
+ var SourceType = function () {};
+
+ Source.addType('foo', SourceType, function (err) {
+ t.error(err);
+ t.equal(Source.getType('foo'), SourceType);
+ t.end();
+ });
+ });
+
+ t.test('triggers workers to load worker source code', function (t) {
+ var SourceType = function () {};
+ SourceType.workerSourceURL = 'worker-source.js';
+
+ var Source = proxyquire('../../../js/source/source', {
+ '../util/dispatcher': function () {
+ this.broadcast = function (type, params) {
+ if (type === 'load worker source') {
+ t.equal(Source.getType('bar'), SourceType);
+ t.equal(params.name, 'bar');
+ t.equal(params.url, 'worker-source.js');
+ t.end();
+ }
+ };
+ }
+ });
+
+ Source.addType('bar', SourceType, function (err) { t.error(err); });
+ });
+
+ t.test('throws for duplicate source type', function (t) {
+ Source.addType('source.test.type-3', function () {}, function (err) {
+ t.error(err);
+ t.throws(function () {
+ Source.addType('source.test.type-3', function () {}, function (err) {
+ t.error(err);
+ t.fail();
+ });
+ });
+ });
+ t.end();
+ });
+
+ t.end();
+});
diff --git a/test/js/style/style.test.js b/test/js/style/style.test.js
index 8fd77a4e2c9..e1cf2b24655 100644
--- a/test/js/style/style.test.js
+++ b/test/js/style/style.test.js
@@ -1238,56 +1238,3 @@ test('Style#query*Features', function(t) {
t.end();
});
-test('Style#addSourceType', function (t) {
- var _types = { 'existing': function () {} };
- var Style = proxyquire('../../../js/style/style', {
- '../source/source': {
- getType: function (name) { return _types[name]; },
- setType: function (name, create) { _types[name] = create; }
- }
- });
-
- t.test('adds factory function', function (t) {
- var style = new Style(createStyleJSON());
- var SourceType = function () {};
-
- // expect no call to load worker source
- style.dispatcher.broadcast = function (type) {
- if (type === 'load worker source') {
- t.fail();
- }
- };
-
- style.addSourceType('foo', SourceType, function () {
- t.equal(_types['foo'], SourceType);
- t.end();
- });
- });
-
- t.test('triggers workers to load worker source code', function (t) {
- var style = new Style(createStyleJSON());
- var SourceType = function () {};
- SourceType.workerSourceURL = 'worker-source.js';
-
- style.dispatcher.broadcast = function (type, params) {
- if (type === 'load worker source') {
- t.equal(_types['bar'], SourceType);
- t.equal(params.name, 'bar');
- t.equal(params.url, 'worker-source.js');
- t.end();
- }
- };
-
- style.addSourceType('bar', SourceType, function (err) { t.error(err); });
- });
-
- t.test('refuses to add new type over existing name', function (t) {
- var style = new Style(createStyleJSON());
- style.addSourceType('existing', function () {}, function (err) {
- t.ok(err);
- t.end();
- });
- });
-
- t.end();
-});