diff --git a/css/80_app.css b/css/80_app.css index e652cbecf..d050ec313 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3532,6 +3532,7 @@ img.tile-removing { border: 1px solid #ccc; border-radius: 4px; background: #fff; + margin-bottom: 10px; } .mode-save .warning-section { diff --git a/data/core.yaml b/data/core.yaml index 4c3fb7bee..4f7ee4487 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -260,6 +260,7 @@ en: save: Upload cancel: Cancel changes: "{count} Changes" + download_changes: Download osmChange file warnings: Warnings modified: Modified deleted: Deleted @@ -463,7 +464,7 @@ en: keep_remote: Use theirs restore: Restore delete: Leave Deleted - download_changes: Or download your changes. + download_changes: Or download osmChange file done: "All conflicts resolved!" help: | Another user changed some of the same map features you changed. diff --git a/dist/locales/en.json b/dist/locales/en.json index de62b5573..98ea19cf7 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -334,6 +334,7 @@ "save": "Upload", "cancel": "Cancel", "changes": "{count} Changes", + "download_changes": "Download osmChange file", "warnings": "Warnings", "modified": "Modified", "deleted": "Deleted", @@ -572,7 +573,7 @@ "keep_remote": "Use theirs", "restore": "Restore", "delete": "Leave Deleted", - "download_changes": "Or download your changes.", + "download_changes": "Or download osmChange file", "done": "All conflicts resolved!", "help": "Another user changed some of the same map features you changed.\nClick on each feature below for more details about the conflict, and choose whether to keep\nyour changes or the other user's changes.\n" } diff --git a/modules/modes/save.js b/modules/modes/save.js index 391fe2f47..b16f1240e 100644 --- a/modules/modes/save.js +++ b/modules/modes/save.js @@ -3,7 +3,6 @@ import _ from 'lodash'; import { d3keybinding } from '../lib/d3.keybinding.js'; import { t } from '../util/locale'; -import { JXON } from '../util/jxon'; import { actionDiscardTags, @@ -248,11 +247,7 @@ export function modeSave(context) { selection.call(uiConflicts(context) .list(conflicts) - .on('download', function() { - var data = JXON.stringify(changeset.update({ id: 'CHANGEME' }).osmChangeJXON(origChanges)), - win = window.open('data:text/xml,' + encodeURIComponent(data), '_blank'); - win.focus(); - }) + .origChanges(origChanges) .on('cancel', function() { history.pop(); selection.remove(); diff --git a/modules/ui/commit_changes.js b/modules/ui/commit_changes.js index e47fd81b2..c33c1bddb 100644 --- a/modules/ui/commit_changes.js +++ b/modules/ui/commit_changes.js @@ -1,6 +1,11 @@ import * as d3 from 'd3'; import { t } from '../util/locale'; +import { JXON } from '../util/jxon'; +import { actionDiscardTags } from '../actions'; +import { osmChangeset } from '../osm'; import { svgIcon } from '../svg'; +import { utilDetect } from '../util/detect'; + import { utilDisplayName, utilDisplayType, @@ -9,10 +14,13 @@ import { export function uiCommitChanges(context) { + var detected = utilDetect(); + function commitChanges(selection) { - var summary = context.history().difference().summary(); + var history = context.history(), + summary = history.difference().summary(); var container = selection.selectAll('.modal-section.commit-section') .data([0]); @@ -85,6 +93,41 @@ export function uiCommitChanges(context) { .on('click', zoomToEntity); + // Download changeset link + var changeset = new osmChangeset().update({ id: undefined }), + changes = history.changes(actionDiscardTags(history.difference())); + + delete changeset.id; // Export without chnageset_id + + var data = JXON.stringify(changeset.osmChangeJXON(changes)), + blob = new Blob([data], {type: 'text/xml;charset=utf-8;'}), + fileName = 'changes.osc'; + + var linkEnter = container.selectAll('.download-changes') + .data([0]) + .enter() + .append('a') + .attr('class', 'download-changes'); + + if (detected.download) { // All except IE11 and Edge + linkEnter // download the data as a file + .attr('href', window.URL.createObjectURL(blob)) + .attr('download', fileName); + + } else { // IE11 and Edge + linkEnter // open data uri in a new tab + .attr('target', '_blank') + .on('click.download', function() { + navigator.msSaveBlob(blob, fileName); + }); + } + + linkEnter + .call(svgIcon('#icon-load', 'inline')) + .append('span') + .text(t('commit.download_changes')); + + function mouseover(d) { if (d.entity) { context.surface().selectAll( diff --git a/modules/ui/conflicts.js b/modules/ui/conflicts.js index c6d023a1c..ca89b9980 100644 --- a/modules/ui/conflicts.js +++ b/modules/ui/conflicts.js @@ -1,14 +1,18 @@ import * as d3 from 'd3'; import { t } from '../util/locale'; -import { geoExtent } from '../geo/index'; -import { svgIcon } from '../svg/index'; -import { utilEntityOrMemberSelector } from '../util/index'; +import { JXON } from '../util/jxon'; +import { geoExtent } from '../geo'; +import { osmChangeset } from '../osm'; +import { svgIcon } from '../svg'; +import { utilDetect } from '../util/detect'; +import { utilEntityOrMemberSelector } from '../util'; import { utilRebind } from '../util/rebind'; export function uiConflicts(context) { - var dispatch = d3.dispatch('download', 'cancel', 'save'), - list; + var dispatch = d3.dispatch('cancel', 'save'), + origChanges, + conflictList; function conflicts(selection) { @@ -30,14 +34,46 @@ export function uiConflicts(context) { .append('div') .attr('class', 'body fillL'); - body + var conflictsHelp = body .append('div') .attr('class', 'conflicts-help') - .text(t('save.conflict.help')) + .text(t('save.conflict.help')); + + + // Download changes link + var detected = utilDetect(), + changeset = new osmChangeset(); + + delete changeset.id; // Export without chnageset_id + + var data = JXON.stringify(changeset.osmChangeJXON(origChanges)), + blob = new Blob([data], {type: 'text/xml;charset=utf-8;'}), + fileName = 'changes.osc'; + + var linkEnter = conflictsHelp.selectAll('.download-changes') + .data([0]) + .enter() .append('a') - .attr('class', 'conflicts-download') - .text(t('save.conflict.download_changes')) - .on('click.download', function() { dispatch.call('download'); }); + .attr('class', 'download-changes'); + + if (detected.download) { // All except IE11 and Edge + linkEnter // download the data as a file + .attr('href', window.URL.createObjectURL(blob)) + .attr('download', fileName); + + } else { // IE11 and Edge + linkEnter // open data uri in a new tab + .attr('target', '_blank') + .on('click.download', function() { + navigator.msSaveBlob(blob, fileName); + }); + } + + linkEnter + .call(svgIcon('#icon-load', 'inline')) + .append('span') + .text(t('save.conflict.download_changes')); + body .append('div') @@ -57,7 +93,7 @@ export function uiConflicts(context) { buttons .append('button') - .attr('disabled', list.length > 1) + .attr('disabled', conflictList.length > 1) .attr('class', 'action conflicts-button col6') .text(t('save.title')) .on('click.try_again', function() { dispatch.call('save'); }); @@ -71,12 +107,12 @@ export function uiConflicts(context) { function showConflict(selection, index) { - if (index < 0 || index >= list.length) return; + if (index < 0 || index >= conflictList.length) return; var parent = d3.select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed.. - if (index === list.length - 1) { + if (index === conflictList.length - 1) { window.setTimeout(function() { parent.select('.conflicts-button') .attr('disabled', null); @@ -90,7 +126,7 @@ export function uiConflicts(context) { var item = selection .selectAll('.conflict') - .data([list[index]]); + .data([conflictList[index]]); var enter = item.enter() .append('div') @@ -99,7 +135,7 @@ export function uiConflicts(context) { enter .append('h4') .attr('class', 'conflict-count') - .text(t('save.conflict.count', { num: index + 1, total: list.length })); + .text(t('save.conflict.count', { num: index + 1, total: conflictList.length })); enter .append('a') @@ -141,7 +177,7 @@ export function uiConflicts(context) { .attr('class', 'conflict-nav-button action col6') .attr('disabled', function(d, i) { return (i === 0 && index === 0) || - (i === 1 && index === list.length - 1) || null; + (i === 1 && index === conflictList.length - 1) || null; }) .on('click', function(d, i) { var container = parent.select('.conflict-container'), @@ -252,8 +288,15 @@ export function uiConflicts(context) { // ] // } conflicts.list = function(_) { - if (!arguments.length) return list; - list = _; + if (!arguments.length) return conflictList; + conflictList = _; + return conflicts; + }; + + + conflicts.origChanges = function(_) { + if (!arguments.length) return origChanges; + origChanges = _; return conflicts; }; diff --git a/modules/util/detect.js b/modules/util/detect.js index ed8f3ad2e..92a9691a5 100644 --- a/modules/util/detect.js +++ b/modules/util/detect.js @@ -110,6 +110,8 @@ export function utilDetect(force) { detected.filedrop = (window.FileReader && 'ondrop' in window); + detected.download = !(detected.ie || detected.browser.toLowerCase() === 'edge'); + function nav(x) { return navigator.userAgent.indexOf(x) !== -1; }