diff --git a/modules/ui/index.js b/modules/ui/index.js
index e663bed92..256d57a7f 100644
--- a/modules/ui/index.js
+++ b/modules/ui/index.js
@@ -1,8 +1,6 @@
export { uiInit } from './init';
export { uiAccount } from './account';
export { uiAttribution } from './attribution';
-export { uiBackgroundDisplayOptions } from './background_display_options';
-export { uiBackgroundOffset } from './background_offset';
export { uiChangesetEditor } from './changeset_editor';
export { uiCmd } from './cmd';
export { uiCombobox } from './combobox';
diff --git a/modules/ui/init.js b/modules/ui/init.js
index b893cbf60..7c0d33160 100644
--- a/modules/ui/init.js
+++ b/modules/ui/init.js
@@ -37,11 +37,11 @@ import { uiVersion } from './version';
import { uiZoom } from './zoom';
import { uiCmd } from './cmd';
-import { uiBackground } from './panes/background';
-import { uiHelp } from './panes/help';
-import { uiIssues } from './panes/issues';
-import { uiMapData } from './panes/map_data';
-import { uiPreferences } from './panes/preferences';
+import { uiPaneBackground } from './panes/background';
+import { uiPaneHelp } from './panes/help';
+import { uiPaneIssues } from './panes/issues';
+import { uiPaneMapData } from './panes/map_data';
+import { uiPanePreferences } from './panes/preferences';
export function uiInit(context) {
var _initCounter = 0;
@@ -107,11 +107,11 @@ export function uiInit(context) {
.call(uiGeolocate(context));
var uiPanes = [
- uiBackground(context),
- uiMapData(context),
- uiIssues(context),
- uiPreferences(context),
- uiHelp(context)
+ uiPaneBackground(context),
+ uiPaneMapData(context),
+ uiPaneIssues(context),
+ uiPanePreferences(context),
+ uiPaneHelp(context)
];
uiPanes.forEach(function(pane) {
diff --git a/modules/ui/panes/background.js b/modules/ui/panes/background.js
index 90c9f8f36..88d4eba13 100644
--- a/modules/ui/panes/background.js
+++ b/modules/ui/panes/background.js
@@ -1,304 +1,36 @@
import _debounce from 'lodash-es/debounce';
-import { descending as d3_descending, ascending as d3_ascending } from 'd3-array';
import { event as d3_event, select as d3_select } from 'd3-selection';
-import { t, textDirection } from '../../util/locale';
-import { tooltip } from '../../util/tooltip';
-import { svgIcon } from '../../svg/icon';
-import { uiBackgroundDisplayOptions } from '../background_display_options';
-import { uiBackgroundOffset } from '../background_offset';
+import { t } from '../../util/locale';
import { uiCmd } from '../cmd';
-import { uiDisclosure } from '../disclosure';
-import { uiMapInMap } from '../map_in_map';
-import { uiSettingsCustomBackground } from '../settings/custom_background';
-import { uiTooltipHtml } from '../tooltipHtml';
import { uiPane } from '../pane';
+import { uiBackgroundDisplayOptions } from '../sections/background_display_options';
+import { uiBackgroundList } from '../sections/background_list';
+import { uiBackgroundOffset } from '../sections/background_offset';
+import { uiOverlayList } from '../sections/overlay_list';
-export function uiBackground(context) {
+export function uiPaneBackground(context) {
var _key = t('background.key');
- var _customSource = context.background().findSource('custom');
- var _previousBackground = context.background().findSource(context.storage('background-last-used-toggle'));
-
- var _backgroundList = d3_select(null);
- var _overlayList = d3_select(null);
+ var _backgroundListContainer = d3_select(null);
+ var _overlayListContainer = d3_select(null);
var _displayOptionsContainer = d3_select(null);
var _offsetContainer = d3_select(null);
+ var backgroundList = uiBackgroundList(context);
var backgroundDisplayOptions = uiBackgroundDisplayOptions(context);
var backgroundOffset = uiBackgroundOffset(context);
-
- var settingsCustomBackground = uiSettingsCustomBackground(context)
- .on('change', customChanged);
-
-
- function setTooltips(selection) {
- selection.each(function(d, i, nodes) {
- var item = d3_select(this).select('label');
- var span = item.select('span');
- var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
- var description = d.description();
- var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
-
- item.call(tooltip().destroyAny);
-
- if (d === _previousBackground) {
- item.call(tooltip()
- .placement(placement)
- .html(true)
- .title(function() {
- var tip = '
' + t('background.switch') + '
';
- return uiTooltipHtml(tip, uiCmd('⌘' + _key));
- })
- );
- } else if (description || isOverflowing) {
- item.call(tooltip()
- .placement(placement)
- .title(description || d.name())
- );
- }
- });
- }
-
-
- function updateLayerSelections(selection) {
- function active(d) {
- return context.background().showsLayer(d);
- }
-
- selection.selectAll('li')
- .classed('active', active)
- .classed('switch', function(d) { return d === _previousBackground; })
- .call(setTooltips)
- .selectAll('input')
- .property('checked', active);
- }
-
-
- function chooseBackground(d) {
- if (d.id === 'custom' && !d.template()) {
- return editCustom();
- }
-
- d3_event.preventDefault();
- _previousBackground = context.background().baseLayerSource();
- context.storage('background-last-used-toggle', _previousBackground.id);
- context.storage('background-last-used', d.id);
- context.background().baseLayerSource(d);
- _backgroundList.call(updateLayerSelections);
- document.activeElement.blur();
- }
-
-
- function customChanged(d) {
- if (d && d.template) {
- _customSource.template(d.template);
- chooseBackground(_customSource);
- } else {
- _customSource.template('');
- chooseBackground(context.background().findSource('none'));
- }
- }
-
-
- function editCustom() {
- d3_event.preventDefault();
- context.container()
- .call(settingsCustomBackground);
- }
-
-
- function chooseOverlay(d) {
- d3_event.preventDefault();
- context.background().toggleOverlayLayer(d);
- _overlayList.call(updateLayerSelections);
- document.activeElement.blur();
- }
-
-
- function drawListItems(layerList, type, change, filter) {
- var sources = context.background()
- .sources(context.map().extent(), context.map().zoom(), true)
- .filter(filter);
-
- var layerLinks = layerList.selectAll('li')
- .data(sources, function(d) { return d.name(); });
-
- layerLinks.exit()
- .remove();
-
- var enter = layerLinks.enter()
- .append('li')
- .classed('layer-custom', function(d) { return d.id === 'custom'; })
- .classed('best', function(d) { return d.best(); });
-
- var label = enter
- .append('label');
-
- label
- .append('input')
- .attr('type', type)
- .attr('name', 'layers')
- .on('change', change);
-
- label
- .append('span')
- .text(function(d) { return d.name(); });
-
- enter.filter(function(d) { return d.id === 'custom'; })
- .append('button')
- .attr('class', 'layer-browse')
- .call(tooltip()
- .title(t('settings.custom_background.tooltip'))
- .placement((textDirection === 'rtl') ? 'right' : 'left')
- )
- .on('click', editCustom)
- .call(svgIcon('#iD-icon-more'));
-
- enter.filter(function(d) { return d.best(); })
- .append('div')
- .attr('class', 'best')
- .call(tooltip()
- .title(t('background.best_imagery'))
- .placement((textDirection === 'rtl') ? 'right' : 'left')
- )
- .append('span')
- .html('★');
-
-
- layerList.selectAll('li')
- .sort(sortSources);
-
- layerList
- .call(updateLayerSelections);
-
-
- function sortSources(a, b) {
- return a.best() && !b.best() ? -1
- : b.best() && !a.best() ? 1
- : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
- }
- }
-
-
- function renderBackgroundList(selection) {
-
- // the background list
- var container = selection.selectAll('.layer-background-list')
- .data([0]);
-
- _backgroundList = container.enter()
- .append('ul')
- .attr('class', 'layer-list layer-background-list')
- .attr('dir', 'auto')
- .merge(container);
-
-
- // add minimap toggle below list
- var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
- .data([0])
- .enter()
- .append('ul')
- .attr('class', 'layer-list bg-extras-list');
-
- var minimapLabelEnter = bgExtrasListEnter
- .append('li')
- .attr('class', 'minimap-toggle-item')
- .append('label')
- .call(tooltip()
- .html(true)
- .title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
- .placement('top')
- );
-
- minimapLabelEnter
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function() {
- d3_event.preventDefault();
- uiMapInMap.toggle();
- });
-
- minimapLabelEnter
- .append('span')
- .text(t('background.minimap.description'));
-
-
- var panelLabelEnter = bgExtrasListEnter
- .append('li')
- .attr('class', 'background-panel-toggle-item')
- .append('label')
- .call(tooltip()
- .html(true)
- .title(uiTooltipHtml(t('background.panel.tooltip'), uiCmd('⌘⇧' + t('info_panels.background.key'))))
- .placement('top')
- );
-
- panelLabelEnter
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function() {
- d3_event.preventDefault();
- context.ui().info.toggle('background');
- });
-
- panelLabelEnter
- .append('span')
- .text(t('background.panel.description'));
-
-
- // "Info / Report a Problem" link
- selection.selectAll('.imagery-faq')
- .data([0])
- .enter()
- .append('div')
- .attr('class', 'imagery-faq')
- .append('a')
- .attr('target', '_blank')
- .call(svgIcon('#iD-icon-out-link', 'inline'))
- .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
- .append('span')
- .text(t('background.imagery_problem_faq'));
-
- updateBackgroundList();
- }
-
-
- function renderOverlayList(selection) {
- var container = selection.selectAll('.layer-overlay-list')
- .data([0]);
-
- _overlayList = container.enter()
- .append('ul')
- .attr('class', 'layer-list layer-overlay-list')
- .attr('dir', 'auto')
- .merge(container);
-
- updateOverlayList();
- }
-
- function updateBackgroundList() {
- _backgroundList
- .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
- }
-
- function updateOverlayList() {
- _overlayList
- .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
- }
-
+ var overlayList = uiOverlayList(context);
function update() {
- if (!backgroundPane.selection().select('.disclosure-wrap-background_list').classed('hide')) {
- updateBackgroundList();
- }
+ _backgroundListContainer
+ .call(backgroundList);
- if (!backgroundPane.selection().select('.disclosure-wrap-overlay_list').classed('hide')) {
- updateOverlayList();
- }
+ _overlayListContainer
+ .call(overlayList);
_displayOptionsContainer
.call(backgroundDisplayOptions);
@@ -307,18 +39,21 @@ export function uiBackground(context) {
.call(backgroundOffset);
}
-
function quickSwitch() {
if (d3_event) {
d3_event.stopImmediatePropagation();
d3_event.preventDefault();
}
- if (_previousBackground) {
- chooseBackground(_previousBackground);
+ var previousBackground = context.background().findSource(context.storage('background-last-used-toggle'));
+ if (previousBackground) {
+ var newPreviousBackground = context.background().baseLayerSource();
+ context.storage('background-last-used-toggle', newPreviousBackground.id);
+ context.storage('background-last-used', previousBackground.id);
+ context.background().baseLayerSource(previousBackground);
+ document.activeElement.blur();
}
}
-
var backgroundPane = uiPane('background', context)
.key(_key)
.title(t('background.title'))
@@ -328,22 +63,14 @@ export function uiBackground(context) {
backgroundPane.renderContent = function(content) {
// background list
- content
+ _backgroundListContainer = content
.append('div')
- .attr('class', 'background-background-list-container')
- .call(uiDisclosure(context, 'background_list', true)
- .title(t('background.backgrounds'))
- .content(renderBackgroundList)
- );
+ .attr('class', 'background-background-list-container');
// overlay list
- content
+ _overlayListContainer = content
.append('div')
- .attr('class', 'background-overlay-list-container')
- .call(uiDisclosure(context, 'overlay_list', true)
- .title(t('background.overlays'))
- .content(renderOverlayList)
- );
+ .attr('class', 'background-overlay-list-container');
// display options
_displayOptionsContainer = content
@@ -355,6 +82,8 @@ export function uiBackground(context) {
.append('div')
.attr('class', 'background-offset');
+ update();
+
// add listeners
context.map()
@@ -362,13 +91,9 @@ export function uiBackground(context) {
_debounce(function() { window.requestIdleCallback(update); }, 1000)
);
-
context.background()
.on('change.background-update', update);
-
- update();
-
context.keybinding()
.on(uiCmd('⌘' + _key), quickSwitch);
};
diff --git a/modules/ui/panes/help.js b/modules/ui/panes/help.js
index c23bde88e..eec1a42d3 100644
--- a/modules/ui/panes/help.js
+++ b/modules/ui/panes/help.js
@@ -11,7 +11,7 @@ import { t, textDirection } from '../../util/locale';
import { tooltip } from '../../util/tooltip';
import { icon } from '../intro/helper';
-export function uiHelp(context) {
+export function uiPaneHelp(context) {
var docKeys = [
['help', [
diff --git a/modules/ui/panes/issues.js b/modules/ui/panes/issues.js
index 38705d204..ad0e2885e 100644
--- a/modules/ui/panes/issues.js
+++ b/modules/ui/panes/issues.js
@@ -13,7 +13,7 @@ import { utilGetSetValue, utilHighlightEntities, utilNoAuto } from '../../util';
import { uiPane } from '../pane';
-export function uiIssues(context) {
+export function uiPaneIssues(context) {
var MINSQUARE = 0;
var MAXSQUARE = 20;
diff --git a/modules/ui/panes/map_data.js b/modules/ui/panes/map_data.js
index 9686f87fc..15dd49eec 100644
--- a/modules/ui/panes/map_data.js
+++ b/modules/ui/panes/map_data.js
@@ -15,7 +15,7 @@ import { uiCmd } from '../cmd';
import { uiPane } from '../pane';
-export function uiMapData(context) {
+export function uiPaneMapData(context) {
var osmDataToggleKey = uiCmd('⌥' + t('area_fill.wireframe.key'));
var features = context.features().keys();
var layers = context.layers();
diff --git a/modules/ui/panes/preferences.js b/modules/ui/panes/preferences.js
index db284ace5..af16b0fe9 100644
--- a/modules/ui/panes/preferences.js
+++ b/modules/ui/panes/preferences.js
@@ -7,7 +7,7 @@ import { uiDisclosure } from '../disclosure';
import { uiPane } from '../pane';
-export function uiPreferences(context) {
+export function uiPanePreferences(context) {
let _showThirdPartyIcons = context.storage('preferences.privacy.thirdpartyicons') || 'true';
function renderPrivacyOptions(selection) {
diff --git a/modules/ui/background_display_options.js b/modules/ui/sections/background_display_options.js
similarity index 95%
rename from modules/ui/background_display_options.js
rename to modules/ui/sections/background_display_options.js
index f22f5d7e8..9632dcb4c 100644
--- a/modules/ui/background_display_options.js
+++ b/modules/ui/sections/background_display_options.js
@@ -3,10 +3,10 @@ import {
select as d3_select
} from 'd3-selection';
-import { t, textDirection } from '../util/locale';
-import { svgIcon } from '../svg/icon';
-import { uiDisclosure } from './disclosure';
-import { utilDetect } from '../util/detect';
+import { t, textDirection } from '../../util/locale';
+import { svgIcon } from '../../svg/icon';
+import { uiDisclosure } from '../disclosure';
+import { utilDetect } from '../../util/detect';
export function uiBackgroundDisplayOptions(context) {
diff --git a/modules/ui/sections/background_list.js b/modules/ui/sections/background_list.js
new file mode 100644
index 000000000..49c5265a5
--- /dev/null
+++ b/modules/ui/sections/background_list.js
@@ -0,0 +1,268 @@
+import { descending as d3_descending, ascending as d3_ascending } from 'd3-array';
+import {
+ event as d3_event,
+ select as d3_select
+} from 'd3-selection';
+
+import { t, textDirection } from '../../util/locale';
+import { tooltip } from '../../util/tooltip';
+import { svgIcon } from '../../svg/icon';
+import { uiCmd } from '../cmd';
+import { uiDisclosure } from '../disclosure';
+import { uiSettingsCustomBackground } from '../settings/custom_background';
+import { uiMapInMap } from '../map_in_map';
+import { uiTooltipHtml } from '../tooltipHtml';
+
+export function uiBackgroundList(context) {
+
+ var _backgroundList = d3_select(null);
+
+ var _customSource = context.background().findSource('custom');
+
+ var settingsCustomBackground = uiSettingsCustomBackground(context)
+ .on('change', customChanged);
+
+ function previousBackgroundID() {
+ return context.storage('background-last-used-toggle');
+ }
+
+ function render(selection) {
+
+ // the background list
+ var container = selection.selectAll('.layer-background-list')
+ .data([0]);
+
+ _backgroundList = container.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-background-list')
+ .attr('dir', 'auto')
+ .merge(container);
+
+
+ // add minimap toggle below list
+ var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
+ .data([0])
+ .enter()
+ .append('ul')
+ .attr('class', 'layer-list bg-extras-list');
+
+ var minimapLabelEnter = bgExtrasListEnter
+ .append('li')
+ .attr('class', 'minimap-toggle-item')
+ .append('label')
+ .call(tooltip()
+ .html(true)
+ .title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
+ .placement('top')
+ );
+
+ minimapLabelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function() {
+ d3_event.preventDefault();
+ uiMapInMap.toggle();
+ });
+
+ minimapLabelEnter
+ .append('span')
+ .text(t('background.minimap.description'));
+
+
+ var panelLabelEnter = bgExtrasListEnter
+ .append('li')
+ .attr('class', 'background-panel-toggle-item')
+ .append('label')
+ .call(tooltip()
+ .html(true)
+ .title(uiTooltipHtml(t('background.panel.tooltip'), uiCmd('⌘⇧' + t('info_panels.background.key'))))
+ .placement('top')
+ );
+
+ panelLabelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function() {
+ d3_event.preventDefault();
+ context.ui().info.toggle('background');
+ });
+
+ panelLabelEnter
+ .append('span')
+ .text(t('background.panel.description'));
+
+
+ // "Info / Report a Problem" link
+ selection.selectAll('.imagery-faq')
+ .data([0])
+ .enter()
+ .append('div')
+ .attr('class', 'imagery-faq')
+ .append('a')
+ .attr('target', '_blank')
+ .call(svgIcon('#iD-icon-out-link', 'inline'))
+ .attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
+ .append('span')
+ .text(t('background.imagery_problem_faq'));
+
+ updateBackgroundList();
+ }
+
+ function setTooltips(selection) {
+ selection.each(function(d, i, nodes) {
+ var item = d3_select(this).select('label');
+ var span = item.select('span');
+ var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
+ var description = d.description();
+ var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
+
+ item.call(tooltip().destroyAny);
+
+ if (d.id === previousBackgroundID()) {
+ item.call(tooltip()
+ .placement(placement)
+ .html(true)
+ .title(function() {
+ var tip = '' + t('background.switch') + '
';
+ return uiTooltipHtml(tip, uiCmd('⌘' + t('background.key')));
+ })
+ );
+ } else if (description || isOverflowing) {
+ item.call(tooltip()
+ .placement(placement)
+ .title(description || d.name())
+ );
+ }
+ });
+ }
+
+ function updateBackgroundList() {
+ _backgroundList
+ .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
+ }
+
+ function drawListItems(layerList, type, change, filter) {
+ var sources = context.background()
+ .sources(context.map().extent(), context.map().zoom(), true)
+ .filter(filter);
+
+ var layerLinks = layerList.selectAll('li')
+ .data(sources, function(d) { return d.name(); });
+
+ layerLinks.exit()
+ .remove();
+
+ var enter = layerLinks.enter()
+ .append('li')
+ .classed('layer-custom', function(d) { return d.id === 'custom'; })
+ .classed('best', function(d) { return d.best(); });
+
+ var label = enter
+ .append('label');
+
+ label
+ .append('input')
+ .attr('type', type)
+ .attr('name', 'layers')
+ .on('change', change);
+
+ label
+ .append('span')
+ .text(function(d) { return d.name(); });
+
+ enter.filter(function(d) { return d.id === 'custom'; })
+ .append('button')
+ .attr('class', 'layer-browse')
+ .call(tooltip()
+ .title(t('settings.custom_background.tooltip'))
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ )
+ .on('click', editCustom)
+ .call(svgIcon('#iD-icon-more'));
+
+ enter.filter(function(d) { return d.best(); })
+ .append('div')
+ .attr('class', 'best')
+ .call(tooltip()
+ .title(t('background.best_imagery'))
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ )
+ .append('span')
+ .html('★');
+
+
+ layerList.selectAll('li')
+ .sort(sortSources);
+
+ layerList
+ .call(updateLayerSelections);
+
+
+ function sortSources(a, b) {
+ return a.best() && !b.best() ? -1
+ : b.best() && !a.best() ? 1
+ : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
+ }
+ }
+
+ function updateLayerSelections(selection) {
+ function active(d) {
+ return context.background().showsLayer(d);
+ }
+
+ selection.selectAll('li')
+ .classed('active', active)
+ .classed('switch', function(d) { return d.id === previousBackgroundID(); })
+ .call(setTooltips)
+ .selectAll('input')
+ .property('checked', active);
+ }
+
+
+ function chooseBackground(d) {
+ if (d.id === 'custom' && !d.template()) {
+ return editCustom();
+ }
+
+ d3_event.preventDefault();
+ var previousBackground = context.background().baseLayerSource();
+ context.storage('background-last-used-toggle', previousBackground.id);
+ context.storage('background-last-used', d.id);
+ context.background().baseLayerSource(d);
+ document.activeElement.blur();
+ }
+
+
+ function customChanged(d) {
+ if (d && d.template) {
+ _customSource.template(d.template);
+ chooseBackground(_customSource);
+ } else {
+ _customSource.template('');
+ chooseBackground(context.background().findSource('none'));
+ }
+ }
+
+
+ function editCustom() {
+ d3_event.preventDefault();
+ context.container()
+ .call(settingsCustomBackground);
+ }
+
+
+ function backgroundList(selection) {
+ selection
+ .call(uiDisclosure(context, 'background_list', true)
+ .title(t('background.backgrounds'))
+ .content(render)
+ );
+ }
+
+ context.background()
+ .on('change.background_list', function() {
+ _backgroundList.call(updateLayerSelections);
+ });
+
+ return backgroundList;
+}
diff --git a/modules/ui/background_offset.js b/modules/ui/sections/background_offset.js
similarity index 96%
rename from modules/ui/background_offset.js
rename to modules/ui/sections/background_offset.js
index 00016ab1c..407626576 100644
--- a/modules/ui/background_offset.js
+++ b/modules/ui/sections/background_offset.js
@@ -4,10 +4,10 @@ import {
selectAll as d3_selectAll
} from 'd3-selection';
-import { t, textDirection } from '../util/locale';
-import { geoMetersToOffset, geoOffsetToMeters } from '../geo';
-import { svgIcon } from '../svg/icon';
-import { uiDisclosure } from './disclosure';
+import { t, textDirection } from '../../util/locale';
+import { geoMetersToOffset, geoOffsetToMeters } from '../../geo';
+import { svgIcon } from '../../svg/icon';
+import { uiDisclosure } from '../disclosure';
export function uiBackgroundOffset(context) {
diff --git a/modules/ui/sections/overlay_list.js b/modules/ui/sections/overlay_list.js
new file mode 100644
index 000000000..757d5ebdc
--- /dev/null
+++ b/modules/ui/sections/overlay_list.js
@@ -0,0 +1,124 @@
+import { descending as d3_descending, ascending as d3_ascending } from 'd3-array';
+import {
+ event as d3_event,
+ select as d3_select
+} from 'd3-selection';
+
+import { t } from '../../util/locale';
+import { tooltip } from '../../util/tooltip';
+import { uiDisclosure } from '../disclosure';
+
+export function uiOverlayList(context) {
+
+ var _overlayList = d3_select(null);
+
+ function render(selection) {
+
+ var container = selection.selectAll('.layer-overlay-list')
+ .data([0]);
+
+ _overlayList = container.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-overlay-list')
+ .attr('dir', 'auto')
+ .merge(container);
+
+ updateOverlayList();
+ }
+
+ function setTooltips(selection) {
+ selection.each(function(d, i, nodes) {
+ var item = d3_select(this).select('label');
+ var span = item.select('span');
+ var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
+ var description = d.description();
+ var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
+
+ item.call(tooltip().destroyAny);
+
+ if (description || isOverflowing) {
+ item.call(tooltip()
+ .placement(placement)
+ .title(description || d.name())
+ );
+ }
+ });
+ }
+
+ function updateLayerSelections(selection) {
+ function active(d) {
+ return context.background().showsLayer(d);
+ }
+
+ selection.selectAll('li')
+ .classed('active', active)
+ .call(setTooltips)
+ .selectAll('input')
+ .property('checked', active);
+ }
+
+
+ function chooseOverlay(d) {
+ d3_event.preventDefault();
+ context.background().toggleOverlayLayer(d);
+ _overlayList.call(updateLayerSelections);
+ document.activeElement.blur();
+ }
+
+ function drawListItems(layerList, type, change, filter) {
+ var sources = context.background()
+ .sources(context.map().extent(), context.map().zoom(), true)
+ .filter(filter);
+
+ var layerLinks = layerList.selectAll('li')
+ .data(sources, function(d) { return d.name(); });
+
+ layerLinks.exit()
+ .remove();
+
+ var enter = layerLinks.enter()
+ .append('li');
+
+ var label = enter
+ .append('label');
+
+ label
+ .append('input')
+ .attr('type', type)
+ .attr('name', 'layers')
+ .on('change', change);
+
+ label
+ .append('span')
+ .text(function(d) { return d.name(); });
+
+
+ layerList.selectAll('li')
+ .sort(sortSources);
+
+ layerList
+ .call(updateLayerSelections);
+
+
+ function sortSources(a, b) {
+ return a.best() && !b.best() ? -1
+ : b.best() && !a.best() ? 1
+ : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
+ }
+ }
+
+ function updateOverlayList() {
+ _overlayList
+ .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
+ }
+
+ function overlayList(selection) {
+ selection
+ .call(uiDisclosure(context, 'overlay_list', true)
+ .title(t('background.overlays'))
+ .content(render)
+ );
+ }
+
+ return overlayList;
+}