From 0357c2b80cbe4a97f0d4a29056f3dbf7f44b25c2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 7 Feb 2020 14:16:32 -0500 Subject: [PATCH] Rework presets.js file - WIP to fix resolving field inheritance --- modules/presets/preset.js | 197 +++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/modules/presets/preset.js b/modules/presets/preset.js index c8d0a8b8e..4dd702b3f 100644 --- a/modules/presets/preset.js +++ b/modules/presets/preset.js @@ -8,101 +8,42 @@ import { utilSafeClassName } from '../util/util'; // `presetPreset` decorates a given `preset` Object // with some extra methods for searching and matching geometry // -export function presetPreset(presetID, preset, fields, addable, rawPresets) { +export function presetPreset(presetID, preset, allFields, addable, rawPresets) { let _this = Object.assign({}, preset); // shallow copy + let _addable = addable || false; _this.id = presetID; - // for use in classes, element ids, css selectors - _this.safeid = utilSafeClassName(presetID); + _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids - _this.parentPresetID = () => { - const endIndex = _this.id.lastIndexOf('/'); - if (endIndex < 0) return null; - return _this.id.substring(0, endIndex); - }; + _this.originalTerms = (_this.terms || []).join(); + _this.originalName = _this.name || ''; - // For a preset without fields, use the fields of the parent _this. - // Replace {preset} placeholders with the fields of the specified presets. - function resolveFieldInheritance() { + _this.originalScore = _this.matchScore || 1; - // Skip `fields` for the keys which define the _this. - // These are usually `typeCombo` fields like `shop=*` - function shouldInheritFieldWithID(fieldID) { - const f = fields[fieldID]; - if (f.key) { - if (_this.tags[f.key] !== undefined && - // inherit anyway if multiple values are allowed or just a checkbox - f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check' - ) return false; - } - return true; - } + _this.originalReference = _this.reference || {}; - // returns an array of field IDs to inherit from the given presetID, if found - function inheritedFieldIDs(presetID, prop) { - if (!presetID) return null; + _this.fields = (_this.fields || []).map(f => allFields[f]); - const inheritPreset = rawPresets[presetID]; - if (!inheritPreset) return null; - - let inheritFieldIDs = inheritPreset[prop] || []; - if (prop === 'fields') { - inheritFieldIDs = inheritFieldIDs.filter(shouldInheritFieldWithID); - } - - return inheritFieldIDs; - } - - - ['fields', 'moreFields'].forEach(prop => { - let fieldIDs = []; - if (preset[prop] && preset[prop].length) { // fields were defined - preset[prop].forEach(fieldID => { - const match = fieldID.match(/\{(.*)\}/); - if (match !== null) { // presetID wrapped in braces {} - const inheritIDs = inheritedFieldIDs(match[1], prop); - if (inheritIDs !== null) { - fieldIDs = fieldIDs.concat(inheritIDs); - } else { - /* eslint-disable no-console */ - console.log(`Cannot resolve presetID ${match[0]} found in ${_this.id} ${prop}`); - /* eslint-enable no-console */ - } - } else { - fieldIDs.push(fieldID); // no braces - just a normal field - } - }); - - } else { // no fields defined, so use the parent's if possible - fieldIDs = inheritedFieldIDs(_this.parentPresetID(), prop); - } - fieldIDs = utilArrayUniq(fieldIDs); - preset[prop] = fieldIDs; - rawPresets[_this.id][prop] = fieldIDs; - }); - } + _this.moreFields = (_this.moreFields || []).map(f => allFields[f]); if (rawPresets) { resolveFieldInheritance(); } - _this.fields = (_this.fields || []).map(getFields); - _this.moreFields = (_this.moreFields || []).map(getFields); + _this.tags = _this.tags || {}; + + _this.addTags = _this.addTags || _this.tags; + + _this.removeTags = _this.removeTags || _this.addTags; + _this.geometry = (_this.geometry || []); - function getFields(f) { - return fields[f]; - } - - _this.matchGeometry = (geom) => _this.geometry.indexOf(geom) >= 0; _this.matchAllGeometry = (geoms) => geoms.every(_this.matchGeometry); - _this.originalScore = _this.matchScore || 1; - _this.matchScore = (entityTags) => { const tags = _this.tags; let seen = {}; @@ -140,9 +81,6 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { }; - _this.originalName = _this.name || ''; - - _this.name = () => { if (_this.suggestion) { let path = presetID.split('/'); @@ -154,27 +92,23 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { }; - _this.originalTerms = (_this.terms || []).join(); - _this.terms = () => _this.t('terms', { 'default': _this.originalTerms }) .toLowerCase().trim().split(/\s*,+\s*/); + _this.isFallback = () => { const tagCount = Object.keys(_this.tags).length; return tagCount === 0 || (tagCount === 1 && _this.tags.hasOwnProperty('area')); }; - addable = addable || false; - _this.addable = function(val) { - if (!arguments.length) return addable; - addable = val; - return addable; + if (!arguments.length) return _addable; + _addable = val; + return _this; }; - const _reference = _this.reference || {}; _this.reference = (geom) => { // Lookup documentation on Wikidata... const qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata']; @@ -183,8 +117,8 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { } // Lookup documentation on OSM Wikibase... - let key = _reference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0]; - let value = _reference.value || _this.tags[key]; + let key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0]; + let value = _this.originalReference.value || _this.tags[key]; if (geom === 'relation' && key === 'type') { if (value in _this.tags) { @@ -203,16 +137,15 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { }; - _this.removeTags = _this.removeTags || _this.addTags || _this.tags || {}; - - _this.unsetTags = (tags, geometry) => { + _this.unsetTags = (tags, geometry, skipFieldDefaults) => { tags = utilObjectOmit(tags, Object.keys(_this.removeTags)); - for (let f in _this.fields) { - const field = _this.fields[f]; - if (field.matchGeometry(geometry) && field.default === tags[field.key]) { - delete tags[field.key]; - } + if (geometry && !skipFieldDefaults) { + _this.fields.forEach(field => { + if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) { + delete tags[field.key]; + } + }); } delete tags.area; @@ -220,8 +153,6 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { }; - _this.addTags = _this.addTags || _this.tags || {}; - _this.setTags = (tags, geometry, skipFieldDefaults) => { const addTags = _this.addTags; tags = Object.assign({}, tags); // shallow copy @@ -255,18 +186,84 @@ export function presetPreset(presetID, preset, fields, addable, rawPresets) { } } } + if (geometry && !skipFieldDefaults) { - for (let f in _this.fields) { - const field = _this.fields[f]; + _this.fields.forEach(field => { if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) { tags[field.key] = field.default; } - } + }); } return tags; }; + // For a preset without fields, use the fields of the parent preset. + // Replace {preset} placeholders with the fields of the specified presets. + function resolveFieldInheritance() { + + ['fields', 'moreFields'].forEach(prop => { + let fieldIDs = []; + if (preset[prop] && preset[prop].length) { // fields were defined + preset[prop].forEach(fieldID => { + const match = fieldID.match(/\{(.*)\}/); + if (match !== null) { // presetID wrapped in braces {} + const inheritIDs = inheritedFieldIDs(match[1], prop); + if (inheritIDs !== null) { + fieldIDs = fieldIDs.concat(inheritIDs); + } else { + /* eslint-disable no-console */ + console.log(`Cannot resolve presetID ${match[0]} found in ${_this.id} ${prop}`); + /* eslint-enable no-console */ + } + } else { + fieldIDs.push(fieldID); // no braces - just a normal field + } + }); + + } else { // no fields defined, so use the parent's if possible + const endIndex = _this.id.lastIndexOf('/'); + const parentID = endIndex && _this.id.substring(0, endIndex); + if (parentID) { + fieldIDs = inheritedFieldIDs(parentID, prop); + } + } + + fieldIDs = utilArrayUniq(fieldIDs); + preset[prop] = fieldIDs; + rawPresets[_this.id][prop] = fieldIDs; + }); + + // Skip `fields` for the keys which define the _this. + // These are usually `typeCombo` fields like `shop=*` + function shouldInheritFieldWithID(fieldID) { + const f = allFields[fieldID]; + if (f.key) { + if (_this.tags[f.key] !== undefined && + // inherit anyway if multiple values are allowed or just a checkbox + f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check' + ) return false; + } + return true; + } + + // returns an array of field IDs to inherit from the given presetID, if found + function inheritedFieldIDs(presetID, prop) { + if (!presetID) return null; + + const inheritPreset = rawPresets[presetID]; + if (!inheritPreset) return null; + + let inheritFieldIDs = inheritPreset[prop] || []; + if (prop === 'fields') { + inheritFieldIDs = inheritFieldIDs.filter(shouldInheritFieldWithID); + } + + return inheritFieldIDs; + } + } + + return _this; }