diff --git a/css/45_waterways.css b/css/45_waterways.css index 34d970e04..8f7ea9ba2 100644 --- a/css/45_waterways.css +++ b/css/45_waterways.css @@ -2,13 +2,13 @@ /* defaults */ .preset-icon .icon.tag-waterway.other-line { - color: #77d3de; - fill: #77d3de; + color: #7dd; + fill: #7dd; } .preset-icon .icon.iD-category-water, .preset-icon .icon.tag-type-waterway, .preset-icon .icon.tag-waterway { - color: #77d3de; + color: #7dd; fill: #fff; } @@ -38,10 +38,10 @@ path.line.fill.tag-waterway { fill: rgba(119, 211, 222, 0.3); } path.line.casing.tag-waterway { - stroke: #3d6c71; + stroke: #444; } path.line.stroke.tag-waterway { - stroke: #77d3de; + stroke: #7dd; } @@ -91,9 +91,9 @@ path.line.stroke.tag-waterway-river { /* ditch */ .preset-icon .icon.tag-waterway-ditch { - color: #8eabf3; + color: #39a; } path.line.stroke.tag-waterway-ditch { - stroke: #8eabf3; + stroke: #39a; } diff --git a/css/50_misc.css b/css/50_misc.css index fe454adab..f7ac406be 100644 --- a/css/50_misc.css +++ b/css/50_misc.css @@ -93,7 +93,7 @@ path.line.casing.tag-aerialway { /* pistes */ path.line.stroke.tag-piste { - stroke: #9ac; + stroke: #a9d; } path.line.casing.tag-piste { stroke: #444; diff --git a/css/65_data.css b/css/65_data.css index 2b19ae553..8de9ca09c 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -1,7 +1,7 @@ /* OSM Notes and KeepRight Layers */ -.kr_error-header-icon .qa_error-fill, +.error-header-icon .qa_error-fill, .layer-keepRight .qa_error .qa_error-fill, .layer-improveOSM .qa_error .qa_error-fill { stroke: #333; @@ -44,12 +44,12 @@ /* adjustment for error icon */ -.kr_error-header-icon .preset-icon-28 { +.error-header-icon .preset-icon-28 { top: auto; left: auto; } -.kr_error-header-icon { +.error-header-icon { display: flex; align-items: center; justify-content: center; @@ -211,4 +211,4 @@ stroke: #000; stroke-width: 5px; stroke-miterlimit: 1; -} \ No newline at end of file +} diff --git a/css/80_app.css b/css/80_app.css index f8aeb054f..d73dfa5b4 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -593,7 +593,7 @@ button.add-note svg.icon { .field-help-title button.close, .sidebar-component .header button.data-editor-close, .sidebar-component .header button.note-editor-close, -.sidebar-component .header button.keepRight-editor-close, +.sidebar-component .header button.error-editor-close, .entity-editor-pane .header button.preset-close, .preset-list-pane .header button.preset-choose { position: absolute; @@ -603,7 +603,7 @@ button.add-note svg.icon { [dir='rtl'] .field-help-title button.close, [dir='rtl'] .sidebar-component .header button.data-editor-close, [dir='rtl'] .sidebar-component .header button.note-editor-close, -[dir='rtl'] .sidebar-component .header button.keepRight-editor-close, +[dir='rtl'] .sidebar-component .header button.error-editor-close, [dir='rtl'] .entity-editor-pane .header button.preset-close, [dir='rtl'] .preset-list-pane .header button.preset-choose { left: 0; @@ -1177,7 +1177,7 @@ img.tag-reference-wiki-image { } .data-editor .quick-links, -.keepRight-editor .quick-links, +.error-editor .quick-links, .note-editor .quick-links { padding: 5px 0 0 0; } @@ -2500,7 +2500,7 @@ input.key-trap { /* OSM Note / KeepRight Editors ------------------------------------------------------- */ .note-header, -.kr_error-header { +.error-header { background-color: #f6f6f6; border-radius: 5px; border: 1px solid #ccc; @@ -2510,7 +2510,7 @@ input.key-trap { } .note-header-icon, -.kr_error-header-icon { +.error-header-icon { background-color: #fff; padding: 10px; flex: 0 0 62px; @@ -2521,20 +2521,20 @@ input.key-trap { border-radius: 5px 0 0 5px; } [dir='rtl'] .note-header-icon, -[dir='rtl'] .kr_error-header-icon { +[dir='rtl'] .error-header-icon { border-right: unset; border-left: 1px solid #ccc; border-radius: 0 5px 5px 0; } .note-header-icon .icon-wrap, -.kr_error-header-icon .icon-wrap { +.error-header-icon .icon-wrap { position: absolute; top: 0px; } .note-header-label, -.kr_error-header-label { +.error-header-label { background-color: #f6f6f6; padding: 0 15px; flex: 1 1 100%; @@ -2543,7 +2543,7 @@ input.key-trap { border-radius: 0 5px 5px 0; } [dir='rtl'] .note-header-label, -[dir='rtl'] .kr_error-header-label { +[dir='rtl'] .error-header-label { border-radius: 5px 0 0 5px; } @@ -2610,46 +2610,44 @@ input.key-trap { } .note-save, -.keepRight-save { +.error-save { padding-top: 20px; } -.kr_error-details, -.kr_error-comment-container { + +.error-details { padding: 10px; } - -.keepRight-save .new-comment-input, -.note-save .new-comment-input { - width: 100%; - height: 100px; - max-height: 300px; - min-height: 100px; -} - -.keepRight-save .detail-section, -.note-save .detail-section { - margin: 10px 0; -} - -.note-report { - float: right; -} - -.kr_error-details-container { +.error-details-container { background: #ececec; padding: 10px; margin-top: 20px; border-radius: 4px; border: 1px solid #ccc; } - -.kr_error-details-description { +.error-details-description { margin-bottom: 10px; } -.kr_error-details-description-text::first-letter { +.error-details-description-text::first-letter { text-transform: capitalize; } +.note-save .new-comment-input, +.error-save .new-comment-input { + width: 100%; + height: 100px; + max-height: 300px; + min-height: 100px; +} + +.note-save .detail-section, +.error-save .detail-section { + margin: 10px 0; +} + +.note-report { + float: right; +} + /* Custom Data Editor ------------------------------------------------------- */ diff --git a/modules/core/history.js b/modules/core/history.js index feb496362..97f730579 100644 --- a/modules/core/history.js +++ b/modules/core/history.js @@ -101,7 +101,7 @@ export function coreHistory(context) { } - // determine diffrence and dispatch a change event + // determine difference and dispatch a change event function change(previous, isAnnotated) { var difference = coreDifference(previous, history.graph()); dispatch.call('change', this, difference); diff --git a/modules/services/improveOSM.js b/modules/services/improveOSM.js index 5623a09db..9d3b51ab7 100644 --- a/modules/services/improveOSM.js +++ b/modules/services/improveOSM.js @@ -65,11 +65,11 @@ function updateRtree(item, replace) { } function linkErrorObject(d) { - return '' + d + ''; + return '' + d + ''; } function linkEntity(d) { - return '' + d + ''; + return '' + d + ''; } function pointAverage(points) { @@ -329,6 +329,36 @@ export default { }); }, + getComments: function(d, callback) { + // If comments already retrieved no need to do so again + if (d.comments !== undefined) { return callback({}, d); } + + var key = d.error_key; + var qParams = {}; + + if (key === 'ow') { + qParams = d.identifier; + } else if (key === 'mr') { + qParams.tileX = d.identifier.x; + qParams.tileY = d.identifier.y; + } else if (key === 'tr') { + qParams.targetId = d.identifier; + } + + var url = _impOsmUrls[key] + '/retrieveComments?' + utilQsString(qParams); + + var that = this; + d3_json(url, function(err, data) { + // comments are served newest to oldest + var comments = data.comments ? data.comments.reverse() : []; + + that.replaceError(d.update({ + comments: comments + })); + return callback(err, d); + }); + }, + postUpdate: function(d, callback) { if (!services.osm.authenticated()) { // Username required in payload return callback({ message: 'Not Authenticated', status: -3}, d); @@ -360,17 +390,16 @@ export default { payload.targetIds = [ d.identifier ]; } - // Comments don't currently work, if they ever do in future - // it looks as though they require a separate post - // if (d.newComment !== undefined) { - // payload.text = d.newComment; - // } - - if (d.newStatus !== d.status) { + if (d.newStatus !== undefined) { payload.status = d.newStatus; payload.text = 'status changed'; } + // Comment take place of default text + if (d.newComment !== undefined) { + payload.text = d.newComment; + } + _erCache.inflightPost[d.id] = d3_request(url) .header('Content-Type', 'application/json') .post(JSON.stringify(payload), function(err) { @@ -379,12 +408,29 @@ export default { // Unsuccessful response status, keep issue open if (err.status !== 200) { return callback(err, d); } - that.removeError(d); + // Just a comment, update error in cache + if (d.newStatus === undefined) { + var now = new Date(); + var comments = d.comments ? d.comments : []; - // No pretty identifier, so we just use coordinates - if (d.newStatus === 'SOLVED') { - var closedID = d.loc[1].toFixed(5) + '/' + d.loc[0].toFixed(5); - _erCache.closed[key + ':' + closedID] = true; + comments.push({ + username: payload.username, + text: payload.text, + timestamp: now.getTime() / 1000 + }); + + that.replaceError(d.update({ + comments: comments, + newComment: undefined + })); + } else { + that.removeError(d); + + if (d.newStatus === 'SOLVED') { + // No pretty identifier, so we just use coordinates + var closedID = d.loc[1].toFixed(5) + '/' + d.loc[0].toFixed(5); + _erCache.closed[key + ':' + closedID] = true; + } } return callback(err, d); @@ -430,4 +476,4 @@ export default { getClosedIDs: function() { return Object.keys(_erCache.closed).sort(); } -}; \ No newline at end of file +}; diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 0995cab27..68e34587a 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -168,11 +168,11 @@ function parseError(capture, idType) { function linkErrorObject(d) { - return '' + d + ''; + return '' + d + ''; } function linkEntity(d) { - return '' + d + ''; + return '' + d + ''; } function linkURL(d) { @@ -499,4 +499,4 @@ export default { return Object.keys(_krCache.closed).sort(); } -}; \ No newline at end of file +}; diff --git a/modules/ui/combobox.js b/modules/ui/combobox.js index 93b0ae596..def5e1fed 100644 --- a/modules/ui/combobox.js +++ b/modules/ui/combobox.js @@ -177,7 +177,6 @@ export function uiCombobox(context, klass) { break; case 9: // ⇥ Tab - d3_event.stopPropagation(); accept(); break; diff --git a/modules/ui/improveOSM_comments.js b/modules/ui/improveOSM_comments.js new file mode 100644 index 000000000..47608e0c5 --- /dev/null +++ b/modules/ui/improveOSM_comments.js @@ -0,0 +1,94 @@ +import { select as d3_select } from 'd3-selection'; + +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { services } from '../services'; +import { utilDetect } from '../util/detect'; + +export function uiImproveOsmComments() { + var _error; + + + function errorComments(selection) { + // make the div immediately so it appears above the buttons + var comments = selection.selectAll('.comments-container') + .data([0]); + + comments = comments.enter() + .append('div') + .attr('class', 'comments-container') + .merge(comments); + + // must retrieve comments from API before they can be displayed + services.improveOSM.getComments(_error, function(err, d) { + if (!d.comments) { return; } // nothing to do here + + var commentEnter = comments.selectAll('.comment') + .data(d.comments) + .enter() + .append('div') + .attr('class', 'comment'); + + commentEnter + .append('div') + .attr('class', 'comment-avatar') + .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); + + var mainEnter = commentEnter + .append('div') + .attr('class', 'comment-main'); + + var metadataEnter = mainEnter + .append('div') + .attr('class', 'comment-metadata'); + + metadataEnter + .append('div') + .attr('class', 'comment-author') + .each(function(d) { + var selection = d3_select(this); + var osm = services.osm; + if (osm && d.username) { + selection = selection + .append('a') + .attr('class', 'comment-author-link') + .attr('href', osm.userURL(d.username)) + .attr('tabindex', -1) + .attr('target', '_blank'); + } + selection + .text(function(d) { return d.username; }); + }); + + metadataEnter + .append('div') + .attr('class', 'comment-date') + .text(function(d) { + return t('note.status.commented', { when: localeDateString(d.timestamp) }); + }); + + mainEnter + .append('div') + .attr('class', 'comment-text') + .append('p') + .text(function(d) { return d.text; }); + }); + } + + function localeDateString(s) { + if (!s) return null; + var detected = utilDetect(); + var options = { day: 'numeric', month: 'short', year: 'numeric' }; + var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(detected.locale, options); + } + + errorComments.error = function(val) { + if (!arguments.length) return _error; + _error = val; + return errorComments; + }; + + return errorComments; +} \ No newline at end of file diff --git a/modules/ui/improveOSM_details.js b/modules/ui/improveOSM_details.js index 075ccd0cb..efa27ef9b 100644 --- a/modules/ui/improveOSM_details.js +++ b/modules/ui/improveOSM_details.js @@ -32,7 +32,7 @@ export function uiImproveOsmDetails(context) { function improveOsmDetails(selection) { - var details = selection.selectAll('.kr_error-details') + var details = selection.selectAll('.error-details') .data( (_error ? [_error] : []), function(d) { return d.id + '-' + (d.status || 0); } @@ -43,13 +43,13 @@ export function uiImproveOsmDetails(context) { var detailsEnter = details.enter() .append('div') - .attr('class', 'kr_error-details kr_error-details-container'); + .attr('class', 'error-details error-details-container'); // description var descriptionEnter = detailsEnter .append('div') - .attr('class', 'kr_error-details-description'); + .attr('class', 'error-details-description'); descriptionEnter .append('h4') @@ -57,14 +57,14 @@ export function uiImproveOsmDetails(context) { descriptionEnter .append('div') - .attr('class', 'kr_error-details-description-text') + .attr('class', 'error-details-description-text') .html(errorDetail); // If there are entity links in the error message.. - descriptionEnter.selectAll('.kr_error_entity_link, .kr_error_object_link') + descriptionEnter.selectAll('.error_entity_link, .error_object_link') .each(function() { var link = d3_select(this); - var isObjectLink = link.classed('kr_error_object_link'); + var isObjectLink = link.classed('error_object_link'); var entityID = isObjectLink ? (utilEntityRoot(_error.object_type) + _error.object_id) : this.textContent; @@ -124,4 +124,4 @@ export function uiImproveOsmDetails(context) { return improveOsmDetails; -} \ No newline at end of file +} diff --git a/modules/ui/improveOSM_editor.js b/modules/ui/improveOSM_editor.js index c1c29c1b5..14405b538 100644 --- a/modules/ui/improveOSM_editor.js +++ b/modules/ui/improveOSM_editor.js @@ -1,4 +1,5 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; import { services } from '../services'; @@ -6,18 +7,20 @@ import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; import { + uiImproveOsmComments, uiImproveOsmDetails, uiImproveOsmHeader, uiQuickLinks, uiTooltipHtml } from './index'; -import { utilRebind } from '../util'; +import { utilNoAuto, utilRebind } from '../util'; export function uiImproveOsmEditor(context) { var dispatch = d3_dispatch('change'); var errorDetails = uiImproveOsmDetails(context); + var errorComments = uiImproveOsmComments(context); var errorHeader = uiImproveOsmHeader(context); var quickLinks = uiQuickLinks(); @@ -47,7 +50,7 @@ export function uiImproveOsmEditor(context) { headerEnter .append('button') - .attr('class', 'fr keepRight-editor-close') + .attr('class', 'fr error-editor-close') .on('click', function() { context.enter(modeBrowse(context)); }) @@ -66,16 +69,17 @@ export function uiImproveOsmEditor(context) { .attr('class', 'body') .merge(body); - var editor = body.selectAll('.keepRight-editor') + var editor = body.selectAll('.error-editor') .data([0]); editor.enter() .append('div') - .attr('class', 'modal-section keepRight-editor') + .attr('class', 'modal-section error-editor') .merge(editor) .call(errorHeader.error(_error)) .call(quickLinks.choices(choices)) .call(errorDetails.error(_error)) + .call(errorComments.error(_error)) .call(improveOsmSaveSection); } @@ -95,12 +99,47 @@ export function uiImproveOsmEditor(context) { // enter var saveSectionEnter = saveSection.enter() .append('div') - .attr('class', 'keepRight-save save-section cf'); + .attr('class', 'error-save save-section cf'); + + saveSectionEnter + .append('h4') + .attr('class', '.error-save-header') + .text(t('note.newComment')); + + saveSectionEnter + .append('textarea') + .attr('class', 'new-comment-input') + .attr('placeholder', t('QA.keepRight.comment_placeholder')) + .attr('maxlength', 1000) + .property('value', function(d) { return d.newComment; }) + .call(utilNoAuto) + .on('input', changeInput) + .on('blur', changeInput); // update saveSection = saveSectionEnter .merge(saveSection) .call(errorSaveButtons); + + function changeInput() { + var input = d3_select(this); + var val = input.property('value').trim(); + + if (val === '') { + val = undefined; + } + + // store the unsaved comment with the error itself + _error = _error.update({ newComment: val }); + + var errorService = services.improveOSM; + if (errorService) { + errorService.replaceError(_error); + } + + saveSection + .call(errorSaveButtons); + } } function errorSaveButtons(selection) { @@ -117,11 +156,10 @@ export function uiImproveOsmEditor(context) { .append('div') .attr('class', 'buttons'); - // Comments don't currently work - // buttonEnter - // .append('button') - // .attr('class', 'button comment-button action') - // .text(t('QA.keepRight.save_comment')); + buttonEnter + .append('button') + .attr('class', 'button comment-button action') + .text(t('QA.keepRight.save_comment')); buttonEnter .append('button') @@ -136,20 +174,19 @@ export function uiImproveOsmEditor(context) { buttonSection = buttonSection .merge(buttonEnter); - // Comments don't currently work - // buttonSection.select('.comment-button') - // .attr('disabled', function(d) { - // return d.newComment === undefined ? true : null; - // }) - // .on('click.comment', function(d) { - // this.blur(); // avoid keeping focus on the button - #4641 - // var errorService = services.improveOSM; - // if (errorService) { - // errorService.postUpdate(d, function(err, error) { - // dispatch.call('change', error); - // }); - // } - // }); + buttonSection.select('.comment-button') + .attr('disabled', function(d) { + return d.newComment === undefined ? true : null; + }) + .on('click.comment', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var errorService = services.improveOSM; + if (errorService) { + errorService.postUpdate(d, function(err, error) { + dispatch.call('change', error); + }); + } + }); buttonSection.select('.close-button') .text(function(d) { diff --git a/modules/ui/improveOSM_header.js b/modules/ui/improveOSM_header.js index d87b7cb3e..f9ae38327 100644 --- a/modules/ui/improveOSM_header.js +++ b/modules/ui/improveOSM_header.js @@ -22,7 +22,7 @@ export function uiImproveOsmHeader() { function improveOsmHeader(selection) { - var header = selection.selectAll('.kr_error-header') + var header = selection.selectAll('.error-header') .data( (_error ? [_error] : []), function(d) { return d.id + '-' + (d.status || 0); } @@ -33,11 +33,11 @@ export function uiImproveOsmHeader() { var headerEnter = header.enter() .append('div') - .attr('class', 'kr_error-header'); + .attr('class', 'error-header'); var iconEnter = headerEnter .append('div') - .attr('class', 'kr_error-header-icon') + .attr('class', 'error-header-icon') .classed('new', function(d) { return d.id < 0; }); var svgEnter = iconEnter @@ -81,7 +81,7 @@ export function uiImproveOsmHeader() { headerEnter .append('div') - .attr('class', 'kr_error-header-label') + .attr('class', 'error-header-label') .text(errorTitle); } @@ -94,4 +94,4 @@ export function uiImproveOsmHeader() { return improveOsmHeader; -} \ No newline at end of file +} diff --git a/modules/ui/index.js b/modules/ui/index.js index c1361064e..25775c454 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -28,6 +28,7 @@ export { uiFormFields } from './form_fields'; export { uiFullScreen } from './full_screen'; export { uiGeolocate } from './geolocate'; export { uiHelp } from './help'; +export { uiImproveOsmComments } from './improveOSM_comments'; export { uiImproveOsmDetails } from './improveOSM_details'; export { uiImproveOsmEditor } from './improveOSM_editor'; export { uiImproveOsmHeader } from './improveOSM_header'; @@ -72,4 +73,4 @@ export { uiUndoRedo } from './undo_redo'; export { uiVersion } from './version'; export { uiViewOnOSM } from './view_on_osm'; export { uiViewOnKeepRight } from './view_on_keepRight'; -export { uiZoom } from './zoom'; +export { uiZoom } from './zoom'; \ No newline at end of file diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 12d613448..96b6536b6 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -37,7 +37,7 @@ export function uiKeepRightDetails(context) { function keepRightDetails(selection) { - var details = selection.selectAll('.kr_error-details') + var details = selection.selectAll('.error-details') .data( (_error ? [_error] : []), function(d) { return d.id + '-' + (d.status || 0); } @@ -48,13 +48,13 @@ export function uiKeepRightDetails(context) { var detailsEnter = details.enter() .append('div') - .attr('class', 'kr_error-details kr_error-details-container'); + .attr('class', 'error-details error-details-container'); // description var descriptionEnter = detailsEnter .append('div') - .attr('class', 'kr_error-details-description'); + .attr('class', 'error-details-description'); descriptionEnter .append('h4') @@ -62,14 +62,14 @@ export function uiKeepRightDetails(context) { descriptionEnter .append('div') - .attr('class', 'kr_error-details-description-text') + .attr('class', 'error-details-description-text') .html(errorDetail); // If there are entity links in the error message.. - descriptionEnter.selectAll('.kr_error_entity_link, .kr_error_object_link') + descriptionEnter.selectAll('.error_entity_link, .error_object_link') .each(function() { var link = d3_select(this); - var isObjectLink = link.classed('kr_error_object_link'); + var isObjectLink = link.classed('error_object_link'); var entityID = isObjectLink ? (utilEntityRoot(_error.object_type) + _error.object_id) : this.textContent; diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index ca502b1e1..c35ea4b5e 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -49,7 +49,7 @@ export function uiKeepRightEditor(context) { headerEnter .append('button') - .attr('class', 'fr keepRight-editor-close') + .attr('class', 'fr error-editor-close') .on('click', function() { context.enter(modeBrowse(context)); }) @@ -68,12 +68,12 @@ export function uiKeepRightEditor(context) { .attr('class', 'body') .merge(body); - var editor = body.selectAll('.keepRight-editor') + var editor = body.selectAll('.error-editor') .data([0]); editor.enter() .append('div') - .attr('class', 'modal-section keepRight-editor') + .attr('class', 'modal-section error-editor') .merge(editor) .call(keepRightHeader.error(_error)) .call(quickLinks.choices(choices)) @@ -108,7 +108,7 @@ export function uiKeepRightEditor(context) { // enter var saveSectionEnter = saveSection.enter() .append('div') - .attr('class', 'keepRight-save save-section cf'); + .attr('class', 'error-save save-section cf'); saveSectionEnter .append('h4') diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 0e611c74f..33a958b75 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -28,7 +28,7 @@ export function uiKeepRightHeader() { function keepRightHeader(selection) { - var header = selection.selectAll('.kr_error-header') + var header = selection.selectAll('.error-header') .data( (_error ? [_error] : []), function(d) { return d.id + '-' + (d.status || 0); } @@ -39,11 +39,11 @@ export function uiKeepRightHeader() { var headerEnter = header.enter() .append('div') - .attr('class', 'kr_error-header'); + .attr('class', 'error-header'); var iconEnter = headerEnter .append('div') - .attr('class', 'kr_error-header-icon') + .attr('class', 'error-header-icon') .classed('new', function(d) { return d.id < 0; }); iconEnter @@ -55,7 +55,7 @@ export function uiKeepRightHeader() { headerEnter .append('div') - .attr('class', 'kr_error-header-label') + .attr('class', 'error-header-label') .text(errorTitle); } @@ -68,4 +68,4 @@ export function uiKeepRightHeader() { return keepRightHeader; -} \ No newline at end of file +} diff --git a/modules/ui/raw_tag_editor.js b/modules/ui/raw_tag_editor.js index 50b37de57..47069e159 100644 --- a/modules/ui/raw_tag_editor.js +++ b/modules/ui/raw_tag_editor.js @@ -155,10 +155,10 @@ export function uiRawTagEditor(context) { .order(); items - .each(function(tag) { + .each(function(d) { var row = d3_select(this); - var key = row.select('input.key'); // propagate bound data to child - var value = row.select('input.value'); // propagate bound data to child + var key = row.select('input.key'); // propagate bound data + var value = row.select('input.value'); // propagate bound data if (_entityID && taginfo && _state !== 'hover') { bindTypeahead(key, value); @@ -167,20 +167,22 @@ export function uiRawTagEditor(context) { var isRelation = (_entityID && context.entity(_entityID).type === 'relation'); var reference; - if (isRelation && tag.key === 'type') { - reference = uiTagReference({ rtype: tag.value }, context); + if (isRelation && d.key === 'type') { + reference = uiTagReference({ rtype: d.value }, context); } else { - reference = uiTagReference({ key: tag.key, value: tag.value }, context); + reference = uiTagReference({ key: d.key, value: d.value }, context); } if (_state === 'hover') { reference.showing(false); } - row.select('.inner-wrap') + row.select('.inner-wrap') // propagate bound data .call(reference.button); row.call(reference.body); + + row.select('button.remove'); // propagate bound data }); items.selectAll('input.key') @@ -344,10 +346,15 @@ export function uiRawTagEditor(context) { function removeTag(d) { if (isReadOnly(d)) return; - var t = {}; - t[d.key] = undefined; - dispatch.call('change', this, t); - d3_select(this.parentNode).remove(); + + if (d.key === '') { // removing the blank row + _showBlank = false; + content(wrap); + } else { + var t = {}; + t[d.key] = undefined; + dispatch.call('change', this, t); + } } @@ -359,7 +366,7 @@ export function uiRawTagEditor(context) { _showBlank = true; content(wrap); list.selectAll('li:last-child input.key').node().focus(); - }, 1); + }, 10); } } diff --git a/modules/util/util.js b/modules/util/util.js index dc5220916..75d70a7b4 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -159,9 +159,6 @@ export function utilStringQs(str) { if (parts.length === 2) { obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]); } - if (parts[0] === 'mvt') { - obj[parts[0]] = (parts[2] !== undefined) ? (decodeURIComponent(parts[1]) + '=' + decodeURIComponent(parts[2])) : (decodeURIComponent(parts[1])); - } return obj; }, {}); } diff --git a/test/spec/services/mapillary.js b/test/spec/services/mapillary.js index f1ca87dc4..8642b9b51 100644 --- a/test/spec/services/mapillary.js +++ b/test/spec/services/mapillary.js @@ -14,7 +14,7 @@ describe('iD.serviceMapillary', function() { beforeEach(function() { context = iD.coreContext().assetPath('../dist/'); context.projection - .scale(667544.214430109) // z14 + .scale(iD.geoZoomToScale(14)) .translate([-116508, 0]) // 10,0 .clipExtent([[0,0], dimensions]); diff --git a/test/spec/services/openstreetcam.js b/test/spec/services/openstreetcam.js index a3f512986..13d03b56a 100644 --- a/test/spec/services/openstreetcam.js +++ b/test/spec/services/openstreetcam.js @@ -1,6 +1,6 @@ describe('iD.serviceOpenstreetcam', function() { - var dimensions = [64, 64], - context, server, openstreetcam; + var dimensions = [64, 64]; + var context, server, openstreetcam; before(function() { iD.services.openstreetcam = iD.serviceOpenstreetcam; @@ -13,7 +13,7 @@ describe('iD.serviceOpenstreetcam', function() { beforeEach(function() { context = iD.coreContext().assetPath('../dist/'); context.projection - .scale(667544.214430109) // z14 + .scale(iD.geoZoomToScale(14)) .translate([-116508, 0]) // 10,0 .clipExtent([[0,0], dimensions]); diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index 0bc95d8f3..476ff73c3 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -596,21 +596,42 @@ describe('iD.serviceOsm', function () { }); describe('#loadNotes', function() { + var notesXML = '' + + '' + + '' + + ' 1' + + ' https://www.openstreetmap.org/api/0.6/notes/1' + + ' https://www.openstreetmap.org/api/0.6/notes/1/comment' + + ' https://www.openstreetmap.org/api/0.6/notes/1/close' + + ' 2019-01-01 00:00:00 UTC' + + ' open' + + ' ' + + ' ' + + ' 2019-01-01 00:00:00 UTC' + + ' 1' + + ' Steve' + + ' https://www.openstreetmap.org/user/Steve' + + ' opened' + + ' This is a note' + + ' <p>This is a note</p>' + + ' ' + + ' ' + + '' + + ''; + beforeEach(function() { + var dimensions = [64, 64]; context.projection - .scale(116722210.56960216) - .translate([244505613.61327893, 74865520.92230521]) - .clipExtent([[0,0], [609.34375, 826]]); + .scale(iD.geoZoomToScale(14)) + .translate([-116508, 0]) // 10,0 + .clipExtent([[0,0], dimensions]); }); it('fires loadedNotes when notes are loaded', function() { connection.on('loadedNotes', spy); - connection.loadNotes(context.projection, [64, 64], {}); + connection.loadNotes(context.projection, {}); - var url = 'http://www.openstreetmap.org/api/0.6/notes?limit=10000&closed=7&bbox=-120.05859375,34.45221847282654,-119.970703125,34.52466147177173'; - var notesXML = ''; // TODO: determine output even though this test note is closed and will be gone soon - - server.respondWith('GET', url, + server.respondWith('GET', /notes\?/, [200, { 'Content-Type': 'text/xml' }, notesXML ]); server.respond(); @@ -623,7 +644,7 @@ describe('iD.serviceOsm', function () { beforeEach(function() { var dimensions = [64, 64]; context.projection - .scale(667544.214430109) // z14 + .scale(iD.geoZoomToScale(14)) .translate([-116508, 0]) // 10,0 .clipExtent([[0,0], dimensions]); }); @@ -646,17 +667,17 @@ describe('iD.serviceOsm', function () { describe('#getNote', function() { - it('returns a note', function (done) { - var note = iD.osmNote({ id: 1, loc: [0, 0], }); - var obj = { - note: { note: { 1: note } } - }; - connection.caches(obj); - var result = connection.getNote(1); - expect(result).to.deep.equal(note); - done(); - }); + it('returns a note', function (done) { + var note = iD.osmNote({ id: 1, loc: [0, 0], }); + var obj = { + note: { note: { 1: note } } + }; + connection.caches(obj); + var result = connection.getNote(1); + expect(result).to.deep.equal(note); + done(); }); + }); describe('#removeNote', function() { it('removes a note that is new', function(done) { @@ -701,7 +722,8 @@ describe('iD.serviceOsm', function () { describe('API capabilities', function() { - var capabilitiesXML = '' + + var capabilitiesXML = '' + + '' + '' + '' + '' + diff --git a/test/spec/services/streetside.js b/test/spec/services/streetside.js index b8adb2f1c..8221718d1 100644 --- a/test/spec/services/streetside.js +++ b/test/spec/services/streetside.js @@ -1,6 +1,6 @@ describe('iD.serviceStreetside', function() { - var dimensions = [64, 64], - context, server, streetside; + var dimensions = [64, 64]; + var context, server, streetside; before(function() { iD.services.streetside = iD.serviceStreetside; @@ -13,7 +13,7 @@ describe('iD.serviceStreetside', function() { beforeEach(function() { context = iD.coreContext().assetPath('../dist/'); context.projection - .scale(667544.214430109) // z14 + .scale(iD.geoZoomToScale(14)) .translate([-116508, 0]) // 10,0 .clipExtent([[0,0], dimensions]); @@ -53,7 +53,7 @@ describe('iD.serviceStreetside', function() { // adjust projection so that only one tile is fetched // (JSONP hack will return the same data for every fetch) context.projection - .scale(10680707.430881744) // z18 + .scale(iD.geoZoomToScale(18)) .translate([-1863988.9381333336, 762.8270222954452]) // 10.002,0.002 .clipExtent([[0,0], dimensions]); @@ -84,7 +84,7 @@ describe('iD.serviceStreetside', function() { it('does not load bubbles around null island', function() { context.projection - .scale(10680707.430881744) // z18 + .scale(iD.geoZoomToScale(18)) .translate([0, 0]) .clipExtent([[0,0], dimensions]);