mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-16 13:59:27 +02:00
Merge branch 'schema-builder-v5' into develop
This commit is contained in:
@@ -44,10 +44,12 @@ _Breaking developer changes, which may affect downstream projects or sites that
|
||||
#### :bug: Bugfixes
|
||||
* Fix selection of best background source when starting on a zoomed-out view ([#9325])
|
||||
#### :rocket: Presets
|
||||
* add support for tagging schema v5 ([#9320])
|
||||
* Render `natural=strait` features in blue color ([#9294])
|
||||
#### :hammer: Development
|
||||
|
||||
[#9294]: https://github.com/openstreetmap/iD/issues/9294
|
||||
[#9320]: https://github.com/openstreetmap/iD/pull/9320
|
||||
[#9325]: https://github.com/openstreetmap/iD/issues/9325
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import { utilSafeClassName } from '../util/util';
|
||||
// `presetField` decorates a given `field` Object
|
||||
// with some extra methods for searching and matching geometry
|
||||
//
|
||||
export function presetField(fieldID, field) {
|
||||
export function presetField(fieldID, field, allFields) {
|
||||
allFields = allFields || {};
|
||||
let _this = Object.assign({}, field); // shallow copy
|
||||
|
||||
_this.id = fieldID;
|
||||
@@ -25,17 +26,29 @@ export function presetField(fieldID, field) {
|
||||
_this.t.append = (scope, options) => t.append(`_tagging.presets.fields.${fieldID}.${scope}`, options);
|
||||
_this.hasTextForStringId = (scope) => localizer.hasTextForStringId(`_tagging.presets.fields.${fieldID}.${scope}`);
|
||||
|
||||
_this.title = () => _this.overrideLabel || _this.t('label', { 'default': fieldID });
|
||||
_this.resolveReference = which => {
|
||||
const referenceRegex = /^\{(.*)\}$/;
|
||||
const match = (field[which] || '').match(referenceRegex);
|
||||
if (match) {
|
||||
const field = allFields[match[1]];
|
||||
if (field) {
|
||||
return field;
|
||||
}
|
||||
console.error(`Unable to resolve referenced field: ${match[1]}`); // eslint-disable-line no-console
|
||||
}
|
||||
return _this;
|
||||
};
|
||||
|
||||
_this.title = () => _this.overrideLabel || _this.resolveReference('label').t('label', { 'default': fieldID });
|
||||
_this.label = () => _this.overrideLabel ?
|
||||
selection => selection.text(_this.overrideLabel) :
|
||||
_this.t.append('label', { 'default': fieldID });
|
||||
_this.resolveReference('label').t.append('label', { 'default': fieldID });
|
||||
|
||||
const _placeholder = _this.placeholder;
|
||||
_this.placeholder = () => _this.t('placeholder', { 'default': _placeholder });
|
||||
_this.placeholder = () => _this.resolveReference('placeholder').t('placeholder', { 'default': '' });
|
||||
|
||||
_this.originalTerms = (_this.terms || []).join();
|
||||
|
||||
_this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
|
||||
_this.terms = () => _this.resolveReference('label').t('terms', { 'default': _this.originalTerms })
|
||||
.toLowerCase().trim().split(/\s*,+\s*/);
|
||||
|
||||
_this.increment = _this.type === 'number' ? (_this.increment || 1) : undefined;
|
||||
|
||||
@@ -96,7 +96,7 @@ export function presetIndex() {
|
||||
let f = d.fields[fieldID];
|
||||
|
||||
if (f) { // add or replace
|
||||
f = presetField(fieldID, f);
|
||||
f = presetField(fieldID, f, _fields);
|
||||
if (f.locationSet) newLocationSets.push(f);
|
||||
_fields[fieldID] = f;
|
||||
|
||||
|
||||
+36
-15
@@ -11,15 +11,17 @@ import { utilSafeClassName } from '../util/util';
|
||||
export function presetPreset(presetID, preset, addable, allFields, allPresets) {
|
||||
allFields = allFields || {};
|
||||
allPresets = allPresets || {};
|
||||
let _this = Object.assign({}, preset); // shallow copy
|
||||
let _this = Object.assign({}, preset); // shallow copy
|
||||
let _addable = addable || false;
|
||||
let _resolvedFields; // cache
|
||||
let _resolvedMoreFields; // cache
|
||||
let _searchName; // cache
|
||||
let _searchNameStripped; // cache
|
||||
let _searchAliases; // cache
|
||||
let _resolvedFields; // cache
|
||||
let _resolvedMoreFields; // cache
|
||||
let _searchName; // cache
|
||||
let _searchNameStripped; // cache
|
||||
let _searchAliases; // cache
|
||||
let _searchAliasesStripped; // cache
|
||||
|
||||
const referenceRegex = /^\{(.*)\}$/;
|
||||
|
||||
_this.id = presetID;
|
||||
|
||||
_this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
|
||||
@@ -38,9 +40,9 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
|
||||
|
||||
_this.originalMoreFields = (_this.moreFields || []);
|
||||
|
||||
_this.fields = () => _resolvedFields || (_resolvedFields = resolve('fields'));
|
||||
_this.fields = () => _resolvedFields || (_resolvedFields = resolveFields('fields'));
|
||||
|
||||
_this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
|
||||
_this.moreFields = () => _resolvedMoreFields || (_resolvedMoreFields = resolveFields('moreFields'));
|
||||
|
||||
_this.resetFields = () => _resolvedFields = _resolvedMoreFields = null;
|
||||
|
||||
@@ -99,12 +101,27 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
|
||||
return t.append(textID, options);
|
||||
};
|
||||
|
||||
function resolveReference(which) {
|
||||
const match = (_this[which] || '').match(referenceRegex);
|
||||
if (match) {
|
||||
const preset = allPresets[match[1]];
|
||||
if (preset) {
|
||||
return preset;
|
||||
}
|
||||
console.error(`Unable to resolve referenced preset: ${match[1]}`); // eslint-disable-line no-console
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
|
||||
_this.name = () => {
|
||||
return _this.t('name', { 'default': _this.originalName });
|
||||
return resolveReference('originalName')
|
||||
.t('name', { 'default': _this.originalName || presetID });
|
||||
};
|
||||
|
||||
_this.nameLabel = () => _this.t.append('name', { 'default': _this.originalName });
|
||||
_this.nameLabel = () => {
|
||||
return resolveReference('originalName')
|
||||
.t.append('name', { 'default': _this.originalName || presetID });
|
||||
};
|
||||
|
||||
_this.subtitle = () => {
|
||||
if (_this.suggestion) {
|
||||
@@ -125,11 +142,15 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
|
||||
};
|
||||
|
||||
_this.aliases = () => {
|
||||
return _this.t('aliases', { 'default': _this.originalAliases }).trim().split(/\s*[\r\n]+\s*/);
|
||||
return resolveReference('originalName')
|
||||
.t('aliases', { 'default': _this.originalAliases }).trim().split(/\s*[\r\n]+\s*/);
|
||||
};
|
||||
|
||||
_this.terms = () => _this.t('terms', { 'default': _this.originalTerms })
|
||||
.toLowerCase().trim().split(/\s*,+\s*/);
|
||||
_this.terms = () => {
|
||||
return resolveReference('originalName')
|
||||
.t('terms', { 'default': _this.originalTerms })
|
||||
.toLowerCase().trim().split(/\s*,+\s*/);
|
||||
};
|
||||
|
||||
_this.searchName = () => {
|
||||
if (!_searchName) {
|
||||
@@ -267,12 +288,12 @@ export function presetPreset(presetID, preset, addable, allFields, allPresets) {
|
||||
|
||||
// For a preset without fields, use the fields of the parent preset.
|
||||
// Replace {preset} placeholders with the fields of the specified presets.
|
||||
function resolve(which) {
|
||||
function resolveFields(which) {
|
||||
const fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
|
||||
let resolved = [];
|
||||
|
||||
fieldIDs.forEach(fieldID => {
|
||||
const match = fieldID.match(/\{(.*)\}/);
|
||||
const match = fieldID.match(referenceRegex);
|
||||
if (match !== null) { // a presetID wrapped in braces {}
|
||||
resolved = resolved.concat(inheritFields(match[1], which));
|
||||
} else if (allFields[fieldID]) { // a normal fieldID
|
||||
|
||||
@@ -98,9 +98,10 @@ export function uiFieldAccess(field, context) {
|
||||
options.splice(options.length - 4, 0, 'dismount');
|
||||
}
|
||||
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
return options.map(function(option) {
|
||||
return {
|
||||
title: field.t('options.' + option + '.description'),
|
||||
title: stringsField.t('options.' + option + '.description'),
|
||||
value: option
|
||||
};
|
||||
});
|
||||
|
||||
@@ -33,10 +33,11 @@ export function uiFieldCheck(field, context) {
|
||||
|
||||
|
||||
if (options) {
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
for (var i in options) {
|
||||
var v = options[i];
|
||||
values.push(v === 'undefined' ? undefined : v);
|
||||
texts.push(field.t.html('options.' + v, { 'default': v }));
|
||||
texts.push(stringsField.t.html('options.' + v, { 'default': v }));
|
||||
}
|
||||
} else {
|
||||
values = [undefined, 'yes'];
|
||||
|
||||
@@ -90,8 +90,9 @@ export function uiFieldCombo(field, context) {
|
||||
function displayValue(tval) {
|
||||
tval = tval || '';
|
||||
|
||||
if (field.hasTextForStringId('options.' + tval)) {
|
||||
return field.t('options.' + tval, { default: tval });
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
if (stringsField.hasTextForStringId('options.' + tval)) {
|
||||
return stringsField.t('options.' + tval, { default: tval });
|
||||
}
|
||||
|
||||
if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
|
||||
@@ -107,8 +108,9 @@ export function uiFieldCombo(field, context) {
|
||||
function renderValue(tval) {
|
||||
tval = tval || '';
|
||||
|
||||
if (field.hasTextForStringId('options.' + tval)) {
|
||||
return field.t.append('options.' + tval, { default: tval });
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
if (stringsField.hasTextForStringId('options.' + tval)) {
|
||||
return stringsField.t.append('options.' + tval, { default: tval });
|
||||
}
|
||||
|
||||
if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
|
||||
|
||||
@@ -115,9 +115,10 @@ export function uiFieldCycleway(field, context) {
|
||||
|
||||
|
||||
cycleway.options = function() {
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
return field.options.map(function(option) {
|
||||
return {
|
||||
title: field.t('options.' + option + '.description'),
|
||||
title: stringsField.t('options.' + option + '.description'),
|
||||
value: option
|
||||
};
|
||||
});
|
||||
|
||||
@@ -55,16 +55,17 @@ export function uiFieldRadio(field, context) {
|
||||
enter = labels.enter()
|
||||
.append('label');
|
||||
|
||||
var stringsField = field.resolveReference('stringsCrossReference');
|
||||
enter
|
||||
.append('input')
|
||||
.attr('type', 'radio')
|
||||
.attr('name', field.id)
|
||||
.attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
|
||||
.attr('value', function(d) { return stringsField.t('options.' + d, { 'default': d }); })
|
||||
.attr('checked', false);
|
||||
|
||||
enter
|
||||
.append('span')
|
||||
.html(function(d) { return field.t.html('options.' + d, { 'default': d }); });
|
||||
.each(function(d) { stringsField.t.append('options.' + d, { 'default': d })(d3_select(this)); });
|
||||
|
||||
labels = labels
|
||||
.merge(enter);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
describe('iD.presetField', function() {
|
||||
describe('#references', function() {
|
||||
it('references label and terms of another field', function() {
|
||||
var allFields = {};
|
||||
var other = iD.presetField('other', {}, allFields);
|
||||
var field = iD.presetField('test', {label: '{other}'}, allFields);
|
||||
allFields.other = other;
|
||||
allFields.preset = field;
|
||||
|
||||
// mock localizer
|
||||
sinon.spy(other, 't');
|
||||
sinon.spy(field, 't');
|
||||
|
||||
field.title();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(field.t).not.to.have.been.called;
|
||||
|
||||
other.t.resetHistory();
|
||||
field.t.resetHistory();
|
||||
|
||||
field.terms();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(field.t).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('references placeholder of another field', function() {
|
||||
var allFields = {};
|
||||
var other = iD.presetField('other', {}, allFields);
|
||||
var field = iD.presetField('test', {placeholder: '{other}'}, allFields);
|
||||
allFields.other = other;
|
||||
allFields.preset = field;
|
||||
|
||||
// mock localizer
|
||||
sinon.spy(other, 't');
|
||||
sinon.spy(field, 't');
|
||||
|
||||
field.placeholder();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(field.t).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('references string options of another field', function() {
|
||||
var allFields = {};
|
||||
var other = iD.presetField('other', {}, allFields);
|
||||
var field = iD.presetField('test', {stringsCrossReference: '{other}', options: ['v'], key: 'k'}, allFields);
|
||||
allFields.other = other;
|
||||
allFields.preset = field;
|
||||
|
||||
// mock localizer
|
||||
sinon.spy(other, 't');
|
||||
sinon.spy(field, 't');
|
||||
sinon.stub(other, 'hasTextForStringId').returns(true);
|
||||
|
||||
var context = iD.coreContext().assetPath('../dist/').init();
|
||||
var uiField = iD.uiFieldCombo(field, context);
|
||||
uiField.tags({k: 'v'});
|
||||
expect(field.t).not.to.have.been.called;
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -228,4 +228,36 @@ describe('iD.presetPreset', function() {
|
||||
expect(preset.addable()).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#references', function() {
|
||||
it('references name, aliases and terms of another preset', function() {
|
||||
var allPresets = {};
|
||||
var other = iD.presetPreset('other', {}, undefined, undefined, allPresets);
|
||||
var preset = iD.presetPreset('test', {name: '{other}'}, undefined, undefined, allPresets);
|
||||
allPresets.other = other;
|
||||
allPresets.preset = preset;
|
||||
|
||||
// mock localizer
|
||||
sinon.spy(other, 't');
|
||||
sinon.spy(preset, 't');
|
||||
|
||||
preset.name();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(preset.t).not.to.have.been.called;
|
||||
|
||||
other.t.resetHistory();
|
||||
preset.t.resetHistory();
|
||||
|
||||
preset.aliases();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(preset.t).not.to.have.been.called;
|
||||
|
||||
other.t.resetHistory();
|
||||
preset.t.resetHistory();
|
||||
|
||||
preset.terms();
|
||||
expect(other.t).to.have.been.calledOnce;
|
||||
expect(preset.t).not.to.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user