diff --git a/API.md b/API.md
index 8038fa3df..0ed7f2705 100644
--- a/API.md
+++ b/API.md
@@ -145,7 +145,7 @@ certain parts of the iD code to be replaced at runtime by custom code or data.
iD is written in a modular style and bundled with [rollup.js](http://rollupjs.org/),
which makes hot code replacement tricky. (ES6 module exports are
-[immutable bindings](http://www.2ality.com/2015/07/es6-module-exports.html)).
+[immutable live bindings](http://www.2ality.com/2015/07/es6-module-exports.html)).
Because of this, the parts of iD which are designed for customization are exported
as live bound objects that can be overriden at runtime _before initializing the iD context_.
@@ -169,7 +169,9 @@ delete iD.services.mapillary;
### Background Imagery
iD's background imagery database is stored in the `iD.data.imagery` array and can be
-overridden. (Note that the "None" and "Custom" options will always be shown in the list)
+overridden or modified prior to creating the iD context.
+
+Note that the "None" and "Custom" options will always be shown in the list.
To remove all imagery from iD:
```js
@@ -213,16 +215,59 @@ For more details about the `iD.data.imagery` structure, see
### Presets
-iD can use external presets exclusively or along with the default OpenStreetMap presets. This is configured using the `context.presets` accessor. To use external presets alone, initialize the iD context with a custom `Presets` object:
+iD's preset database is stored in the `iD.data.presets` object and can be overridden
+or modified prior to creating the iD context.
+The format of the `presets` object is
+[documented here](https://github.com/openstreetmap/iD/tree/master/data/presets#custom-presets).
+
+To add a new preset to iD's existing preset database.
```js
-
-var id = iD.Context()
- .presets(customPresets);
-
+iD.data.presets.presets["aerialway/zipline"] = {
+ geometry: ["line"],
+ fields: ["incline"],
+ tags: { "aerialway": "zip_line" },
+ name: "Zipline"
+};
```
-The format of the Preset object is [documented here](https://github.com/openstreetmap/iD/tree/master/data/presets#custom-presets).
+To completely replace iD's default presets with your own:
+```js
+iD.data.presets = myPresets;
+```
+
+To run iD with the minimal set of presets that only match basic geometry types:
+```js
+iD.data.presets = {
+ presets: {
+ "area": {
+ "name": "Area",
+ "tags": {},
+ "geometry": ["area"]
+ },
+ "line": {
+ "name": "Line",
+ "tags": {},
+ "geometry": ["line"]
+ },
+ "point": {
+ "name": "Point",
+ "tags": {},
+ "geometry": ["point"]
+ },
+ "vertex": {
+ "name": "Vertex",
+ "tags": {},
+ "geometry": ["vertex"]
+ },
+ "relation": {
+ "name": "Relation",
+ "tags": {},
+ "geometry": ["relation"]
+ }
+ }
+};
+```
### Minimum Editable Zoom
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 233eec336..a7422101f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@
* :warning: Flattened namespace means that all functions have changed names (#3479)
* e.g. `iD.actions.Move` -> `iD.actionMove`, `iD.geo.Extent` -> `iD.geoExtent`
* Many deprecated names are still exported as symbols, e.g. `iD.Context` - we will remove these eventually
+* :warning: Customized iD deployments can manipulate live objects, rather than iD.Context accessors
+ * No longer need to call things like `presets()`, `imagery()`, `taginfo()` when creating `iD.Context`
+ * See [API.md](https://github.com/openstreetmap/iD/blob/master/API.md#customized-deployments) for details on customized deployments
* :warning: iD has upgraded to the latest released versions of d3, lodash, rbush, etc.
* d3 no longer adds itself to the global namespace, but can now be accessed via `iD.d3`
* :warning: iD now uses `npm` scripts for all build processes
diff --git a/data/index.js b/data/index.js
index 970d5544d..3387e7524 100644
--- a/data/index.js
+++ b/data/index.js
@@ -12,19 +12,18 @@ export { default as dataImperial } from './imperial.json';
export { default as dataDriveLeft } from './drive-left.json';
export { en as dataEn } from '../dist/locales/en.json';
+import { dataImagery } from './imagery.json';
import { presets } from './presets/presets.json';
import { defaults } from './presets/defaults.json';
import { categories } from './presets/categories.json';
import { fields } from './presets/fields.json';
-export var dataPresets = {
- presets: presets,
- defaults: defaults,
- categories: categories,
- fields: fields
-};
-
-import { dataImagery } from './imagery.json';
export var data = {
- imagery: dataImagery
+ imagery: dataImagery,
+ presets: {
+ presets: presets,
+ defaults: defaults,
+ categories: categories,
+ fields: fields
+ }
};
diff --git a/data/presets/README.md b/data/presets/README.md
index ac5d0986f..42114967c 100644
--- a/data/presets/README.md
+++ b/data/presets/README.md
@@ -274,6 +274,12 @@ For example:
"tags": {},
"geometry": ["vertex"],
"matchScore": 0.1
+},
+"relation": {
+ "name": "Relation",
+ "tags": {},
+ "geometry": ["relation"],
+ "matchScore": 0.1
}
```
diff --git a/dist/index.html b/dist/index.html
index 99f55093e..7ea898aaf 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -37,9 +37,7 @@
document.getElementById('id-container').innerHTML = 'Sorry, your browser is not currently supported. Please use Potlatch 2 to edit the map.';
document.getElementById('id-container').className = 'unsupported';
} else {
- var id = iD.Context()
- .presets(iD.dataPresets);
-
+ var id = iD.Context();
id.ui()(document.getElementById('id-container'));
}
diff --git a/index.html b/index.html
index be8418bbd..7168ff60b 100644
--- a/index.html
+++ b/index.html
@@ -19,7 +19,6 @@
-
+
diff --git a/test/rendering.html b/test/rendering.html
index 7573a3a0d..ae3c5d69a 100644
--- a/test/rendering.html
+++ b/test/rendering.html
@@ -46,7 +46,7 @@
};
context.presets = function() {
- return iD.presetInit().load({
+ return iD.presetIndex().load({
presets: {
'amenity/restaurant': {
geometry: ['point'],
diff --git a/test/spec/actions/split.js b/test/spec/actions/split.js
index 94c304a51..009c71195 100644
--- a/test/spec/actions/split.js
+++ b/test/spec/actions/split.js
@@ -1,8 +1,7 @@
describe('iD.actionSplit', function () {
beforeEach(function () {
- iD.areaKeys = iD.Context()
- .presets(iD.dataPresets).presets().areaKeys();
+ iD.areaKeys = iD.Context().presets().areaKeys();
});
describe('#disabled', function () {
diff --git a/test/spec/core/context.js b/test/spec/core/context.js
index 036b79d27..04f665400 100644
--- a/test/spec/core/context.js
+++ b/test/spec/core/context.js
@@ -51,54 +51,6 @@ describe('iD.Context', function() {
});
});
- describe('#presets', function() {
- it('supports custom presets', function() {
- var presetsCollection = {
- presets: {
- 'mines': {
- geometry: ['point', 'area'],
- name: 'Mining Concession',
- tags: { 'concession': 'mining' }
- },
- 'area': {
- 'name': 'Area',
- 'tags': {},
- 'geometry': ['area']
- },
- 'point': {
- 'name': 'Point',
- 'tags': {},
- 'geometry': ['point']
- },
- 'line': {
- 'name': 'Line',
- 'tags': {},
- 'geometry': ['line']
- },
- 'vertex': {
- 'name': 'Other',
- 'tags': {},
- 'geometry': ['vertex']
- }
- },
- fields: {
- 'name': {
- 'key': 'name',
- 'type': 'localized',
- 'label': 'Name',
- 'placeholder': 'Common name (if any)'
- }
- }
- };
-
- var context = iD.Context().presets(presetsCollection),
- way = iD.Way({tags: {concession: 'mining', area: 'yes'}}),
- graph = iD.Graph([way]);
-
- expect(context.presets().match(way, graph).id).to.eql('mines');
- });
- });
-
describe('#debug', function() {
it('sets and gets debug flags', function() {
var context = iD.Context(),
diff --git a/test/spec/modes/add_point.js b/test/spec/modes/add_point.js
index b8fff59f8..66916c577 100644
--- a/test/spec/modes/add_point.js
+++ b/test/spec/modes/add_point.js
@@ -5,7 +5,6 @@ describe.skip('iD.modeAddPoint', function() {
var container = d3.select(document.createElement('div'));
context = iD.Context()
- .presets(iD.dataPresets)
.container(container);
context.loadTiles = function () {};
diff --git a/test/spec/osm/way.js b/test/spec/osm/way.js
index a5879cdf2..a49ea4082 100644
--- a/test/spec/osm/way.js
+++ b/test/spec/osm/way.js
@@ -318,7 +318,7 @@ describe('iD.osmWay', function() {
describe('#isArea', function() {
before(function() {
- iD.Context().presets(iD.dataPresets);
+ iD.Context();
});
it('returns false when the way has no tags', function() {
diff --git a/test/spec/presets/init.js b/test/spec/presets/index.js
similarity index 55%
rename from test/spec/presets/init.js
rename to test/spec/presets/index.js
index e14a18f61..163dd9a2d 100644
--- a/test/spec/presets/init.js
+++ b/test/spec/presets/index.js
@@ -1,65 +1,84 @@
-describe('iD.presetInit', function() {
- var p = {
- point: {
- tags: {},
- geometry: ['point']
- },
- line: {
- tags: {},
- geometry: ['line']
- },
- vertex: {
- tags: {},
- geometry: ['vertex']
- },
- residential: {
- tags: { highway: 'residential' },
- geometry: ['line']
- },
- park: {
- tags: { leisure: 'park' },
- geometry: ['point', 'area']
- }
- };
+describe('iD.presetIndex', function() {
+ var savedPresets;
- var c = iD.presetInit().load({presets: p});
+ before(function () {
+ savedPresets = iD.data.presets;
+ });
+
+ after(function () {
+ iD.data.presets = savedPresets;
+ });
describe('#match', function() {
+ var testPresets = {
+ presets: {
+ point: {
+ tags: {},
+ geometry: ['point']
+ },
+ line: {
+ tags: {},
+ geometry: ['line']
+ },
+ vertex: {
+ tags: {},
+ geometry: ['vertex']
+ },
+ residential: {
+ tags: { highway: 'residential' },
+ geometry: ['line']
+ },
+ park: {
+ tags: { leisure: 'park' },
+ geometry: ['point', 'area']
+ }
+ }
+ };
+
it('returns a collection containing presets matching a geometry and tags', function() {
- var way = iD.Way({ tags: { highway: 'residential' } }),
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets(),
+ way = iD.Way({ tags: { highway: 'residential' } }),
graph = iD.Graph([way]);
- expect(c.match(way, graph).id).to.eql('residential');
+
+ expect(presets.match(way, graph).id).to.eql('residential');
});
it('returns the appropriate fallback preset when no tags match', function() {
- var point = iD.Node(),
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets(),
+ point = iD.Node(),
line = iD.Way({ tags: { foo: 'bar' } }),
graph = iD.Graph([point, line]);
- expect(c.match(point, graph).id).to.eql('point');
- expect(c.match(line, graph).id).to.eql('line');
+ expect(presets.match(point, graph).id).to.eql('point');
+ expect(presets.match(line, graph).id).to.eql('line');
});
it('matches vertices on a line as vertices', function() {
- var point = iD.Node({ tags: { leisure: 'park' } }),
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets(),
+ point = iD.Node({ tags: { leisure: 'park' } }),
line = iD.Way({ nodes: [point.id], tags: { 'highway': 'residential' } }),
graph = iD.Graph([point, line]);
- expect(c.match(point, graph).id).to.eql('vertex');
+ expect(presets.match(point, graph).id).to.eql('vertex');
});
it('matches vertices on an addr:interpolation line as points', function() {
- var point = iD.Node({ tags: { leisure: 'park' } }),
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets(),
+ point = iD.Node({ tags: { leisure: 'park' } }),
line = iD.Way({ nodes: [point.id], tags: { 'addr:interpolation': 'even' } }),
graph = iD.Graph([point, line]);
- expect(c.match(point, graph).id).to.eql('park');
+ expect(presets.match(point, graph).id).to.eql('park');
});
-
});
+
describe('#areaKeys', function() {
- var presets = iD.presetInit().load({
+ var testPresets = {
presets: {
'amenity/fuel/shell': {
tags: { 'amenity': 'fuel' },
@@ -91,62 +110,78 @@ describe('iD.presetInit', function() {
geometry: ['point', 'area']
}
}
- });
+ };
it('whitelists keys for presets with area geometry', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys()).to.include.keys('natural');
});
it('blacklists key-values for presets with a line geometry', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys().natural).to.include.keys('tree_row');
expect(presets.areaKeys().natural.tree_row).to.be.true;
});
it('does not blacklist key-values for presets with both area and line geometry', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys().golf).not.to.include.keys('water_hazard');
});
it('does not blacklist key-values for presets with neither area nor line geometry', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys().natural).not.to.include.keys('peak');
});
it('does not blacklist generic \'*\' key-values', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys().natural).not.to.include.keys('natural');
});
it('ignores keys like \'highway\' that are assumed to be lines', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys()).not.to.include.keys('highway');
});
it('ignores suggestion presets', function() {
+ iD.data.presets = testPresets;
+ var presets = iD.Context().presets();
expect(presets.areaKeys()).not.to.include.keys('amenity');
});
-
});
- describe('expected matches', function() {
- var presets;
- before(function() {
- presets = iD.presetInit().load(iD.dataPresets);
- });
+ describe('expected matches', function() {
it('prefers building to multipolygon', function() {
- var relation = iD.Relation({tags: {type: 'multipolygon', building: 'yes'}}),
- graph = iD.Graph([relation]);
+ iD.data.presets = savedPresets;
+ var presets = iD.Context().presets(),
+ relation = iD.Relation({ tags: { type: 'multipolygon', building: 'yes' }}),
+ graph = iD.Graph([relation]);
expect(presets.match(relation, graph).id).to.eql('building');
});
it('prefers building to address', function() {
- var way = iD.Way({tags: {area: 'yes', building: 'yes', 'addr:housenumber': '1234'}}),
+ iD.data.presets = savedPresets;
+ var presets = iD.Context().presets(),
+ way = iD.Way({ tags: { area: 'yes', building: 'yes', 'addr:housenumber': '1234' }}),
graph = iD.Graph([way]);
expect(presets.match(way, graph).id).to.eql('building');
});
it('prefers pedestrian to area', function() {
- var way = iD.Way({tags: {area: 'yes', highway: 'pedestrian'}}),
+ iD.data.presets = savedPresets;
+ var presets = iD.Context().presets(),
+ way = iD.Way({ tags: { area: 'yes', highway: 'pedestrian' }}),
graph = iD.Graph([way]);
expect(presets.match(way, graph).id).to.eql('highway/pedestrian');
});
});
+
});
diff --git a/test/spec/ui/fields/access.js b/test/spec/ui/fields/access.js
index 772a543ee..4e84c5dd6 100644
--- a/test/spec/ui/fields/access.js
+++ b/test/spec/ui/fields/access.js
@@ -2,8 +2,7 @@ describe('iD.uiFieldAccess', function() {
var selection, field;
beforeEach(function() {
selection = d3.select(document.createElement('div'));
- field = iD.Context()
- .presets(iD.dataPresets).presets().field('access');
+ field = iD.Context().presets().field('access');
});
it('creates inputs for a variety of modes of access', function() {
diff --git a/test/spec/ui/fields/wikipedia.js b/test/spec/ui/fields/wikipedia.js
index 237a21671..6ffc13a3a 100644
--- a/test/spec/ui/fields/wikipedia.js
+++ b/test/spec/ui/fields/wikipedia.js
@@ -14,7 +14,7 @@ describe('iD.uiFieldWikipedia', function() {
context = iD.Context();
context.history().merge([entity]);
selection = d3.select(document.createElement('div'));
- field = context.presets(iD.dataPresets).presets().field('wikipedia');
+ field = context.presets().field('wikipedia');
window.JSONP_DELAY = 0;
window.JSONP_FIX = {
entities: {
@@ -114,7 +114,7 @@ describe('iD.uiFieldWikipedia', function() {
// skip delayed wikidata for 'Skip' // 'Skip' wikidata +20ms
expect(spy.getCall(4)).to.have.been.calledWith({ wikipedia: 'de:Title', wikidata: 'Q216353' }); // 'Title' wikidata +40ms
done();
- }, 50);
+ }, 100);
});
it('does not set delayed wikidata tag if selected entity has changed', function(done) {
@@ -140,7 +140,7 @@ describe('iD.uiFieldWikipedia', function() {
expect(spy.getCall(1)).to.have.been.calledWith({ wikipedia: 'de:Title' }); // 'Title' on blur
// wikidata tag not changed because another entity is now selected
done();
- }, 50);
+ }, 100);
});
});