diff --git a/css/80_app.css b/css/80_app.css index aeba94aea..fa948cefa 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3909,6 +3909,10 @@ svg.mouseclick use.right { background: #ffb; } +.mode-save .error-section { + background: #ffa5a5; +} + .mode-save .warning-section .changeset-list button { border-left: 1px solid #ccc; } diff --git a/modules/core/context.js b/modules/core/context.js index a69b5f882..5fd428894 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -7,6 +7,8 @@ import _isObject from 'lodash-es/isObject'; import _isString from 'lodash-es/isString'; import _map from 'lodash-es/map'; +import mapcssParse from 'mapcss-parse'; + import { dispatch as d3_dispatch } from 'd3-dispatch'; import { @@ -455,14 +457,14 @@ export function coreContext() { locale = locale.split('-')[0]; } - // if (utilExternalValidationRules()) { - // var validationsUrl = utilStringQs(window.location.hash).validations; - // d3_text(validationsUrl, function (err, mapcss) { - // if (err) return; - // var validations = _map(mapcssParse(mapcss), function(mapcssConfig) { return utilMapCSSRule(mapcssConfig); }); - // context.validationRules = function() { return validations; }; - // }); - // } + if (utilExternalValidationRules()) { + var validationsUrl = utilStringQs(window.location.hash).validations; + d3_text(validationsUrl, function (err, mapcss) { + if (err) return; + var validations = _map(mapcssParse(mapcss), function(mapcssConfig) { return utilMapCSSRule(mapcssConfig, context.presets().areaKeys()); }); + context.validationRules = function() { return validations; }; + }); + } history = coreHistory(context); context.graph = history.graph; diff --git a/modules/core/history.js b/modules/core/history.js index 9e45f17cb..cd7dbffbe 100644 --- a/modules/core/history.js +++ b/modules/core/history.js @@ -9,6 +9,7 @@ import _isEmpty from 'lodash-es/isEmpty'; import _forEach from 'lodash-es/forEach'; import _map from 'lodash-es/map'; import _omit from 'lodash-es/omit'; +import _pickBy from 'lodash-es/pickBy'; import _reject from 'lodash-es/reject'; import _values from 'lodash-es/values'; import _without from 'lodash-es/without'; @@ -281,15 +282,16 @@ export function coreHistory(context) { validate: function(changes) { + // strip mapcss checks if no mapcss to check against. + var validationsToRun = _pickBy(Validations, function(validation) { + return validation === Validations.validationMapCSSChecks && context.hasOwnProperty('validationRules') || + validation !== Validations.validationMapCSSChecks; + }); return _flatten( - _map(Validations, function(fn) { - var warnings; - if (fn === Validations.validationMapCSSChecks && context.hasOwnProperty('validationRules')) { - warnings = fn()(changes, _stack[_index].graph, context.validationRules()); - } else { - warnings = fn()(changes, _stack[_index].graph); - } - return warnings; + _map(validationsToRun, function(fn, name) { + return name === 'validationMapCSSChecks' ? + fn()(changes, _stack[_index].graph, context.validationRules()) : + fn()(changes, _stack[_index].graph); }) ); }, diff --git a/modules/presets/index.js b/modules/presets/index.js index 04c61cfba..ec47773dd 100644 --- a/modules/presets/index.js +++ b/modules/presets/index.js @@ -121,7 +121,7 @@ export function presetIndex() { areaKeys[key][value] = true; } }); - + console.log(areaKeys); return areaKeys; }; diff --git a/modules/ui/commit_warnings.js b/modules/ui/commit_warnings.js index 0c83cfbf5..74174772a 100644 --- a/modules/ui/commit_warnings.js +++ b/modules/ui/commit_warnings.js @@ -3,91 +3,107 @@ import { modeSelect } from '../modes'; import { svgIcon } from '../svg'; import { tooltip } from '../util/tooltip'; import { utilEntityOrMemberSelector } from '../util'; - +import _reduce from 'lodash-es/reduce'; +import _forEach from 'lodash-es/forEach'; export function uiCommitWarnings(context) { function commitWarnings(selection) { var changes = context.history().changes(); - var warnings = context.history().validate(changes); + var validations = context.history().validate(changes); - var container = selection.selectAll('.warning-section') - .data(warnings.length ? [0] : []); - - container.exit() - .remove(); - - var containerEnter = container.enter() - .append('div') - .attr('class', 'modal-section warning-section fillL2'); - - containerEnter - .append('h3') - .text(t('commit.warnings')); - - containerEnter - .append('ul') - .attr('class', 'changeset-list'); - - container = containerEnter - .merge(container); - - - var items = container.select('ul').selectAll('li') - .data(warnings); - - items.exit() - .remove(); - - var itemsEnter = items.enter() - .append('li') - .attr('class', 'warning-item'); - - itemsEnter - .call(svgIcon('#iD-icon-alert', 'pre-text')); - - itemsEnter - .append('strong') - .text(function(d) { return d.message; }); - - itemsEnter.filter(function(d) { return d.tooltip; }) - .call(tooltip() - .title(function(d) { return d.tooltip; }) - .placement('top') - ); - - items = itemsEnter - .merge(items); - - items - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .on('click', warningClick); - - - function mouseover(d) { - if (d.entity) { - context.surface().selectAll( - utilEntityOrMemberSelector([d.entity.id], context.graph()) - ).classed('hover', true); + validations = _reduce(validations, function(validations, val) { + var type = val.id === 'mapcss_error' ? 'error' : 'warning'; + if (validations.hasOwnProperty(type)) { + validations[type].push(val); + } else { + validations[type] = [val]; } - } + return validations; + }, {}); + + _forEach(validations, function(instances, type) { + + var section = type + '-section'; + var instanceItem = type + '-item'; + + var container = selection.selectAll('.' + section) + .data(instances.length ? [0] : []); + + container.exit() + .remove(); + + var containerEnter = container.enter() + .append('div') + .attr('class', 'modal-section ' + section + ' fillL2'); + + containerEnter + .append('h3') + .text(type === 'warning' ? t('commit.warnings') : t('commit.errors')); + + containerEnter + .append('ul') + .attr('class', 'changeset-list'); + + container = containerEnter + .merge(container); - function mouseout() { - context.surface().selectAll('.hover') - .classed('hover', false); - } + var items = container.select('ul').selectAll('li') + .data(instances); + + items.exit() + .remove(); + + var itemsEnter = items.enter() + .append('li') + .attr('class', instanceItem); + + itemsEnter + .call(svgIcon('#iD-icon-alert', 'pre-text')); + + itemsEnter + .append('strong') + .text(function(d) { return d.message; }); + + itemsEnter.filter(function(d) { return d.tooltip; }) + .call(tooltip() + .title(function(d) { return d.tooltip; }) + .placement('top') + ); + + items = itemsEnter + .merge(items); + + items + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .on('click', warningClick); - function warningClick(d) { - if (d.entity) { - context.map().zoomTo(d.entity); - context.enter(modeSelect(context, [d.entity.id])); + function mouseover(d) { + if (d.entity) { + context.surface().selectAll( + utilEntityOrMemberSelector([d.entity.id], context.graph()) + ).classed('hover', true); + } } - } + + function mouseout() { + context.surface().selectAll('.hover') + .classed('hover', false); + } + + + function warningClick(d) { + if (d.entity) { + context.map().zoomTo(d.entity); + context.enter(modeSelect(context, [d.entity.id])); + } + } + }); } diff --git a/modules/util/mapcss_rule.js b/modules/util/mapcss_rule.js index 5429a9673..ce5b25b71 100644 --- a/modules/util/mapcss_rule.js +++ b/modules/util/mapcss_rule.js @@ -1,6 +1,7 @@ import _isMatch from 'lodash-es/isMatch'; +import _reduce from 'lodash-es/reduce'; -export function utilMapCSSRule(selector) { +export function utilMapCSSRule(selector, areaKeys) { var ruleChecks = { equals: function (tags) { return _isMatch(tags, selector.equals); @@ -60,12 +61,53 @@ export function utilMapCSSRule(selector) { matches: function(entity) { return this.buildChecks().every(function(check) { return check(entity.tags); }); + }, + // borrowed from Way#isArea() + inferGeometry() { + // keys is a map of osm key + all found values across the selector map. + var selectorKeys = _reduce(Object.keys(selector), function(expectedTags, key) { + var values; + if(/regex/gi.test(key)) { + Object.keys(p[key]).forEach(function(regexKey) { + values = p[key][rKey].map(function(val) { return val.replace(/\$|\^/g, '') }) + if (expectedTags.hasOwnProperty(regexKey)) { + values = values.concat(expectedTags[regexKey]) + + } + expectedTags[regexKey] = values + }) + } + if (key === 'equals') { + var equalsKey = Object.keys(p[key])[0] + values = [p[key][equalsKey]] + if (expectedTags.hasOwnProperty(equalsKey)) { + values = values.concat(expectedTags[equalsKey]) + } + expectedTags[equalsKey] = values + } + return expectedTags + }, {}) + + if (Object.keys(keys).indexOf('area') > -1) { + inferredGeometry = 'area'; + return; + } + + for (var key in Object.keys(keys)) { + if (key in areaKeys) { + if (keys[key] !== 'absence' && key in ) { + + } + } + + } + }, geometryMatches: function(entity, graph) { if (entity.type === 'node' || entity.type === 'relation') { return selector.geometry === entity.type; } else if (entity.type === 'way') { - return selector.geometry === entity.geometry(graph); + return this.inferGeometry() === entity.geometry(graph); } }, diff --git a/modules/validations/mapcss_checks.js b/modules/validations/mapcss_checks.js index f7c508ae8..e287d2bd8 100644 --- a/modules/validations/mapcss_checks.js +++ b/modules/validations/mapcss_checks.js @@ -1,11 +1,11 @@ export function validationMapCSSChecks() { - var validation = function(changes, graph, rules) { + var validation = function(changes, graph, rules, areaKeys) { var warnings = []; var createdModified = ['created', 'modified']; for (var i = 0; i < createdModified.length; i++) { var entities = changes[createdModified[i]]; for (var j = 0; j < entities.length; j++) { - var entity = entities[i]; + var entity = entities[j]; for (var k = 0; k < rules.length; k++) { var rule = rules[k]; rule.findWarnings(entity, graph, warnings); diff --git a/test/spec/util/mapcss_rule.js b/test/spec/util/mapcss_rule.js index 5be819a16..40a65f6fa 100644 --- a/test/spec/util/mapcss_rule.js +++ b/test/spec/util/mapcss_rule.js @@ -41,12 +41,6 @@ describe('iD.utilMapCSSRule', function() { 'presence': 'amenity', 'positiveRegex': { amenity: ['^school$', '^healthcare$'] }, 'error': 'amenity cannot be healthcare or school!' - }, - { - 'geometry': 'node', - 'presence': 'amenity', - 'negativeRegex': { amenity: ['^school$', '^healthcare$'] }, - 'error': 'amenity must be healtcare or school!' } ]; var rules = selectors.map(function(s) { return iD.utilMapCSSRule(s); });