modules/ui/assistant.js (794 lines of code) (raw):
import _debounce from 'lodash-es/debounce';
import { dataEn } from '../../data';
import { drag as d3_drag } from 'd3-drag';
import {
select as d3_select,
event as d3_event
} from 'd3-selection';
import { svgIcon } from '../svg/icon';
import { currentLocale, t, textDirection } from '../util/locale';
import { services } from '../services';
import { utilDisplayLabel } from '../util';
import { uiIntro } from './intro';
import { uiSuccess } from './success';
import { uiPresetIcon } from './preset_icon';
import { uiEntityEditor } from './entity_editor';
import { uiFeatureList } from './feature_list';
import { uiNoteEditor } from './note_editor';
import { uiKeepRightEditor } from './keepRight_editor';
import { uiImproveOsmEditor } from './improveOSM_editor';
import { uiDataEditor } from './data_editor';
import { uiCommit } from './commit';
import { geoRawMercator } from '../geo/raw_mercator';
import { utilGetDimensions } from '../util/dimensions';
import { decimalCoordinatePair, formattedRoundedDuration } from '../util/units';
function utilTimeOfDayGreeting() {
return t('assistant.greetings.' + utilTimeframe());
}
function utilTimeframe() {
var now = new Date();
var hours = now.getHours();
if (hours >= 20 || hours <= 2) return 'night';
if (hours >= 18) return 'evening';
if (hours >= 12) return 'afternoon';
return 'morning';
}
function utilGreetingIcon() {
var now = new Date();
var hours = now.getHours();
if (hours >= 6 && hours < 18) return 'fas-sun';
return 'fas-moon';
}
export function uiAssistant(context) {
var defaultLoc = t('assistant.global_location');
var currLocation = defaultLoc;
var container = d3_select(null),
header = d3_select(null),
body = d3_select(null);
var featureSearch = uiFeatureList(context);
var savedChangeset = null;
var savedChangeCount = null;
var didEditAnythingYet = false;
var shownPanel = null;
context.storage('sawSplash', true);
var assistant = function(selection) {
container = selection.append('div')
.attr('class', 'assistant');
header = container.append('div')
.attr('class', 'assistant-header assistant-row');
body = container.append('div')
.attr('class', 'assistant-body');
var dragOffset;
var resizer = container
.append('div')
.attr('class', 'resizer-x');
// Set the initial width
container
.style('width', '350px');
resizer.call(d3_drag()
.container(d3_select('#id-container').node())
.on('start', function() {
resizer.classed('dragging', true);
dragOffset = d3_event.sourceEvent.offsetX;
// account for from the assistant wrap's padding
dragOffset += 10;
})
.on('drag', function() {
var x = d3_event.x - dragOffset;
var targetWidth = (textDirection === 'rtl') ? utilGetDimensions(d3_select('#content')).width - x: x;
container
.style('width', targetWidth + 'px');
})
.on('end', function() {
resizer.classed('dragging', false);
})
);
scheduleCurrentLocationUpdate();
context
.on('enter.assistant', redraw);
context.map()
.on('move.assistant', scheduleCurrentLocationUpdate);
redraw();
};
function isBodyCollapsed(collapseCategory) {
return collapseCategory && context.storage('assistant.collapsed.' + collapseCategory) === 'true';
}
function setIsBodyCollapsed(collapseCategory, flag) {
if (!flag) flag = null;
if (collapseCategory) context.storage('assistant.collapsed.' + collapseCategory, flag);
}
function updateDidEditStatus() {
savedChangeset = null;
savedChangeCount = null;
didEditAnythingYet = true;
}
function toggleBody(collapseCategory) {
var bodyOpen = isBodyCollapsed(collapseCategory);
setIsBodyCollapsed(collapseCategory, !bodyOpen);
container.classed('body-collapsed', !bodyOpen);
container.classed('minimal', false);
container.selectAll('.assistant-header .control-col .icon use')
.attr('href', '#iD-icon-' + (bodyOpen ? 'up' : 'down'));
if (!bodyOpen) {
container.on('mouseleave.minimal', function() {
container.classed('minimal', true);
});
} else {
container.on('mouseleave.minimal', null);
}
}
function drawPanel(panel) {
var hasBody = panel.renderBody || panel.message;
var isCollapsible = !panel.prominent && hasBody;
container.attr('class',
'assistant ' +
(panel.theme || 'dark') +
' ' +
(panel.prominent ? 'prominent' : '') +
' ' +
(hasBody ? 'has-body' : '') +
' ' +
(isCollapsible ? 'collapsible' : '') +
' ' +
(isCollapsible && isBodyCollapsed(panel.collapseCategory) ? 'body-collapsed minimal' : '')
);
var iconCol = header.selectAll('.icon-col')
.data([0]);
iconCol = iconCol.enter()
.append('div')
.attr('class', 'icon-col')
.merge(iconCol);
var headerMainCol = header.selectAll('.main-col')
.data([0]);
var headerMainColEnter = headerMainCol.enter()
.append('div')
.attr('class', 'main-col');
headerMainColEnter.append('div')
.attr('class', 'mode-label');
var subjectTitleArea = headerMainColEnter.append('div')
.attr('class', 'subject-title');
subjectTitleArea.append('span');
subjectTitleArea.append('div')
.attr('class', 'controls');
headerMainColEnter.append('div')
.attr('class', 'header-body');
headerMainCol = headerMainColEnter.merge(headerMainCol);
var controlCol = header.selectAll('.control-col')
.data(isCollapsible ? [0] : []);
controlCol.exit()
.remove();
controlCol.enter()
.append('div')
.attr('class', 'control-col')
.append('button')
.call(svgIcon('#iD-icon-' + (isBodyCollapsed(panel.collapseCategory) ? 'down' : 'up')));
if (isCollapsible) {
// make the assistant collapsible by its whole header
header.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
toggleBody(panel.collapseCategory);
});
} else {
header.on('click', null);
}
var modeLabel = headerMainCol.selectAll('.mode-label');
modeLabel.text(panel.modeLabel || '');
var subjectTitle = headerMainCol.selectAll('.subject-title');
subjectTitle.selectAll('span')
.attr('class', panel.titleClass || '')
.text(panel.title);
var subjectTitleControls = subjectTitle.selectAll('.controls');
subjectTitleControls.text('');
if (panel.onClose) {
subjectTitleControls.append('button')
.attr('class', 'close')
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
panel.onClose();
})
.call(svgIcon('#iD-icon-close'));
}
iconCol.html('');
if (panel.headerIcon) {
iconCol.call(svgIcon('#' + panel.headerIcon));
} else {
iconCol.call(panel.renderHeaderIcon);
}
body.text('');
if (panel.renderBody) {
body.call(panel.renderBody);
}
var headerBody = headerMainCol.selectAll('.header-body');
headerBody.text('');
if (panel.renderHeaderBody) {
headerBody.call(panel.renderHeaderBody);
}
if (panel.message) {
var bodyTextRow = body.append('div')
.attr('class', 'assistant-row');
bodyTextRow.append('div')
.attr('class', 'icon-col');
var bodyBodyCol = bodyTextRow
.append('div')
.attr('class', 'main-col sep-top');
var bodyTextArea = bodyBodyCol
.append('div')
.attr('class', 'body-text');
bodyTextArea.html(panel.message);
}
shownPanel = panel;
}
function panelToDraw() {
var mode = context.mode();
if (mode.id === 'save') {
if (context.connection() && context.connection().authenticated()) {
return panelSave(context);
} else {
return panelAuthenticating(context);
}
} else if (mode.id === 'add-point' || mode.id === 'add-line' ||
mode.id === 'add-area' || mode.id === 'draw-line' ||
mode.id === 'draw-area') {
return panelAddDrawGeometry(context, mode);
} else if (mode.id === 'select') {
return panelSelect(context, mode.selectedIDs());
} else if (mode.id === 'drag-node' && mode.restoreSelectedIDs().length) {
return panelSelect(context, mode.restoreSelectedIDs());
} else if (mode.id === 'select-note') {
var note = context.connection() && context.connection().getNote(mode.selectedNoteID());
if (note) {
return panelSelectNote(context, note);
}
} else if (mode.id === 'select-error') {
if (mode.selectedErrorService() === 'keepRight') {
return panelSelectKeepRightError(context, mode.selectedErrorID());
} else if (mode.selectedErrorService() === 'improveOSM') {
return panelSelectImproveOSMError(context, mode.selectedErrorID());
}
} else if (mode.id === 'select-data') {
return panelSelectCustomData(context, mode.selectedDatum());
} else if (!didEditAnythingYet) {
if (savedChangeset) {
return panelSuccess(context);
}
if (context.history().hasRestorableChanges()) {
return panelRestore(context);
}
return panelWelcome(context);
}
scheduleCurrentLocationUpdate();
return panelMapping(context);
}
function redraw() {
if (container.empty()) return;
var mode = context.mode();
if (!mode || !mode.id) return;
if (mode.id !== 'browse') {
updateDidEditStatus();
}
var nextPanel = panelToDraw();
if (shownPanel && shownPanel.hash && nextPanel.hash &&
shownPanel.hash === nextPanel.hash) {
return; // panels are identical, so don't update anything
}
drawPanel(nextPanel);
}
function scheduleCurrentLocationUpdate() {
debouncedGetLocation(context.map().center(), context.map().zoom(), function(placeName) {
currLocation = placeName ? placeName : defaultLoc;
container.selectAll('.map-center-location')
.text(currLocation);
});
}
var debouncedGetLocation = _debounce(getLocation, 250);
function getLocation(loc, zoom, completionHandler) {
if (!services.geocoder || (zoom && zoom < 9)) {
completionHandler(null);
return;
}
services.geocoder.reverse(loc, function(err, result) {
if (err || !result || !result.address) {
completionHandler(null);
return;
}
var addr = result.address;
var place = ((!zoom || zoom > 14) && addr && (addr.town || addr.city || addr.county)) || '';
var region = (addr && (addr.state || addr.country)) || '';
var separator = (place && region) ? t('success.thank_you_where.separator') : '';
var formattedName = t('success.thank_you_where.format',
{ place: place, separator: separator, region: region }
);
completionHandler(formattedName);
});
}
assistant.didSaveChangset = function(changeset, count) {
savedChangeset = changeset;
savedChangeCount = count;
didEditAnythingYet = false;
redraw();
};
return assistant;
function panelWelcome(context) {
var panel = {
prominent: true,
theme: 'light',
headerIcon: utilGreetingIcon(),
title: utilTimeOfDayGreeting(),
onClose: function() {
updateDidEditStatus();
redraw();
}
};
function renderFirstSessionHeader(selection, bodyTextArea) {
var firstTimeInfo = t('assistant.launch.osm_info') + '<br/>' +
t('assistant.launch.first_time_tutorial') + '<br/>' +
t('assistant.launch.thanks_have_fun');
bodyTextArea.html(firstTimeInfo);
bodyTextArea.selectAll('a')
.attr('href', '#')
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
context.isFirstSession = false;
updateDidEditStatus();
context.container().call(uiIntro(context));
redraw();
});
selection
.append('div')
.attr('class', 'main-footer')
.append('button')
.attr('class', 'primary')
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
updateDidEditStatus();
redraw();
})
.append('span')
.text(t('assistant.launch.start_mapping'));
}
function renderBlockedAccountHeader(selection, bodyTextArea, details) {
var link = bodyTextArea
.html(t('assistant.launch.blocks.active', { displayName: '<b>' + details.display_name + '</b>' }))
.append('a')
.attr('class', 'link-out')
.attr('target', '_blank')
.attr('tabindex', -1)
.attr('href', context.connection().userURL(details.display_name) + '/blocks');
link.append('span')
.text(' ' + t('success.help_link_text'));
link
.call(svgIcon('#iD-icon-out-link', 'inline'));
d3_select('.assistant-header .subject-title span')
.text(t('assistant.notice'));
d3_select('.assistant-header .icon-col .icon use')
.attr('href', '#iD-icon-alert');
}
function renderAccountAnniversaryHeader(selection, bodyTextArea, details, joinDate, now) {
var yearCount = now.getFullYear() - joinDate.getFullYear();
var anniversaryInfo = t('assistant.launch.anniversary.years.' + (yearCount === 1 ? 'first' : 'subsequent'), {
years: '<b>' + yearCount + '</b>',
displayName: '<b>' + details.display_name + '</b>'
}) + '<br/>' +
t('assistant.launch.changesets_date', {
changesets: '<b>' + parseFloat(details.changesets_count).toLocaleString(currentLocale) + '</b>',
joinDate: '<b>' + joinDate.toLocaleDateString(currentLocale, { day: 'numeric', month: 'long', year: 'numeric' }) + '</b>'
});
bodyTextArea.html(anniversaryInfo);
d3_select('.assistant-header .subject-title span')
.text(t('assistant.launch.anniversary.happy_anniversary'));
d3_select('.assistant-header .icon-col .icon use')
.attr('href', '#fas-birthday-cake');
}
panel.renderHeaderBody = function(selection) {
var bodyTextArea = selection
.append('div')
.attr('class', 'body-text');
var osm = context.connection();
if (context.isFirstSession) {
renderFirstSessionHeader(selection, bodyTextArea);
return;
}
var genericWelcomesCount = 2;
bodyTextArea.html(t('assistant.launch.generic_welcome.' + Math.floor(Math.random() * genericWelcomesCount)));
if (!osm.authenticated()) return;
osm.userDetails(function(err, details) {
if (err || !details) return;
var joinDate = new Date(details.account_created);
var now = new Date();
if (parseFloat(details.active_blocks) > 0) {
// user has been blocked
renderBlockedAccountHeader(selection, bodyTextArea, details);
} else if (joinDate.getDate() === now.getDate() &&
joinDate.getMonth() === now.getMonth() &&
joinDate.getFullYear() < now.getFullYear() &&
parseFloat(details.changesets_count) > 1) {
// OSM anniversary
renderAccountAnniversaryHeader(selection, bodyTextArea, details, joinDate, now);
} else {
var loggedInInfo = t('assistant.launch.welcome_back_user', {
displayName: '<b>' + details.display_name + '</b>'
}) + '<br/>' +
t('assistant.launch.changesets', {
changesets: '<b>' + parseFloat(details.changesets_count).toLocaleString(currentLocale) + '</b>'
});
bodyTextArea.html(loggedInInfo);
}
});
};
return panel;
}
function panelRestore(context) {
var panel = {
prominent: true,
theme: 'light',
headerIcon: utilGreetingIcon(),
title: utilTimeOfDayGreeting()
};
panel.renderHeaderBody = function(selection) {
var bodyTextArea = selection
.append('div')
.attr('class', 'body-text');
var mainFooter = selection
.append('div')
.attr('class', 'main-footer');
var savedHistoryJSON = JSON.parse(context.history().savedHistoryJSON());
var lastGraph = savedHistoryJSON.stack &&
savedHistoryJSON.stack.length > 0 &&
savedHistoryJSON.stack[savedHistoryJSON.stack.length - 1];
if (!lastGraph) return;
var changeCount = (lastGraph.modified ? lastGraph.modified.length : 0) +
(lastGraph.deleted ? lastGraph.deleted.length : 0);
if (changeCount === 0) return;
var loc = lastGraph.transform &&
geoRawMercator()
.transform(lastGraph.transform)
.invert([0, 0]);
if (!loc) return;
var restoreInfoDict = {
count: '<b>' + changeCount.toString() + '</b>',
location: '<b class="restore-location">' + decimalCoordinatePair(loc, 3) + '</b>'
};
var infoID = 'count_loc';
if (savedHistoryJSON.timestamp) {
infoID = 'count_loc_time';
var milliseconds = (new Date()).getTime() - savedHistoryJSON.timestamp;
restoreInfoDict.duration = '<b>' + formattedRoundedDuration(milliseconds) + '</b>';
}
bodyTextArea.html(t('assistant.restore.info.' + infoID, restoreInfoDict) +
'<br/>' +
t('assistant.restore.ask'));
getLocation(loc, null, function(placeName) {
if (placeName) {
selection.selectAll('.restore-location')
.text(placeName);
}
});
mainFooter.append('button')
.attr('class', 'primary')
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
updateDidEditStatus();
context.container().selectAll('#content')
.attr('class', 'active');
context.history().restore();
redraw();
})
.append('span')
.text(t('assistant.restore.title'));
mainFooter.append('button')
.attr('class', 'destructive')
.on('click', function() {
d3_event.preventDefault();
d3_event.stopPropagation();
// don't show another welcome screen after discarding changes
updateDidEditStatus();
context.container().selectAll('#content')
.attr('class', 'active');
context.history().clearSaved();
context.map().pan([0,0]); // trigger a map redraw
redraw();
})
.append('span')
.text(t('assistant.restore.discard'));
};
return panel;
}
function panelMapping() {
var panel = {
headerIcon: 'fas-map-marked-alt',
modeLabel: t('assistant.mode.mapping'),
title: currLocation,
titleClass: 'map-center-location',
collapseCategory: 'browse'
};
panel.renderBody = function(selection) {
selection
.append('div')
.attr('class', 'feature-list-pane')
.call(featureSearch);
};
return panel;
}
function panelSelectKeepRightError(context, errorID) {
var error = services.keepRight.getError(errorID);
function errorTitle(d) {
var unknown = t('inspector.unknown');
if (!d) return unknown;
var errorType = d.error_type;
var parentErrorType = d.parent_error_type;
var et = dataEn.QA.keepRight.errorTypes[errorType];
var pt = dataEn.QA.keepRight.errorTypes[parentErrorType];
if (et && et.title) {
return t('QA.keepRight.errorTypes.' + errorType + '.title');
} else if (pt && pt.title) {
return t('QA.keepRight.errorTypes.' + parentErrorType + '.title');
} else {
return unknown;
}
}
var panel = {
theme: 'light',
modeLabel: t('QA.keepRight.title'),
title: errorTitle(error),
collapseCategory: 'inspect'
};
panel.renderHeaderIcon = function(selection) {
var icon = selection
.append('div')
.attr('class', 'error-header-icon')
.classed('new', error.id < 0);
icon
.append('div')
.attr('class', 'qa_error ' + error.service + ' error_id-' + error.id + ' error_type-' + error.parent_error_type)
.call(svgIcon('#iD-icon-bolt', 'qa_error-fill'));
};
panel.renderBody = function(selection) {
var editor = uiKeepRightEditor(context)
.error(error);
selection.call(editor);
};
return panel;
}
function panelSelectImproveOSMError(context, errorID) {
var error = services.improveOSM.getError(errorID);
function errorTitle(d) {
var unknown = t('inspector.unknown');
if (!d) return unknown;
var errorType = d.error_key;
var et = dataEn.QA.improveOSM.error_types[errorType];
if (et && et.title) {
return t('QA.improveOSM.error_types.' + errorType + '.title');
} else {
return unknown;
}
}
var panel = {
theme: 'light',
modeLabel: t('QA.improveOSM.title'),
title: errorTitle(error),
collapseCategory: 'inspect'
};
panel.renderHeaderIcon = function(selection) {
var iconEnter = selection
.append('div')
.attr('class', 'error-header-icon')
.classed('new', error.id < 0);
var svgEnter = iconEnter
.append('svg')
.attr('width', '20px')
.attr('height', '30px')
.attr('viewbox', '0 0 20 30')
.attr('class', [
'qa_error',
error.service,
'error_id-' + error.id,
'error_type-' + error.error_type,
'category-' + error.category
].join(' '));
svgEnter
.append('polygon')
.attr('fill', 'currentColor')
.attr('class', 'qa_error-fill')
.attr('points', '16,3 4,3 1,6 1,17 4,20 7,20 10,27 13,20 16,20 19,17.033 19,6');
var getIcon = function(d) {
var picon = d.icon;
if (!picon) {
return '';
} else {
var isMaki = /^maki-/.test(picon);
return '#' + picon + (isMaki ? '-11' : '');
}
};
svgEnter
.append('use')
.attr('class', 'icon-annotation')
.attr('width', '11px')
.attr('height', '11px')
.attr('transform', 'translate(4.5, 7)')
.attr('xlink:href', getIcon(error));
};
panel.renderBody = function(selection) {
var editor = uiImproveOsmEditor(context)
.error(error);
selection.call(editor);
};
return panel;
}
function panelSelectCustomData(context, datum) {
var panel = {
theme: 'light',
modeLabel: t('assistant.mode.inspecting'),
headerIcon: 'iD-icon-data',
title: t('map_data.layers.custom.title'),
collapseCategory: 'inspect'
};
panel.renderBody = function(selection) {
var editor = uiDataEditor(context)
.datum(datum);
selection.call(editor);
};
return panel;
}
function panelSelectNote(context, note) {
var panel = {
theme: 'light',
modeLabel: t('assistant.mode.inspecting'),
title: note.label(),
collapseCategory: 'inspect'
};
panel.renderHeaderIcon = function(selection) {
var icon = selection
.append('div')
.attr('class', 'note-header-icon ' + note.status)
.classed('new', note.id < 0);
icon
.call(svgIcon('#iD-icon-note', 'note-fill'));
var statusIcon = '#iD-icon-' + (note.id < 0 ? 'plus' : (note.status === 'open' ? 'close' : 'apply'));
icon
.append('div')
.attr('class', 'note-icon-annotation')
.call(svgIcon(statusIcon, 'icon-annotation'));
};
panel.renderBody = function(selection) {
var noteEditor = uiNoteEditor(context)
.note(note);
selection.call(noteEditor);
};
return panel;
}
function panelAddDrawGeometry(context, mode) {
var message = t('assistant.instructions.' + mode.id.replace('-', '_'));
if (mode.id === 'add-point' && mode.preset &&
mode.preset.geometry.indexOf('point') === -1) {
message = t('assistant.instructions.add_vertex');
} else if (mode.id.indexOf('draw') !== -1) {
var way = context.entity(mode.wayID);
if (way.nodes.length >= 4) {
message += '<br/>' + t('assistant.instructions.finishing');
}
}
var modeLabelID = 'drawing';
if (mode.id === 'add-point') {
modeLabelID = 'placing';
}
var panel = {
modeLabel: t('assistant.mode.' + modeLabelID),
title: mode.title,
message: message,
collapseCategory: 'draw'
};
panel.renderHeaderIcon = function(selection) {
selection.call(uiPresetIcon(context)
.geometry(mode.geometry)
.preset(mode.preset)
.sizeClass('small')
.pointMarker(false));
};
return panel;
}
function panelSelect(context, selectedIDs) {
var panel = {
hash: 'select ' + selectedIDs.toString(),
theme: 'light',
modeLabel: t('assistant.mode.inspecting'),
title: selectedIDs.length === 1 ? utilDisplayLabel(context.entity(selectedIDs[0]), context) :
t('assistant.feature_count.multiple', { count: selectedIDs.length.toString() }),
collapseCategory: 'inspect'
};
panel.renderHeaderIcon = function(selection) {
if (selectedIDs.length === 1) {
var entity = context.entity(selectedIDs[0]);
var geometry = entity.geometry(context.graph());
var preset = context.presets().match(entity, context.graph());
selection.call(uiPresetIcon(context)
.geometry(geometry)
.preset(preset)
.sizeClass('small')
.pointMarker(false));
} else {
selection.call(svgIcon('#fas-edit'));
}
};
panel.renderBody = function(selection) {
var mode = context.mode();
var entityEditor = uiEntityEditor(context)
.state('select')
.entityIDs(selectedIDs)
.newFeature(mode.newFeature && mode.newFeature());
selection.call(entityEditor);
};
return panel;
}
function panelAuthenticating() {
var panel = {
headerIcon: 'iD-icon-save',
modeLabel: t('assistant.mode.authenticating'),
title: t('assistant.commit.auth.osm_account'),
message: t('assistant.commit.auth.message'),
collapseCategory: 'save'
};
return panel;
}
function panelSave(context) {
var summary = context.history().difference().summary();
var titleID = summary.length === 1 ? 'change' : 'changes';
var panel = {
theme: 'light',
headerIcon: 'iD-icon-save',
modeLabel: t('assistant.mode.saving'),
title: t('commit.' + titleID, { count: summary.length }),
collapseCategory: 'save'
};
panel.renderBody = function(selection) {
var editor = uiCommit(context);
selection.call(editor);
};
return panel;
}
function panelSuccess(context) {
var savedIcon;
if (savedChangeCount <= 25) {
savedIcon = 'fas-smile-beam';
} else if (savedChangeCount <= 50) {
savedIcon = 'fas-grin-beam';
} else {
savedIcon = 'fas-laugh-beam';
}
var panel = {
prominent: true,
theme: 'light',
headerIcon: savedIcon,
title: t('assistant.commit.success.thank_you'),
collapseCategory: 'save',
onClose: function() {
updateDidEditStatus();
redraw();
}
};
panel.renderHeaderBody = function(selection) {
var bodyTextArea = selection
.append('div')
.attr('class', 'body-text');
bodyTextArea.html(
'<b>' + t('assistant.commit.success.just_improved', { location: currLocation }) + '</b>' +
'<br/>'
);
var link = bodyTextArea
.append('span')
.text(t('assistant.commit.success.propagation_help'))
.append('a')
.attr('class', 'link-out')
.attr('target', '_blank')
.attr('tabindex', -1)
.attr('href', t('success.help_link_url'));
link.append('span')
.text(' ' + t('success.help_link_text'));
link
.call(svgIcon('#iD-icon-out-link', 'inline'));
};
panel.renderBody = function(selection) {
var success = uiSuccess(context).changeset(savedChangeset);
selection.call(success);
};
return panel;
}
}