function render()

in modules/ui/commit.js [224:461]


    function render(selection) {
        var osm = context.connection();
        if (!osm) return;

        var header = selection.selectAll('.header')
            .data([0]);

        var headerTitle = header.enter()
            .append('div')
            .attr('class', 'header fillL');

        headerTitle
            .append('div')
            .append('h3')
            .html(t.html('commit.title'));

        headerTitle
            .append('button')
            .attr('class', 'close')
            .on('click', function() {
                dispatch.call('cancel', this);
            })
            .call(svgIcon('#iD-icon-close'));

        var body = selection.selectAll('.body')
            .data([0]);

        body = body.enter()
            .append('div')
            .attr('class', 'body')
            .merge(body);


        // Changeset Section
        var changesetSection = body.selectAll('.changeset-editor')
            .data([0]);

        changesetSection = changesetSection.enter()
            .append('div')
            .attr('class', 'modal-section changeset-editor')
            .merge(changesetSection);

        changesetSection
            .call(changesetEditor
                .changesetID(context.changeset.id)
                .tags(context.changeset.tags)
            );


        // Warnings
        body.call(commitWarnings);


        // Upload Explanation
        var saveSection = body.selectAll('.save-section')
            .data([0]);

        saveSection = saveSection.enter()
            .append('div')
            .attr('class','modal-section save-section fillL')
            .merge(saveSection);

        var prose = saveSection.selectAll('.commit-info')
            .data([0]);

        if (prose.enter().size()) {   // first time, make sure to update user details in prose
            _userDetails = null;
        }

        prose = prose.enter()
            .append('p')
            .attr('class', 'commit-info')
            .html(t.html('commit.upload_explanation'))
            .merge(prose);

        // always check if this has changed, but only update prose.html()
        // if needed, because it can trigger a style recalculation
        osm.userDetails(function(err, user) {
            if (err) return;

            if (_userDetails === user) return;  // no change
            _userDetails = user;

            var userLink = d3_select(document.createElement('div'));

            if (user.image_url) {
                userLink
                    .append('img')
                    .attr('src', user.image_url)
                    .attr('class', 'icon pre-text user-icon');
            }

            userLink
                .append('a')
                .attr('class', 'user-info')
                .html(user.display_name)
                .attr('href', osm.userURL(user.display_name))
                .attr('target', '_blank');

            prose
                .html(t.html('commit.upload_explanation_with_user', { user: userLink.html() }));
        });


        // Request Review
        var requestReview = saveSection.selectAll('.request-review')
            .data([0]);

        // Enter
        var requestReviewEnter = requestReview.enter()
            .append('div')
            .attr('class', 'request-review');

        var requestReviewDomId = utilUniqueString('commit-input-request-review');

        var labelEnter = requestReviewEnter
            .append('label')
            .attr('for', requestReviewDomId);

        if (!labelEnter.empty()) {
            labelEnter
                .call(uiTooltip().title(t.html('commit.request_review_info')).placement('top'));
        }

        labelEnter
            .append('input')
            .attr('type', 'checkbox')
            .attr('id', requestReviewDomId);

        labelEnter
            .append('span')
            .html(t.html('commit.request_review'));

        // Update
        requestReview = requestReview
            .merge(requestReviewEnter);

        var requestReviewInput = requestReview.selectAll('input')
            .property('checked', isReviewRequested(context.changeset.tags))
            .on('change', toggleRequestReview);


        // Buttons
        var buttonSection = saveSection.selectAll('.buttons')
            .data([0]);

        // enter
        var buttonEnter = buttonSection.enter()
            .append('div')
            .attr('class', 'buttons fillL');

        buttonEnter
            .append('button')
            .attr('class', 'secondary-action button cancel-button')
            .append('span')
            .attr('class', 'label')
            .html(t.html('commit.cancel'));

        var uploadButton = buttonEnter
            .append('button')
            .attr('class', 'action button save-button');

        uploadButton.append('span')
            .attr('class', 'label')
            .html(t.html('commit.save'));

        var uploadBlockerTooltipText = getUploadBlockerMessage();

        // update
        buttonSection = buttonSection
            .merge(buttonEnter);

        buttonSection.selectAll('.cancel-button')
            .on('click.cancel', function() {
                dispatch.call('cancel', this);
            });

        buttonSection.selectAll('.save-button')
            .classed('disabled', uploadBlockerTooltipText !== null)
            .on('click.save', function() {
                if (!d3_select(this).classed('disabled')) {
                    this.blur();    // avoid keeping focus on the button - #4641

                    for (var key in context.changeset.tags) {
                        // remove any empty keys before upload
                        if (!key) delete context.changeset.tags[key];
                    }

                    context.uploader().save(context.changeset);
                }
            });

        // remove any existing tooltip
        uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));

        if (uploadBlockerTooltipText) {
            buttonSection.selectAll('.save-button')
                .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
        }

        // Raw Tag Editor
        var tagSection = body.selectAll('.tag-section.raw-tag-editor')
            .data([0]);

        tagSection = tagSection.enter()
            .append('div')
            .attr('class', 'modal-section tag-section raw-tag-editor')
            .merge(tagSection);

        tagSection
            .call(rawTagEditor
                .tags(Object.assign({}, context.changeset.tags))   // shallow copy
                .render
            );

        var changesSection = body.selectAll('.commit-changes-section')
            .data([0]);

        changesSection = changesSection.enter()
            .append('div')
            .attr('class', 'modal-section commit-changes-section')
            .merge(changesSection);

        // Change summary
        changesSection.call(commitChanges.render);


        function toggleRequestReview() {
            var rr = requestReviewInput.property('checked');
            updateChangeset({ review_requested: (rr ? 'yes' : undefined) });

            tagSection
                .call(rawTagEditor
                    .tags(Object.assign({}, context.changeset.tags))   // shallow copy
                    .render
                );
        }
    }