function render()

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);
            };
        }

    }