in modules/ui/init.js [61:491]
function render(container) {
container
.on('click.ui', function(d3_event) {
// we're only concerned with the primary mouse button
if (d3_event.button !== 0) return;
if (!d3_event.composedPath) return;
// some targets have default click events we don't want to override
var isOkayTarget = d3_event.composedPath().some(function(node) {
// we only care about element nodes
return node.nodeType === 1 &&
// clicking <input> focuses it and/or changes a value
(node.nodeName === 'INPUT' ||
// clicking <label> affects its <input> by default
node.nodeName === 'LABEL' ||
// clicking <a> opens a hyperlink by default
node.nodeName === 'A');
});
if (isOkayTarget) return;
// disable double-tap-to-zoom on touchscreens
d3_event.preventDefault();
});
var detected = utilDetect();
// only WebKit supports gesture events
if ('GestureEvent' in window &&
// Listening for gesture events on iOS 13.4+ breaks double-tapping,
// but we only need to do this on desktop Safari anyway. – #7694
!detected.isMobileWebKit) {
// On iOS we disable pinch-to-zoom of the UI via the `touch-action`
// CSS property, but on desktop Safari we need to manually cancel the
// default gesture events.
container.on('gesturestart.ui gesturechange.ui gestureend.ui', function(d3_event) {
// disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
d3_event.preventDefault();
});
}
if ('PointerEvent' in window) {
d3_select(window)
.on('pointerdown.ui pointerup.ui', function(d3_event) {
var pointerType = d3_event.pointerType || 'mouse';
if (_lastPointerType !== pointerType) {
_lastPointerType = pointerType;
container
.attr('pointer', pointerType);
}
}, true);
} else {
_lastPointerType = 'mouse';
container
.attr('pointer', 'mouse');
}
container
.attr('lang', localizer.localeCode())
.attr('dir', localizer.textDirection());
// setup fullscreen keybindings (no button shown at this time)
container
.call(uiFullScreen(context));
var map = context.map();
map.redrawEnable(false); // don't draw until we've set zoom/lat/long
map
.on('hitMinZoom.ui', function() {
ui.flash
.iconName('#iD-icon-no')
.label(t.html('cannot_zoom'))();
});
container
.append('svg')
.attr('id', 'ideditor-defs')
.call(ui.svgDefs);
container
.append('div')
.attr('class', 'sidebar')
.call(ui.sidebar);
var content = container
.append('div')
.attr('class', 'main-content active');
// Top toolbar
content
.append('div')
.attr('class', 'top-toolbar-wrap')
.append('div')
.attr('class', 'top-toolbar fillD')
.call(uiTopToolbar(context));
content
.append('div')
.attr('class', 'main-map')
.attr('dir', 'ltr')
.call(map);
var overMap = content
.append('div')
.attr('class', 'over-map');
// HACK: Mobile Safari 14 likes to select anything selectable when long-
// pressing, even if it's not targeted. This conflicts with long-pressing
// to show the edit menu. We add a selectable offscreen element as the first
// child to trick Safari into not showing the selection UI.
overMap
.append('div')
.attr('class', 'select-trap')
.text('t');
overMap
.call(uiMapInMap(context))
.call(uiNotice(context));
overMap
.append('div')
.attr('class', 'spinner')
.call(uiSpinner(context));
// Map controls
var controls = overMap
.append('div')
.attr('class', 'map-controls');
controls
.append('div')
.attr('class', 'map-control zoombuttons')
.call(uiZoom(context));
controls
.append('div')
.attr('class', 'map-control zoom-to-selection-control')
.call(uiZoomToSelection(context));
controls
.append('div')
.attr('class', 'map-control geolocate-control')
.call(uiGeolocate(context));
controls.on('wheel.mapControls', function(d3_event) {
if (!d3_event.deltaX) {
controls.node().scrollTop += d3_event.deltaY;
}
});
// Add panes
// This should happen after map is initialized, as some require surface()
var panes = overMap
.append('div')
.attr('class', 'map-panes');
var uiPanes = [
uiPaneBackground(context),
uiPaneMapData(context),
uiPaneIssues(context),
uiPanePreferences(context),
uiPaneHelp(context)
];
uiPanes.forEach(function(pane) {
controls
.append('div')
.attr('class', 'map-control map-pane-control ' + pane.id + '-control')
.call(pane.renderToggleButton);
panes
.call(pane.renderPane);
});
ui.info = uiInfo(context);
overMap
.call(ui.info);
overMap
.append('div')
.attr('class', 'photoviewer')
.classed('al', true) // 'al'=left, 'ar'=right
.classed('hide', true)
.call(ui.photoviewer);
overMap
.append('div')
.attr('class', 'attribution-wrap')
.attr('dir', 'ltr')
.call(uiAttribution(context));
// Add footer
var about = content
.append('div')
.attr('class', 'map-footer');
about
.append('div')
.attr('class', 'api-status')
.call(uiStatus(context));
var footer = about
.append('div')
.attr('class', 'map-footer-bar fillD');
footer
.append('div')
.attr('class', 'flash-wrap footer-hide');
var footerWrap = footer
.append('div')
.attr('class', 'main-footer-wrap footer-show');
footerWrap
.append('div')
.attr('class', 'scale-block')
.call(uiScale(context));
var aboutList = footerWrap
.append('div')
.attr('class', 'info-block')
.append('ul')
.attr('class', 'map-footer-list');
aboutList
.append('li')
.attr('class', 'user-list')
.call(uiContributors(context));
aboutList
.append('li')
.attr('class', 'fb-road-license')
.attr('tabindex', -1)
.call(uiRapidServiceLicense());
var apiConnections = context.apiConnections();
if (apiConnections && apiConnections.length > 1) {
aboutList
.append('li')
.attr('class', 'source-switch')
.call(uiSourceSwitch(context)
.keys(apiConnections)
);
}
aboutList
.append('li')
.attr('class', 'issues-info')
.call(uiIssuesInfo(context));
aboutList
.append('li')
.attr('class', 'feature-warning')
.call(uiFeatureInfo(context));
var issueLinks = aboutList
.append('li');
issueLinks
.append('a')
.attr('target', '_blank')
.attr('tabindex', -1)
.attr('href', 'https://github.com/facebookincubator/RapiD/issues')
.call(svgIcon('#iD-icon-bug', 'light'))
.call(uiTooltip().title(t.html('report_a_bug')).placement('top'));
issueLinks
.append('a')
.attr('target', '_blank')
.attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
.call(svgIcon('#iD-icon-translate', 'light'))
.call(uiTooltip().title(t.html('help_translate')).placement('top'));
aboutList
.append('li')
.attr('class', 'version')
.call(uiVersion(context));
if (!context.embed()) {
aboutList
.call(uiAccount(context));
}
// Setup map dimensions and move map to initial center/zoom.
// This should happen after .main-content and toolbars exist.
ui.onResize();
map.redrawEnable(true);
ui.hash = behaviorHash(context);
ui.hash();
if (!ui.hash.hadHash) {
map.centerZoom([0, 0], 2);
}
// Bind events
window.onbeforeunload = function() {
return context.save();
};
window.onunload = function() {
context.history().unlock();
};
d3_select(window)
.on('resize.editor', function() {
ui.onResize();
});
var panPixels = 80;
context.keybinding()
.on('⌫', function(d3_event) { d3_event.preventDefault(); })
.on([t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
.on('←', pan([panPixels, 0]))
.on('↑', pan([0, panPixels]))
.on('→', pan([-panPixels, 0]))
.on('↓', pan([0, -panPixels]))
.on(uiCmd('⌥←'), pan([map.dimensions()[0], 0]))
.on(uiCmd('⌥↑'), pan([0, map.dimensions()[1]]))
.on(uiCmd('⌥→'), pan([-map.dimensions()[0], 0]))
.on(uiCmd('⌥↓'), pan([0, -map.dimensions()[1]]))
.on(uiCmd('⌘' + t('background.key')), function quickSwitch(d3_event) {
if (d3_event) {
d3_event.stopImmediatePropagation();
d3_event.preventDefault();
}
var previousBackground = context.background().findSource(prefs('background-last-used-toggle'));
if (previousBackground) {
var currentBackground = context.background().baseLayerSource();
prefs('background-last-used-toggle', currentBackground.id);
prefs('background-last-used', previousBackground.id);
context.background().baseLayerSource(previousBackground);
}
})
.on(t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
d3_event.preventDefault();
d3_event.stopPropagation();
context.map().toggleWireframe();
})
.on(uiCmd('⌥' + t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
d3_event.preventDefault();
d3_event.stopPropagation();
// Don't allow layer changes while drawing - #6584
var mode = context.mode();
if (mode && /^draw/.test(mode.id)) return;
var layer = context.layers().layer('osm');
if (layer) {
layer.enabled(!layer.enabled());
if (!layer.enabled()) {
context.enter(modeBrowse(context));
}
}
})
.on(t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
d3_event.preventDefault();
context.map().toggleHighlightEdited();
});
context
.on('enter.editor', function(entered) {
container
.classed('mode-' + entered.id, true);
})
.on('exit.editor', function(exited) {
container
.classed('mode-' + exited.id, false);
});
context.enter(modeBrowse(context));
var osm = context.connection();
if (!_initCounter++) {
if (!ui.hash.startWalkthrough) {
if (context.history().lock() && context.history().hasRestorableChanges()) {
context.container()
.call(uiRestore(context));
// // If users have already seen the 'welcome to RapiD' splash screen, don't also
// // show them the what's new screen
// } else if (prefs('sawRapidSplash')) {
// context.container()
// .call(uiRapidWhatsNew(context));
} else if (osm && osm.authenticated()) {
context.container()
.call(uiRapidSplash(context));
}
}
context.container()
.call(ui.shortcuts);
}
var auth = uiLoading(context).message(t.html('loading_auth')).blocking(true);
if (osm && auth) {
osm
.on('authLoading.ui', function() {
context.container()
.call(auth);
})
.on('authDone.ui', function() {
auth.close();
});
}
_initCounter++;
if (ui.hash.startWalkthrough) {
ui.hash.startWalkthrough = false;
context.container().call(uiIntro(context));
}
function pan(d) {
return function(d3_event) {
if (d3_event.shiftKey) return;
if (context.container().select('.combobox').size()) return;
d3_event.preventDefault();
context.map().pan(d, 100);
};
}
}