diff --git a/css/80_app.css b/css/80_app.css
index bffaa68b7..2e14ac1d0 100644
--- a/css/80_app.css
+++ b/css/80_app.css
@@ -130,7 +130,6 @@ a, button, input, textarea {
a,
button,
.checkselect label:hover,
-.opacity-options li,
.radial-menu-item {
cursor: pointer;
}
@@ -268,7 +267,7 @@ table th {
}
table.tags, table.tags td, table.tags th {
- border: 1px solid #CCC;
+ border: 1px solid #ccc;
padding: 4px;
}
@@ -304,7 +303,7 @@ ul li { list-style: none;}
display: block;
height: 30px;
background-color: white;
- color: #7092FF;
+ color: #7092ff;
cursor: pointer;
}
@@ -740,6 +739,30 @@ button.save.has-count .count::before {
position: absolute;
}
+
+/* Hide-Toggle
+------------------------------------------------------- */
+
+.hide-toggle .icon.pre-text {
+ vertical-align: text-top;
+ width: 16px;
+ height: 16px;
+ margin-left: -3px;
+}
+[dir='rtl'] .hide-toggle .icon.pre-text {
+ margin-left: 0;
+ margin-right: -3px;
+}
+
+a:visited.hide-toggle,
+a.hide-toggle {
+ display: inline-block;
+ font-size: 14px;
+ font-weight: bold;
+ padding-bottom: 5px;
+}
+
+
/* Inspector
------------------------------------------------------- */
@@ -782,7 +805,6 @@ button.save.has-count .count::before {
bottom: 0;
}
-
.feature-list-pane .inspector-body {
top: 120px;
}
@@ -1071,7 +1093,7 @@ button.save.has-count .count::before {
.preset-list-item button.tag-reference-button {
height: 100%;
- border: 1px solid #CCC;
+ border: 1px solid #ccc;
border-radius: 0 3px 3px 0;
position: absolute;
top: 0;
@@ -1143,7 +1165,7 @@ button.save.has-count .count::before {
}
.preset-editor a.hide-toggle {
- margin: 0 20px 10px 20px;
+ margin: 0 20px 5px 20px;
}
.preset-editor .form-fields-container {
@@ -1218,7 +1240,7 @@ button.save.has-count .count::before {
}
[dir='rtl'] .form-label button {
border-left: none;
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
border-radius: 4px 0 0 0;
width: 31px;
}
@@ -1574,13 +1596,13 @@ input[type=number] {
float: left;
height: 100%;
width: 32px;
- border-left: 1px solid #CCC;
+ border-left: 1px solid #ccc;
border-radius: 0;
background: rgba(0, 0, 0, 0);
}
[dir='rtl'] .spin-control button{
border-left: 0;
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
}
.spin-control button.decrement {
@@ -1604,13 +1626,13 @@ input[type=number] {
}
.spin-control button.decrement::after {
- border-top: 5px solid #CCC;
+ border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.spin-control button.increment::after {
- border-bottom: 5px solid #CCC;
+ border-bottom: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
@@ -1622,7 +1644,7 @@ input[type=number] {
display: block;
background: white;
padding: 5px 10px;
- color: #7092FF;
+ color: #7092ff;
}
.checkselect label:hover {
@@ -1720,7 +1742,7 @@ input[type=number] {
right: 1px;
width: 32px;
margin-left: -32px;
- border: 1px solid #CCC;
+ border: 1px solid #ccc;
border-top-width: 0;
border-right-width: 0;
border-radius: 0 0 4px 0;
@@ -1938,12 +1960,12 @@ div.combobox {
height: 31px;
border: 0;
border-radius: 0;
- border-bottom: 1px solid #CCC;
- border-left: 1px solid #CCC;
+ border-bottom: 1px solid #ccc;
+ border-left: 1px solid #ccc;
}
[dir='rtl'] .tag-row input {
border-left: none;
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
}
.tag-row .key-wrap,
@@ -1963,14 +1985,14 @@ div.combobox {
}
.tag-row input.value {
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
}
[dir='rtl'] .tag-row input.value {
- border-left: 1px solid #CCC;
+ border-left: 1px solid #ccc;
}
.tag-row:first-child input.key {
- border-top: 1px solid #CCC;
+ border-top: 1px solid #ccc;
border-top-left-radius: 4px;
}
[dir='rtl'] .tag-row:first-child input.key {
@@ -1979,14 +2001,14 @@ div.combobox {
}
.tag-row:first-child input.value {
- border-top: 1px solid #CCC;
+ border-top: 1px solid #ccc;
}
.tag-row button {
position: absolute;
height: 31px;
right: 10%;
- border: 1px solid #CCC;
+ border: 1px solid #ccc;
border-top-width: 0;
border-left-width: 0;
}
@@ -2246,16 +2268,12 @@ div.full-screen > button:hover {
.imagery-faq {
margin-bottom: 10px;
-}
-
-.map-data-control .hide-toggle,
-.background-control .hide-toggle {
- padding-bottom: 10px;
+ white-space: nowrap;
}
.layer-list, .controls-list {
margin-bottom: 10px;
- border: 1px solid #CCC;
+ border: 1px solid #ccc;
border-radius: 4px;
}
@@ -2263,7 +2281,7 @@ div.full-screen > button:hover {
position: relative;
height: 30px;
background-color: white;
- color: #7092FF;
+ color: #7092ff;
}
.layer-list:empty {
@@ -2292,7 +2310,7 @@ div.full-screen > button:hover {
.layer-list li.active,
.layer-list li.switch {
- background: #E8EBFF;
+ background: #e8ebff;
}
.layer-list li.best > div.best {
@@ -2324,60 +2342,38 @@ div.full-screen > button:hover {
text-overflow: ellipsis;
}
-.minimap-toggle {
- display: block;
- padding: 5px 10px;
- cursor: pointer;
- color: #7092FF;
- border-radius: 3px;
+
+/* Background Display Options */
+
+.display-options-container {
+ padding: 10px;
}
-.minimap-toggle.active {
- background: #E8EBFF;
+.display-control h5 {
+ padding-bottom: 0;
+ padding-top: 10px;
}
-.minimap-toggle:hover {
- background-color: #ececec;
+.display-control h5 span {
+ margin: 5px;
}
-.hide-toggle {
- display: block;
- padding-left: 12px;
- position: relative;
-}
-[dir='rtl'] .hide-toggle {
- padding-left: 0;
- padding-right: 12px;
+.display-control .display-option-input {
+ height: 20px;
+ width: 160px;
}
-.hide-toggle:before {
- content: '';
- display: block;
- position: absolute;
- height: 0;
- width: 0;
- left: 0;
- top: 5px;
- border-top: 4px solid transparent;
- border-bottom: 4px solid transparent;
- border-left: 8px solid #7092ff;
+.display-control button {
+ height: 30px;
+ width: 30px;
+ margin-left: 5px;
+ margin-right: 0px;
+ vertical-align: text-bottom;
+ border-radius: 4px;
}
-[dir='rtl'] .hide-toggle:before {
- left: auto;
- right: 0;
- border-left: none;
- border-right: 8px solid #7092ff;
-}
-
-.hide-toggle.expanded:before {
- border-top: 8px solid #7092ff;
- border-bottom: 0;
- border-right: 4px solid transparent;
- border-left: 4px solid transparent;
-}
-[dir='rtl'] .hide-toggle.expanded:before {
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
+[dir='rtl'] .display-control button {
+ margin-left: 0px;
+ margin-right: 5px;
}
@@ -2433,7 +2429,7 @@ div.full-screen > button:hover {
}
.nudge-container input.error {
- border: 1px solid #FF7878;
+ border: 1px solid #ff7878;
border-radius: 2px;
background: #ffb;
}
@@ -2485,92 +2481,43 @@ div.full-screen > button:hover {
}
.background-control .nudge.right::after {
- border-top: 5px solid transparent;
- border-bottom: 5px solid transparent;
- border-left: 5px solid #222;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 5px solid #222;
}
.background-control .nudge.left::after {
- border-top: 5px solid transparent;
- border-bottom: 5px solid transparent;
- border-right: 5px solid #222;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-right: 5px solid #222;
}
.background-control .nudge.top::after {
- border-right: 5px solid transparent;
- border-left: 5px solid transparent;
- border-bottom: 5px solid #222;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ border-bottom: 5px solid #222;
}
.background-control .nudge.bottom::after {
- border-right: 5px solid transparent;
- border-left: 5px solid transparent;
- border-top: 5px solid #222;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ border-top: 5px solid #222;
}
-.opacity-options {
- background: url(img/background-pattern-opacity.png) 0 0 repeat;
- height: 20px;
- width: 82px;
- position: absolute;
- right: 50px;
- top: 20px;
- border: 1px solid #ccc;
-}
-[dir='rtl'] .opacity-options {
- left: 50px;
- right: auto;
-}
-
-.opacity-options li {
- height: 100%;
- display: block;
- float: left;
-}
-
-.opacity-options li .select-box{
- position: absolute;
- width: 20px;
- height: 18px;
- z-index: 9999;
-}
-
-.map-data-control li:hover .select-box,
-.map-data-control li.selected .select-box,
-.background-control li:hover .select-box,
-.background-control li.selected .select-box {
- border: 2px solid #7092ff;
- background: rgba(89, 123, 231, .5);
- opacity: .5;
-}
-
-.map-data-control li.selected:hover .select-box,
-.map-data-control li.selected .select-box,
-.background-control li.selected:hover .select-box,
-.background-control li.selected .select-box {
- opacity: 1;
-}
-
-.background-control .opacity {
- background:#222;
- display:inline-block;
- width:20px;
- height:18px;
-}
.map-data-control .layer-list button,
.background-control .layer-list button {
float: right;
height: 100%;
width: 10%;
- border-left: 1px solid #CCC;
+ border-left: 1px solid #ccc;
border-radius: 0;
}
[dir='rtl'] .map-data-control .layer-list button,
[dir='rtl'] .background-control .layer-list button {
float: left;
border-left: none;
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
}
.map-data-control .layer-list button .icon,
@@ -2603,12 +2550,12 @@ div.full-screen > button:hover {
border-radius: 0 0 0 4px;
}
[dir='rtl'] .geolocate-control button {
- border-radius: 0 0 4px 0;
+ border-radius: 0 0 4px 0;
}
.map-overlay.content {
position: fixed;
- top:60px;
+ top: 60px;
bottom: 30px;
padding: 20px 50px 20px 20px;
right: 0;
@@ -2620,13 +2567,17 @@ div.full-screen > button:hover {
right: auto !important;
}
+.map-overlay.content > div {
+ padding-bottom: 15px;
+}
+
/* Help */
.help-control button {
border-radius: 0 0 0 4px;
}
[dir='rtl'] .help-control button {
- border-radius: 0 0 4px 0;
+ border-radius: 0 0 4px 0;
}
.help-wrap p {
@@ -2655,7 +2606,7 @@ div.full-screen > button:hover {
}
.help-wrap .toc {
- width:40%;
+ width: 40%;
float:right;
margin-left: 20px;
margin-bottom: 20px;
@@ -2732,12 +2683,12 @@ div.full-screen > button:hover {
------------------------------------------------------- */
img.tile {
- position:absolute;
- transform-origin:0 0;
- -ms-transform-origin:0 0;
- -webkit-transform-origin:0 0;
- -moz-transform-origin:0 0;
- -o-transform-origin:0 0;
+ position: absolute;
+ transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ -webkit-transform-origin: 0 0;
+ -moz-transform-origin: 0 0;
+ -o-transform-origin: 0 0;
-moz-user-select: none;
-webkit-user-select: none;
@@ -2747,8 +2698,16 @@ img.tile {
opacity: 0;
-webkit-transition: opacity 200ms linear;
- transition: opacity 200ms linear;
-moz-transition: opacity 200ms linear;
+ transition: opacity 200ms linear;
+}
+
+img.tile-loaded {
+ opacity: 1;
+}
+
+img.tile-removing {
+ opacity: 0;
}
.tile-label-debug {
@@ -2763,11 +2722,11 @@ img.tile {
margin-left: -70px;
margin-top: -20px;
- transform-origin:0 0;
- -ms-transform-origin:0 0;
- -webkit-transform-origin:0 0;
- -moz-transform-origin:0 0;
- -o-transform-origin:0 0;
+ transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ -webkit-transform-origin: 0 0;
+ -moz-transform-origin: 0 0;
+ -o-transform-origin: 0 0;
-moz-user-select: none;
-webkit-user-select: none;
@@ -2779,23 +2738,15 @@ img.tile-debug {
outline: 1px solid red;
}
-img.tile-loaded {
- opacity: 1;
-}
-
-img.tile-removing {
- opacity: 0;
-}
-
/* Map
------------------------------------------------------- */
#map {
- position:relative;
- overflow:hidden;
- height:100%;
- background:#000;
+ position: relative;
+ overflow: hidden;
+ height: 100%;
+ background: #000;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
@@ -2803,11 +2754,11 @@ img.tile-removing {
}
#supersurface {
- transform-origin:0 0;
- -ms-transform-origin:0 0;
- -webkit-transform-origin:0 0;
- -moz-transform-origin:0 0;
- -o-transform-origin:0 0;
+ transform-origin: 0 0;
+ -ms-transform-origin: 0 0;
+ -webkit-transform-origin: 0 0;
+ -moz-transform-origin: 0 0;
+ -o-transform-origin: 0 0;
}
#supersurface, .layer {
@@ -3368,7 +3319,7 @@ img.tile-removing {
.modal-section {
padding: 20px;
- border-bottom: 1px solid #CCC;
+ border-bottom: 1px solid #ccc;
}
.modal-section.header h3 {
@@ -3401,8 +3352,8 @@ img.tile-removing {
.modal-actions button,
.save-success a.button {
font-weight: normal;
- color: #7092FF;
- border-bottom: 1px solid #CCC;
+ color: #7092ff;
+ border-bottom: 1px solid #ccc;
border-radius: 0;
height: 160px;
text-align: center;
@@ -3422,7 +3373,7 @@ img.tile-removing {
}
.modal-actions > :first-child {
- border-right: 1px solid #CCC;
+ border-right: 1px solid #ccc;
}
.modal-section:last-child {
@@ -3432,7 +3383,7 @@ img.tile-removing {
/* Restore Modal
------------------------------------------------------- */
.modal-actions .logo-restore {
- color: #7092FF;
+ color: #7092ff;
}
.modal-actions .logo-reset {
color: #E06C5E;
@@ -3450,7 +3401,7 @@ img.tile-removing {
padding-top: 15px;
}
.save-success .logo-osm {
- color: #7092FF;
+ color: #7092ff;
margin-bottom: 10px;
}
.save-success a.button.social {
@@ -3460,14 +3411,14 @@ img.tile-removing {
.save-success .icon.social {
height: 80px;
width: 80px;
- color: #7092FF;
+ color: #7092ff;
}
/* Splash Modal
------------------------------------------------------- */
.modal-actions .logo-walkthrough,
.modal-actions .logo-features {
- color: #7092FF;
+ color: #7092ff;
}
@@ -3658,7 +3609,7 @@ svg.mouseclick use.right {
}
.mode-save .commit-section .changeset-list button {
- border-left: 1px solid #CCC;
+ border-left: 1px solid #ccc;
}
.changeset-list li span.count:before { content: '('; }
@@ -4215,7 +4166,7 @@ li.hide + li.version .badge .tooltip .tooltip-arrow {
.curtain-tooltip .tooltip-inner .instruction {
font-weight: bold;
display: block;
- border-top: 1px solid #CCC;
+ border-top: 1px solid #ccc;
margin-top: 10px;
margin-left: -20px;
margin-right: -20px;
@@ -4293,5 +4244,5 @@ li.hide + li.version .badge .tooltip .tooltip-arrow {
.huge-modal-button .illustration {
height: 100px;
width: 100px;
- color: #7092FF;
+ color: #7092ff;
}
diff --git a/data/core.yaml b/data/core.yaml
index d087073a1..9261276af 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -375,21 +375,27 @@ en:
title: Background
description: Background settings
key: B
- percent_brightness: "{opacity}% brightness"
+ backgrounds: Backgrounds
none: None
best_imagery: Best known imagery source for this location
switch: Switch back to this background
custom: Custom
custom_button: Edit custom background
custom_prompt: "Enter a tile URL template. Valid tokens are:\n - {zoom}/{z}, {x}, {y} for Z/X/Y tile scheme\n - {ty} for flipped TMS-style Y coordinates\n - {u} for quadtile scheme\n - {switch:a,b,c} for DNS server multiplexing\n\nExample:\n{example}"
- fix_misalignment: Adjust imagery offset
- imagery_source_faq: Where does this imagery come from?
+ overlays: Overlays
+ imagery_source_faq: Imagery Info / Report a Problem
reset: reset
- offset: "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
+ display_options: Display Options
+ brightness: Brightness
+ contrast: Contrast
+ saturation: Saturation
+ sharpness: Sharpness
minimap:
- description: Minimap
+ description: Show Minimap
tooltip: Show a zoomed out map to help locate the area currently displayed.
key: '/'
+ fix_misalignment: Adjust imagery offset
+ offset: "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
map_data:
title: Map Data
description: Map Data
diff --git a/dist/locales/en.json b/dist/locales/en.json
index 092534873..e009184a1 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -464,22 +464,28 @@
"title": "Background",
"description": "Background settings",
"key": "B",
- "percent_brightness": "{opacity}% brightness",
+ "backgrounds": "Backgrounds",
"none": "None",
"best_imagery": "Best known imagery source for this location",
"switch": "Switch back to this background",
"custom": "Custom",
"custom_button": "Edit custom background",
"custom_prompt": "Enter a tile URL template. Valid tokens are:\n - {zoom}/{z}, {x}, {y} for Z/X/Y tile scheme\n - {ty} for flipped TMS-style Y coordinates\n - {u} for quadtile scheme\n - {switch:a,b,c} for DNS server multiplexing\n\nExample:\n{example}",
- "fix_misalignment": "Adjust imagery offset",
- "imagery_source_faq": "Where does this imagery come from?",
+ "overlays": "Overlays",
+ "imagery_source_faq": "Imagery Info / Report a Problem",
"reset": "reset",
- "offset": "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters.",
+ "display_options": "Display Options",
+ "brightness": "Brightness",
+ "contrast": "Contrast",
+ "saturation": "Saturation",
+ "sharpness": "Sharpness",
"minimap": {
- "description": "Minimap",
+ "description": "Show Minimap",
"tooltip": "Show a zoomed out map to help locate the area currently displayed.",
"key": "/"
- }
+ },
+ "fix_misalignment": "Adjust imagery offset",
+ "offset": "Drag anywhere in the gray area below to adjust the imagery offset, or enter the offset values in meters."
},
"map_data": {
"title": "Map Data",
diff --git a/modules/renderer/background.js b/modules/renderer/background.js
index d1d59042e..3d9f645b4 100644
--- a/modules/renderer/background.js
+++ b/modules/renderer/background.js
@@ -1,6 +1,7 @@
import _find from 'lodash-es/find';
import { dispatch as d3_dispatch } from 'd3-dispatch';
+import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';
import { select as d3_select } from 'd3-selection';
import { data } from '../../data';
@@ -12,24 +13,83 @@ import { utilRebind } from '../util/rebind';
export function rendererBackground(context) {
- var dispatch = d3_dispatch('change'),
- baseLayer = rendererTileLayer(context).projection(context.projection),
- overlayLayers = [],
- backgroundSources;
+ var dispatch = d3_dispatch('change');
+ var baseLayer = rendererTileLayer(context).projection(context.projection);
+ var _overlayLayers = [];
+ var _backgroundSources = [];
+ var _brightness = 1;
+ var _contrast = 1;
+ var _saturation = 1;
+ var _sharpness = 1;
function background(selection) {
+ var baseFilter = '';
+
+ if (_brightness !== 1) {
+ baseFilter += 'brightness(' + _brightness + ')';
+ }
+ if (_contrast !== 1) {
+ baseFilter += 'contrast(' + _contrast + ')';
+ }
+ if (_saturation !== 1) {
+ baseFilter += 'saturate(' + _saturation + ')';
+ }
+ if (_sharpness < 1) { // gaussian blur
+ var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
+ baseFilter += 'blur(' + blur + 'px)';
+ }
+
var base = selection.selectAll('.layer-background')
.data([0]);
- base.enter()
+ base = base.enter()
.insert('div', '.layer-data')
.attr('class', 'layer layer-background')
.merge(base)
+ .style('filter', baseFilter || null);
+
+
+ var imagery = base.selectAll('.layer-imagery')
+ .data([0]);
+
+ imagery.enter()
+ .append('div')
+ .attr('class', 'layer layer-imagery')
+ .merge(imagery)
.call(baseLayer);
+
+ var maskFilter = '';
+ var mixBlendMode = '';
+ if (_sharpness > 1) { // apply unsharp mask
+ mixBlendMode = 'overlay';
+ maskFilter = 'saturate(0) blur(3px) invert(1)';
+
+ var contrast = _sharpness - 1;
+ maskFilter += ' contrast(' + contrast + ')';
+
+ var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
+ maskFilter += ' brightness(' + brightness + ')';
+ }
+
+ var mask = base.selectAll('.layer-unsharp-mask')
+ .data(_sharpness > 1 ? [0] : []);
+
+ mask.exit()
+ .remove();
+
+ mask.enter()
+ .append('div')
+ .attr('class', 'layer layer-mask layer-unsharp-mask')
+ .merge(mask)
+ .call(baseLayer)
+ .style('filter', maskFilter || null)
+ .style('mix-blend-mode', mixBlendMode || null);
+
+
var overlays = selection.selectAll('.layer-overlay')
- .data(overlayLayers, function(d) { return d.source().name(); });
+ .data(_overlayLayers, function(d) { return d.source().name(); });
overlays.exit()
.remove();
@@ -46,7 +106,7 @@ export function rendererBackground(context) {
if (context.inIntro()) return;
var b = background.baseLayerSource(),
- o = overlayLayers
+ o = _overlayLayers
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
.map(function (d) { return d.source().id; })
.join(','),
@@ -85,7 +145,7 @@ export function rendererBackground(context) {
var imageryUsed = [b.imageryUsed()];
- overlayLayers
+ _overlayLayers
.filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
.forEach(function (d) { imageryUsed.push(d.source().imageryUsed()); });
@@ -117,7 +177,7 @@ export function rendererBackground(context) {
background.sources = function(extent) {
- return backgroundSources.filter(function(source) {
+ return _backgroundSources.filter(function(source) {
return source.intersects(extent);
});
};
@@ -127,7 +187,7 @@ export function rendererBackground(context) {
if (!_) return;
baseLayer.dimensions(_);
- overlayLayers.forEach(function(layer) {
+ _overlayLayers.forEach(function(layer) {
layer.dimensions(_);
});
};
@@ -172,7 +232,7 @@ export function rendererBackground(context) {
background.findSource = function(id) {
- return _find(backgroundSources, function(d) {
+ return _find(_backgroundSources, function(d) {
return d.id && d.id === id;
});
};
@@ -185,22 +245,22 @@ export function rendererBackground(context) {
background.showsLayer = function(d) {
return d.id === baseLayer.source().id ||
- overlayLayers.some(function(layer) { return d.id === layer.source().id; });
+ _overlayLayers.some(function(layer) { return d.id === layer.source().id; });
};
background.overlayLayerSources = function() {
- return overlayLayers.map(function (l) { return l.source(); });
+ return _overlayLayers.map(function (l) { return l.source(); });
};
background.toggleOverlayLayer = function(d) {
var layer;
- for (var i = 0; i < overlayLayers.length; i++) {
- layer = overlayLayers[i];
+ for (var i = 0; i < _overlayLayers.length; i++) {
+ layer = _overlayLayers[i];
if (layer.source() === d) {
- overlayLayers.splice(i, 1);
+ _overlayLayers.splice(i, 1);
dispatch.call('change');
background.updateImagery();
return;
@@ -210,9 +270,10 @@ export function rendererBackground(context) {
layer = rendererTileLayer(context)
.source(d)
.projection(context.projection)
- .dimensions(baseLayer.dimensions());
+ .dimensions(baseLayer.dimensions()
+ );
- overlayLayers.push(layer);
+ _overlayLayers.push(layer);
dispatch.call('change');
background.updateImagery();
};
@@ -235,6 +296,38 @@ export function rendererBackground(context) {
};
+ background.brightness = function(d) {
+ if (!arguments.length) return _brightness;
+ _brightness = d;
+ if (context.mode()) dispatch.call('change');
+ return background;
+ };
+
+
+ background.contrast = function(d) {
+ if (!arguments.length) return _contrast;
+ _contrast = d;
+ if (context.mode()) dispatch.call('change');
+ return background;
+ };
+
+
+ background.saturation = function(d) {
+ if (!arguments.length) return _saturation;
+ _saturation = d;
+ if (context.mode()) dispatch.call('change');
+ return background;
+ };
+
+
+ background.sharpness = function(d) {
+ if (!arguments.length) return _sharpness;
+ _sharpness = d;
+ if (context.mode()) dispatch.call('change');
+ return background;
+ };
+
+
background.init = function() {
function parseMap(qmap) {
if (!qmap) return false;
@@ -251,7 +344,7 @@ export function rendererBackground(context) {
best;
// Add all the available imagery sources
- backgroundSources = dataImagery.map(function(source) {
+ _backgroundSources = dataImagery.map(function(source) {
if (source.type === 'bing') {
return rendererBackgroundSource.Bing(source, dispatch);
} else if (source.id === 'EsriWorldImagery') {
@@ -261,15 +354,15 @@ export function rendererBackground(context) {
}
});
- first = backgroundSources.length && backgroundSources[0];
+ first = _backgroundSources.length && _backgroundSources[0];
// Add 'None'
- backgroundSources.unshift(rendererBackgroundSource.None());
+ _backgroundSources.unshift(rendererBackgroundSource.None());
// Add 'Custom'
var template = context.storage('background-custom-template') || '';
var custom = rendererBackgroundSource.Custom(template);
- backgroundSources.unshift(custom);
+ _backgroundSources.unshift(custom);
// Decide which background layer to display
@@ -290,7 +383,7 @@ export function rendererBackground(context) {
);
}
- var locator = _find(backgroundSources, function(d) {
+ var locator = _find(_backgroundSources, function(d) {
return d.overlay && d.default;
});
diff --git a/modules/ui/background.js b/modules/ui/background.js
index 9b58c79d5..d41bd9d24 100644
--- a/modules/ui/background.js
+++ b/modules/ui/background.js
@@ -7,439 +7,237 @@ import {
import {
event as d3_event,
- select as d3_select,
- selectAll as d3_selectAll
+ select as d3_select
} from 'd3-selection';
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
import { t, textDirection } from '../util/locale';
-import { geoMetersToOffset, geoOffsetToMeters } from '../geo';
-import { utilDetect } from '../util/detect';
-import { utilSetTransform, utilCallWhenIdle } from '../util';
import { svgIcon } from '../svg';
-import { uiMapInMap } from './map_in_map';
+import { uiBackgroundDisplayOptions } from './background_display_options';
+import { uiBackgroundOffset } from './background_offset';
import { uiCmd } from './cmd';
+import { uiDisclosure } from './disclosure';
+import { uiHelp } from './help';
+import { uiMapData } from './map_data';
+import { uiMapInMap } from './map_in_map';
import { uiTooltipHtml } from './tooltipHtml';
+import { utilCallWhenIdle } from '../util';
import { tooltip } from '../util/tooltip';
export function uiBackground(context) {
- var key = t('background.key'),
- detected = utilDetect(),
- opacities = [1, 0.75, 0.5, 0.25],
- directions = [
- ['right', [0.5, 0]],
- ['top', [0, -0.5]],
- ['left', [-0.5, 0]],
- ['bottom', [0, 0.5]]],
- opacityDefault = (context.storage('background-opacity') !== null) ?
- (+context.storage('background-opacity')) : 1.0,
- customSource = context.background().findSource('custom'),
- previous;
+ var key = t('background.key');
- // Can be 0 from <1.3.0 use or due to issue #1923.
- if (opacityDefault === 0) opacityDefault = 1.0;
+ var _customSource = context.background().findSource('custom');
+ var _previousBackground;
+ var _shown = false;
+
+ var _backgroundList = d3_select(null);
+ var _overlayList = d3_select(null);
+ var _displayOptionsContainer = d3_select(null);
+ var _offsetContainer = d3_select(null);
+
+ var backgroundDisplayOptions = uiBackgroundDisplayOptions(context);
+ var backgroundOffset = uiBackgroundOffset(context);
- function background(selection) {
+ 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'));
+
+ 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())
+ );
+ } else {
+ item.call(tooltip().destroy);
+ }
+ });
+ }
+
+
+ function updateLayerSelections(selection) {
+ function active(d) {
+ return context.background().showsLayer(d);
+ }
+
+ selection.selectAll('.layer')
+ .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.background().baseLayerSource(d);
+ _backgroundList.call(updateLayerSelections);
+ document.activeElement.blur();
+ }
+
+
+ function editCustom() {
+ d3_event.preventDefault();
+ var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
+ var template = window.prompt(
+ t('background.custom_prompt', { example: example }),
+ _customSource.template() || example
+ );
+
+ if (template) {
+ context.storage('background-custom-template', template);
+ _customSource.template(template);
+ chooseBackground(_customSource);
+ } else {
+ _backgroundList.call(updateLayerSelections);
+ }
+ }
+
+
+ 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())
+ .filter(filter);
+
+ var layerLinks = layerList.selectAll('li.layer')
+ .data(sources, function(d) { return d.name(); });
+
+ layerLinks.exit()
+ .remove();
+
+ var enter = layerLinks.enter()
+ .append('li')
+ .attr('class', 'layer')
+ .classed('layer-custom', function(d) { return d.id === 'custom'; })
+ .classed('best', function(d) { return d.best(); });
+
+ enter.filter(function(d) { return d.id === 'custom'; })
+ .append('button')
+ .attr('class', 'layer-browse')
+ .call(tooltip()
+ .title(t('background.custom_button'))
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ )
+ .on('click', editCustom)
+ .call(svgIcon('#icon-search'));
+
+ 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('★');
+
+ 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.layer')
+ .sort(sortSources)
+ .style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
+
+ 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 setOpacity(d) {
- var bg = context.container().selectAll('.layer-background')
- .transition()
- .style('opacity', d)
- .attr('data-opacity', d);
+ function renderBackgroundList(selection) {
- if (!detected.opera) {
- utilSetTransform(bg, 0, 0);
- }
+ // the background list
+ var container = selection.selectAll('.layer-background-list')
+ .data([0]);
- opacityList.selectAll('li')
- .classed('active', function(_) { return _ === d; });
-
- context.storage('background-opacity', d);
- }
+ _backgroundList = container.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-background-list')
+ .attr('dir', 'auto')
+ .merge(container);
- function setTooltips(selection) {
- selection.each(function(d, i, nodes) {
- var item = d3_select(this).select('label'),
- span = item.select('span'),
- placement = (i < nodes.length / 2) ? 'bottom' : 'top',
- description = d.description(),
- isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
+ // add minimap toggle below list
+ var minimapEnter = selection.selectAll('.minimap-toggle-list')
+ .data([0])
+ .enter()
+ .append('ul')
+ .attr('class', 'layer-list minimap-toggle-list')
+ .append('li')
+ .attr('class', 'layer minimap-toggle-item');
- if (d === previous) {
- 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())
- );
- } else {
- item.call(tooltip().destroy);
- }
- });
- }
-
-
- function selectLayer() {
- function active(d) {
- return context.background().showsLayer(d);
- }
-
- content.selectAll('.layer')
- .classed('active', active)
- .classed('switch', function(d) { return d === previous; })
- .call(setTooltips)
- .selectAll('input')
- .property('checked', active);
- }
-
-
- function clickSetSource(d) {
- if (d.id === 'custom' && !d.template()) {
- return editCustom();
- }
-
- d3_event.preventDefault();
- previous = context.background().baseLayerSource();
- context.background().baseLayerSource(d);
- selectLayer();
- document.activeElement.blur();
- }
-
-
- function editCustom() {
- d3_event.preventDefault();
- var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
- var template = window.prompt(
- t('background.custom_prompt', { example: example }),
- customSource.template() || example
+ var minimapLabelEnter = minimapEnter
+ .append('label')
+ .call(tooltip()
+ .html(true)
+ .title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
+ .placement('top')
);
- if (template) {
- context.storage('background-custom-template', template);
- customSource.template(template);
- clickSetSource(customSource);
- } else {
- selectLayer();
- }
- }
-
-
- function clickSetOverlay(d) {
- d3_event.preventDefault();
- context.background().toggleOverlayLayer(d);
- selectLayer();
- document.activeElement.blur();
- }
-
-
- function drawList(layerList, type, change, filter) {
- var sources = context.background()
- .sources(context.map().extent())
- .filter(filter);
-
- var layerLinks = layerList.selectAll('li.layer')
- .data(sources, function(d) { return d.name(); });
-
- layerLinks.exit()
- .remove();
-
- var enter = layerLinks.enter()
- .append('li')
- .attr('class', 'layer')
- .classed('layer-custom', function(d) { return d.id === 'custom'; })
- .classed('best', function(d) { return d.best(); });
-
- enter.filter(function(d) { return d.id === 'custom'; })
- .append('button')
- .attr('class', 'layer-browse')
- .call(tooltip()
- .title(t('background.custom_button'))
- .placement((textDirection === 'rtl') ? 'right' : 'left'))
- .on('click', editCustom)
- .call(svgIcon('#icon-search'));
-
- 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('★');
-
- 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.layer')
- .sort(sortSources)
- .style('display', layerList.selectAll('li.layer').data().length > 0 ? 'block' : 'none');
- }
-
-
- function update() {
- backgroundList.call(drawList, 'radio', clickSetSource, function(d) { return !d.isHidden() && !d.overlay; });
- overlayList.call(drawList, 'checkbox', clickSetOverlay, function(d) { return !d.isHidden() && d.overlay; });
-
- selectLayer();
- updateOffsetVal();
- }
-
-
- function updateOffsetVal() {
- var meters = geoOffsetToMeters(context.background().offset()),
- x = +meters[0].toFixed(2),
- y = +meters[1].toFixed(2);
-
- d3_selectAll('.nudge-inner-rect')
- .select('input')
- .classed('error', false)
- .property('value', x + ', ' + y);
-
- d3_selectAll('.nudge-reset')
- .classed('disabled', function() {
- return (x === 0 && y === 0);
- });
- }
-
-
- function resetOffset() {
- if (d3_event.button !== 0) return;
- context.background().offset([0, 0]);
- updateOffsetVal();
- }
-
-
- function nudge(d) {
- context.background().nudge(d, context.map().zoom());
- updateOffsetVal();
- }
-
-
- function buttonOffset(d) {
- if (d3_event.button !== 0) return;
- var timeout = window.setTimeout(function() {
- interval = window.setInterval(nudge.bind(null, d), 100);
- }, 500),
- interval;
-
- function doneNudge() {
- window.clearTimeout(timeout);
- window.clearInterval(interval);
- d3_select(window)
- .on('mouseup.buttonoffset', null, true)
- .on('mousedown.buttonoffset', null, true);
- }
-
- d3_select(window)
- .on('mouseup.buttonoffset', doneNudge, true)
- .on('mousedown.buttonoffset', doneNudge, true);
-
- nudge(d);
- }
-
-
- function inputOffset() {
- if (d3_event.button !== 0) return;
- var input = d3_select(this);
- var d = input.node().value;
-
- if (d === '') return resetOffset();
-
- d = d.replace(/;/g, ',').split(',').map(function(n) {
- // if n is NaN, it will always get mapped to false.
- return !isNaN(n) && n;
+ minimapLabelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function() {
+ d3_event.preventDefault();
+ uiMapInMap.toggle();
});
- if (d.length !== 2 || !d[0] || !d[1]) {
- input.classed('error', true);
- return;
- }
-
- context.background().offset(geoMetersToOffset(d));
- updateOffsetVal();
- }
+ minimapLabelEnter
+ .append('span')
+ .text(t('background.minimap.description'));
- function dragOffset() {
- if (d3_event.button !== 0) return;
- var origin = [d3_event.clientX, d3_event.clientY];
-
- context.container()
- .append('div')
- .attr('class', 'nudge-surface');
-
- d3_select(window)
- .on('mousemove.offset', function() {
- var latest = [d3_event.clientX, d3_event.clientY];
- var d = [
- -(origin[0] - latest[0]) / 4,
- -(origin[1] - latest[1]) / 4
- ];
-
- origin = latest;
- nudge(d);
- })
- .on('mouseup.offset', function() {
- if (d3_event.button !== 0) return;
- d3_selectAll('.nudge-surface')
- .remove();
-
- d3_select(window)
- .on('mousemove.offset', null)
- .on('mouseup.offset', null);
- });
-
- d3_event.preventDefault();
- }
-
-
- function hide() {
- setVisible(false);
- }
-
-
- function toggle() {
- if (d3_event) {
- d3_event.preventDefault();
- }
- tooltipBehavior.hide(button);
- setVisible(!button.classed('active'));
- }
-
-
- function quickSwitch() {
- if (d3_event) {
- d3_event.stopImmediatePropagation();
- d3_event.preventDefault();
- }
- if (previous) {
- clickSetSource(previous);
- }
- }
-
-
- function setVisible(show) {
- if (show !== shown) {
- button.classed('active', show);
- shown = show;
-
- if (show) {
- selection
- .on('mousedown.background-inside', function() {
- d3_event.stopPropagation();
- });
-
- content
- .style('display', 'block')
- .style('right', '-300px')
- .transition()
- .duration(200)
- .style('right', '0px');
-
- content.selectAll('.layer')
- .call(setTooltips);
-
- } else {
- content
- .style('display', 'block')
- .style('right', '0px')
- .transition()
- .duration(200)
- .style('right', '-300px')
- .on('end', function() {
- d3_select(this).style('display', 'none');
- });
-
- selection
- .on('mousedown.background-inside', null);
- }
- }
- }
-
-
- var content = selection
- .append('div')
- .attr('class', 'fillL map-overlay col3 content hide'),
- tooltipBehavior = tooltip()
- .placement((textDirection === 'rtl') ? 'right' : 'left')
- .html(true)
- .title(uiTooltipHtml(t('background.description'), key)),
- button = selection
- .append('button')
- .attr('tabindex', -1)
- .on('click', toggle)
- .call(svgIcon('#icon-layers', 'light'))
- .call(tooltipBehavior),
- shown = false;
-
-
- /* opacity switcher */
-
- var opawrap = content
- .append('div')
- .attr('class', 'opacity-options-wrapper');
-
- opawrap
- .append('h4')
- .text(t('background.title'));
-
- var opacityList = opawrap
- .append('ul')
- .attr('class', 'opacity-options');
-
- opacityList.selectAll('div.opacity')
- .data(opacities)
+ // "Info / Report a Problem" link
+ selection.selectAll('.imagery-faq')
+ .data([0])
.enter()
- .append('li')
- .attr('data-original-title', function(d) {
- return t('background.percent_brightness', { opacity: (d * 100) });
- })
- .on('click.set-opacity', setOpacity)
- .html('')
- .call(tooltip()
- .placement((textDirection === 'rtl') ? 'right' : 'left'))
- .append('div')
- .attr('class', 'opacity')
- .style('opacity', function(d) { return 1.25 - d; });
-
-
- /* background list */
-
- var backgroundList = content
- .append('ul')
- .attr('class', 'layer-list')
- .attr('dir', 'auto');
-
- content
.append('div')
.attr('class', 'imagery-faq')
.append('a')
@@ -449,108 +247,141 @@ export function uiBackground(context) {
.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_source_faq'));
+ }
- /* overlay list */
+ function renderOverlayList(selection) {
+ var container = selection.selectAll('.layer-overlay-list')
+ .data([0]);
- var overlayList = content
+ _overlayList = container.enter()
.append('ul')
- .attr('class', 'layer-list');
+ .attr('class', 'layer-list layer-overlay-list')
+ .attr('dir', 'auto')
+ .merge(container);
+ }
- var controls = content
+
+ function update() {
+ _backgroundList
+ .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
+
+ _overlayList
+ .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
+
+ _displayOptionsContainer
+ .call(backgroundDisplayOptions);
+
+ _offsetContainer
+ .call(backgroundOffset);
+ }
+
+
+ function quickSwitch() {
+ if (d3_event) {
+ d3_event.stopImmediatePropagation();
+ d3_event.preventDefault();
+ }
+ if (_previousBackground) {
+ chooseBackground(_previousBackground);
+ }
+ }
+
+
+ function background(selection) {
+
+ function hidePane() {
+ setVisible(false);
+ }
+
+ function togglePane() {
+ if (d3_event) d3_event.preventDefault();
+ paneTooltip.hide(button);
+ setVisible(!button.classed('active'));
+ }
+
+ function setVisible(show) {
+ if (show !== _shown) {
+ button.classed('active', show);
+ _shown = show;
+
+ if (show) {
+ uiMapData.hidePane();
+ uiHelp.hidePane();
+ update();
+
+ pane
+ .style('display', 'block')
+ .style('right', '-300px')
+ .transition()
+ .duration(200)
+ .style('right', '0px');
+
+ } else {
+ pane
+ .style('display', 'block')
+ .style('right', '0px')
+ .transition()
+ .duration(200)
+ .style('right', '-300px')
+ .on('end', function() {
+ d3_select(this).style('display', 'none');
+ });
+ }
+ }
+ }
+
+
+ var pane = selection
.append('div')
- .attr('class', 'controls-list');
+ .attr('class', 'fillL map-overlay col3 content hide');
+ var paneTooltip = tooltip()
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ .html(true)
+ .title(uiTooltipHtml(t('background.description'), key));
- /* minimap toggle */
+ var button = selection
+ .append('button')
+ .attr('tabindex', -1)
+ .on('click', togglePane)
+ .call(svgIcon('#icon-layers', 'light'))
+ .call(paneTooltip);
- var minimapLabel = controls
- .append('label')
- .call(tooltip()
- .html(true)
- .title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
- .placement('top')
+ pane
+ .append('h2')
+ .text(t('background.title'));
+
+ // background list
+ pane
+ .append('div')
+ .attr('class', 'background-background-list-container')
+ .call(uiDisclosure(context, 'background_list', true)
+ .title(t('background.backgrounds'))
+ .content(renderBackgroundList)
);
- minimapLabel
- .classed('minimap-toggle', true)
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function() {
- uiMapInMap.toggle();
- d3_event.preventDefault();
- });
-
- minimapLabel
- .append('span')
- .text(t('background.minimap.description'));
-
-
- /* imagery offset controls */
-
- var adjustments = content
+ // overlay list
+ pane
.append('div')
- .attr('class', 'adjustments');
-
- adjustments
- .append('a')
- .text(t('background.fix_misalignment'))
- .attr('href', '#')
- .classed('hide-toggle', true)
- .classed('expanded', false)
- .on('click', function() {
- if (d3_event.button !== 0) return;
- var exp = d3_select(this).classed('expanded');
- nudgeContainer.style('display', exp ? 'none' : 'block');
- d3_select(this).classed('expanded', !exp);
- d3_event.preventDefault();
- });
-
- var nudgeContainer = adjustments
- .append('div')
- .attr('class', 'nudge-container cf')
- .style('display', 'none');
-
- nudgeContainer
- .append('div')
- .attr('class', 'nudge-instructions')
- .text(t('background.offset'));
-
- var nudgeRect = nudgeContainer
- .append('div')
- .attr('class', 'nudge-outer-rect')
- .on('mousedown', dragOffset);
-
- nudgeRect
- .append('div')
- .attr('class', 'nudge-inner-rect')
- .append('input')
- .on('change', inputOffset)
- .on('mousedown', function() {
- if (d3_event.button !== 0) return;
- d3_event.stopPropagation();
- });
-
- nudgeContainer
- .append('div')
- .selectAll('button')
- .data(directions).enter()
- .append('button')
- .attr('class', function(d) { return d[0] + ' nudge'; })
- .on('mousedown', function(d) {
- if (d3_event.button !== 0) return;
- buttonOffset(d[1]);
- });
-
- nudgeContainer
- .append('button')
- .attr('title', t('background.reset'))
- .attr('class', 'nudge-reset disabled')
- .on('click', resetOffset)
- .call(
- (textDirection === 'rtl') ? svgIcon('#icon-redo') : svgIcon('#icon-undo')
+ .attr('class', 'background-overlay-list-container')
+ .call(uiDisclosure(context, 'overlay_list', true)
+ .title(t('background.overlays'))
+ .content(renderOverlayList)
);
+ // display options
+ _displayOptionsContainer = pane
+ .append('div')
+ .attr('class', 'background-display-options');
+
+ // offset controls
+ _offsetContainer = pane
+ .append('div')
+ .attr('class', 'background-offset');
+
+
+ // add listeners
context.map()
.on('move.background-update', _debounce(utilCallWhenIdle(update), 1000));
@@ -559,15 +390,18 @@ export function uiBackground(context) {
update();
- setOpacity(opacityDefault);
var keybinding = d3_keybinding('background')
- .on(key, toggle)
+ .on(key, togglePane)
.on(uiCmd('⌘' + key), quickSwitch)
- .on([t('map_data.key'), t('help.key')], hide);
+ .on([t('map_data.key'), t('help.key')], hidePane);
d3_select(document)
.call(keybinding);
+
+ uiBackground.hidePane = hidePane;
+ uiBackground.togglePane = togglePane;
+ uiBackground.setVisible = setVisible;
}
return background;
diff --git a/modules/ui/background_display_options.js b/modules/ui/background_display_options.js
new file mode 100644
index 000000000..9dfbfc80c
--- /dev/null
+++ b/modules/ui/background_display_options.js
@@ -0,0 +1,125 @@
+import {
+ event as d3_event,
+ select as d3_select
+} from 'd3-selection';
+
+
+import { t, textDirection } from '../util/locale';
+import { svgIcon } from '../svg';
+import { uiDisclosure } from './disclosure';
+
+
+export function uiBackgroundDisplayOptions(context) {
+ var _selection = d3_select(null);
+ var sliders = ['brightness', 'contrast', 'saturation', 'sharpness'];
+ var storedOpacity = context.storage('background-opacity');
+
+ var _options = {
+ brightness: (storedOpacity !== null ? (+storedOpacity) : 1),
+ contrast: 1,
+ saturation: 1,
+ sharpness: 1
+ };
+
+
+ function clamp(x, min, max) {
+ return Math.max(min, Math.min(x, max));
+ }
+
+
+ function updateValue(d, val) {
+ if (!val && d3_event && d3_event.target) {
+ val = d3_event.target.value;
+ }
+
+ val = clamp(val, 0.25, 2);
+
+ _options[d] = val;
+ context.background()[d](val);
+
+ if (d === 'brightness') {
+ context.storage('background-opacity', val);
+ }
+
+ _selection
+ .call(render);
+ }
+
+
+ function render(selection) {
+ var container = selection.selectAll('.display-options-container')
+ .data([0]);
+
+ var containerEnter = container.enter()
+ .append('div')
+ .attr('class', 'display-options-container controls-list');
+
+ // add slider controls
+ var slidersEnter = containerEnter.selectAll('.display-control')
+ .data(sliders)
+ .enter()
+ .append('div')
+ .attr('class', function(d) { return 'display-control display-control-' + d; });
+
+ slidersEnter
+ .append('h5')
+ .text(function(d) { return t('background.' + d); })
+ .append('span')
+ .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
+
+ slidersEnter
+ .append('input')
+ .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
+ .attr('type', 'range')
+ .attr('min', '0.25')
+ .attr('max', '2')
+ .attr('step', '0.05')
+ .on('input', function(d) {
+ var val = d3_select(this).property('value');
+ updateValue(d, val);
+ });
+
+ slidersEnter
+ .append('button')
+ .attr('title', t('background.reset'))
+ .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
+ .on('click', function(d) {
+ if (d3_event.button !== 0) return;
+ updateValue(d, 1);
+ })
+ .call(svgIcon('#icon-' + (textDirection === 'rtl' ? 'redo' : 'undo')));
+
+
+ // update
+ container = containerEnter
+ .merge(container);
+
+ container.selectAll('.display-option-input')
+ .property('value', function(d) { return _options[d]; });
+
+ container.selectAll('.display-option-value')
+ .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
+
+ container.selectAll('.display-option-reset')
+ .classed('disabled', function(d) { return _options[d] === 1; });
+
+ // first time only, set brightness if needed
+ if (containerEnter.size() && _options.brightness !== 1) {
+ context.background().brightness(_options.brightness);
+ }
+ }
+
+
+ function backgroundDisplayOptions(selection) {
+ _selection = selection;
+
+ selection
+ .call(uiDisclosure(context, 'background_display_options', true)
+ .title(t('background.display_options'))
+ .content(render)
+ );
+ }
+
+
+ return backgroundDisplayOptions;
+}
diff --git a/modules/ui/background_offset.js b/modules/ui/background_offset.js
new file mode 100644
index 000000000..295557de2
--- /dev/null
+++ b/modules/ui/background_offset.js
@@ -0,0 +1,197 @@
+import {
+ event as d3_event,
+ select as d3_select,
+ selectAll as d3_selectAll
+} from 'd3-selection';
+
+import { t, textDirection } from '../util/locale';
+import { geoMetersToOffset, geoOffsetToMeters } from '../geo';
+import { svgIcon } from '../svg';
+import { uiDisclosure } from './disclosure';
+
+
+export function uiBackgroundOffset(context) {
+ var directions = [
+ ['right', [0.5, 0]],
+ ['top', [0, -0.5]],
+ ['left', [-0.5, 0]],
+ ['bottom', [0, 0.5]]
+ ];
+
+
+ function d3_eventCancel() {
+ d3_event.stopPropagation();
+ d3_event.preventDefault();
+ }
+
+
+ function updateValue() {
+ var meters = geoOffsetToMeters(context.background().offset());
+ var x = +meters[0].toFixed(2);
+ var y = +meters[1].toFixed(2);
+
+ d3_selectAll('.nudge-inner-rect')
+ .select('input')
+ .classed('error', false)
+ .property('value', x + ', ' + y);
+
+ d3_selectAll('.nudge-reset')
+ .classed('disabled', function() {
+ return (x === 0 && y === 0);
+ });
+ }
+
+
+ function resetOffset() {
+ context.background().offset([0, 0]);
+ updateValue();
+ }
+
+
+ function nudge(d) {
+ context.background().nudge(d, context.map().zoom());
+ updateValue();
+ }
+
+
+ function clickNudgeButton(d) {
+ var interval;
+ var timeout = window.setTimeout(function() {
+ interval = window.setInterval(nudge.bind(null, d), 100);
+ }, 500);
+
+ function doneNudge() {
+ window.clearTimeout(timeout);
+ window.clearInterval(interval);
+ d3_select(window)
+ .on('mouseup.buttonoffset', null, true)
+ .on('mousedown.buttonoffset', null, true);
+ }
+
+ d3_select(window)
+ .on('mouseup.buttonoffset', doneNudge, true)
+ .on('mousedown.buttonoffset', doneNudge, true);
+
+ nudge(d);
+ }
+
+
+ function inputOffset() {
+ var input = d3_select(this);
+ var d = input.node().value;
+
+ if (d === '') return resetOffset();
+
+ d = d.replace(/;/g, ',').split(',').map(function(n) {
+ // if n is NaN, it will always get mapped to false.
+ return !isNaN(n) && n;
+ });
+
+ if (d.length !== 2 || !d[0] || !d[1]) {
+ input.classed('error', true);
+ return;
+ }
+
+ context.background().offset(geoMetersToOffset(d));
+ updateValue();
+ }
+
+
+ function dragOffset() {
+ d3_event.preventDefault();
+ if (d3_event.button !== 0) return;
+
+ var origin = [d3_event.clientX, d3_event.clientY];
+
+ context.container()
+ .append('div')
+ .attr('class', 'nudge-surface');
+
+ d3_select(window)
+ .on('mousemove.offset', function() {
+ var latest = [d3_event.clientX, d3_event.clientY];
+ var d = [
+ -(origin[0] - latest[0]) / 4,
+ -(origin[1] - latest[1]) / 4
+ ];
+
+ origin = latest;
+ nudge(d);
+ })
+ .on('mouseup.offset', function() {
+ if (d3_event.button !== 0) return;
+ d3_selectAll('.nudge-surface')
+ .remove();
+
+ d3_select(window)
+ .on('mousemove.offset', null)
+ .on('mouseup.offset', null);
+ });
+ }
+
+
+ function render(selection) {
+ var container = selection.selectAll('.nudge-container')
+ .data([0]);
+
+ var containerEnter = container.enter()
+ .append('div')
+ .attr('class', 'nudge-container cf');
+
+ containerEnter
+ .append('div')
+ .attr('class', 'nudge-instructions')
+ .text(t('background.offset'));
+
+ var nudgeEnter = containerEnter
+ .append('div')
+ .attr('class', 'nudge-outer-rect')
+ .on('mousedown', dragOffset);
+
+ nudgeEnter
+ .append('div')
+ .attr('class', 'nudge-inner-rect')
+ .append('input')
+ .on('change', inputOffset);
+
+ containerEnter
+ .append('div')
+ .selectAll('button')
+ .data(directions).enter()
+ .append('button')
+ .attr('class', function(d) { return d[0] + ' nudge'; })
+ .on('contextmenu', d3_eventCancel)
+ .on('mousedown', function(d) {
+ if (d3_event.button !== 0) return;
+ clickNudgeButton(d[1]);
+ });
+
+ containerEnter
+ .append('button')
+ .attr('title', t('background.reset'))
+ .attr('class', 'nudge-reset disabled')
+ .on('contextmenu', d3_eventCancel)
+ .on('click', function() {
+ if (d3_event.button !== 0) return;
+ resetOffset();
+ })
+ .call(svgIcon('#icon-' + (textDirection === 'rtl' ? 'redo' : 'undo')));
+
+ updateValue();
+ }
+
+
+ function backgroundOffset(selection) {
+ selection
+ .call(uiDisclosure(context, 'background_offset', false)
+ .title(t('background.fix_misalignment'))
+ .content(render)
+ );
+ }
+
+
+ context.background()
+ .on('change.backgroundOffset-update', updateValue);
+
+ return backgroundOffset;
+}
diff --git a/modules/ui/commit.js b/modules/ui/commit.js
index 52696ca30..e7d3837f5 100644
--- a/modules/ui/commit.js
+++ b/modules/ui/commit.js
@@ -269,7 +269,6 @@ export function uiCommit(context) {
updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
var expanded = !tagSection.selectAll('a.hide-toggle.expanded').empty();
-
tagSection
.call(rawTagEditor
.expanded(expanded)
diff --git a/modules/ui/disclosure.js b/modules/ui/disclosure.js
index 82ee9f0b4..6c56e37c6 100644
--- a/modules/ui/disclosure.js
+++ b/modules/ui/disclosure.js
@@ -1,70 +1,115 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
+import { event as d3_event } from 'd3-selection';
+import { svgIcon } from '../svg';
import { utilRebind } from '../util/rebind';
import { uiToggle } from './toggle';
+import { textDirection } from '../util/locale';
-export function uiDisclosure() {
+export function uiDisclosure(context, key, expandedDefault) {
var dispatch = d3_dispatch('toggled'),
- title,
- expanded = false,
- content = function () {};
+ _preference = (context.storage('disclosure.' + key + '.expanded')),
+ _expanded = (_preference === null ? !!expandedDefault : (_preference === 'true')),
+ _title,
+ _updatePreference = true,
+ _content = function () {};
var disclosure = function(selection) {
- var hideToggle = selection.selectAll('.hide-toggle')
+ var hideToggle = selection.selectAll('.hide-toggle-' + key)
.data([0]);
- hideToggle = hideToggle.enter()
+ // enter
+ var hideToggleEnter = hideToggle.enter()
.append('a')
.attr('href', '#')
- .attr('class', 'hide-toggle')
+ .attr('class', 'hide-toggle hide-toggle-' + key)
+ .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
+
+ hideToggleEnter
+ .append('span')
+ .attr('class', 'hide-toggle-text');
+
+ // update
+ hideToggle = hideToggleEnter
.merge(hideToggle);
hideToggle
- .text(title)
.on('click', toggle)
- .classed('expanded', expanded);
+ .classed('expanded', _expanded);
+
+ hideToggle.selectAll('.hide-toggle-text')
+ .text(_title);
+
+ hideToggle.selectAll('.hide-toggle-icon')
+ .attr('xlink:href', _expanded ? '#icon-down'
+ : (textDirection === 'rtl') ? '#icon-backward' : '#icon-forward'
+ );
- var wrap = selection.selectAll('div')
+ var wrap = selection.selectAll('.disclosure-wrap')
.data([0]);
wrap = wrap.enter()
.append('div')
+ .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
.merge(wrap);
wrap
- .classed('hide', !expanded)
- .call(content);
+ .classed('hide', !_expanded)
+ .call(_content);
function toggle() {
- expanded = !expanded;
- hideToggle.classed('expanded', expanded);
- wrap.call(uiToggle(expanded));
- dispatch.call('toggled', this, expanded);
+ d3_event.preventDefault();
+
+ _expanded = !_expanded;
+
+ if (_updatePreference) {
+ context.storage('disclosure.' + key + '.expanded', _expanded);
+ }
+
+ hideToggle
+ .classed('expanded', _expanded);
+
+ hideToggle.selectAll('.hide-toggle-icon')
+ .attr('xlink:href', _expanded ? '#icon-down'
+ : (textDirection === 'rtl') ? '#icon-backward' : '#icon-forward'
+ );
+
+ wrap
+ .call(uiToggle(_expanded));
+
+ dispatch.call('toggled', this, _expanded);
}
};
disclosure.title = function(_) {
- if (!arguments.length) return title;
- title = _;
+ if (!arguments.length) return _title;
+ _title = _;
return disclosure;
};
disclosure.expanded = function(_) {
- if (!arguments.length) return expanded;
- expanded = _;
+ if (!arguments.length) return _expanded;
+ _expanded = _;
+ return disclosure;
+ };
+
+
+ disclosure.updatePreference = function(_) {
+ if (!arguments.length) return _updatePreference;
+ _updatePreference = _;
return disclosure;
};
disclosure.content = function(_) {
- if (!arguments.length) return content;
- content = _;
+ if (!arguments.length) return _content;
+ _content = _;
return disclosure;
};
diff --git a/modules/ui/help.js b/modules/ui/help.js
index 6b1e6b435..01196b3a7 100644
--- a/modules/ui/help.js
+++ b/modules/ui/help.js
@@ -9,7 +9,9 @@ import marked from 'marked';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { uiCmd } from './cmd';
+import { uiBackground } from './background';
import { uiIntro } from './intro';
+import { uiMapData } from './map_data';
import { uiShortcuts } from './shortcuts';
import { uiTooltipHtml } from './tooltipHtml';
import { tooltip } from '../util/tooltip';
@@ -260,12 +262,12 @@ export function uiHelp(context) {
function help(selection) {
- function hide() {
+ function hidePane() {
setVisible(false);
}
- function toggle() {
+ function togglePane() {
if (d3_event) d3_event.preventDefault();
tooltipBehavior.hide(button);
setVisible(!button.classed('active'));
@@ -278,14 +280,15 @@ export function uiHelp(context) {
shown = show;
if (show) {
- selection.on('mousedown.help-inside', function() {
- return d3_event.stopPropagation();
- });
+ uiBackground.hidePane();
+ uiMapData.hidePane();
+
pane.style('display', 'block')
.style('right', '-500px')
.transition()
.duration(200)
.style('right', '0px');
+
} else {
pane.style('right', '0px')
.transition()
@@ -294,7 +297,6 @@ export function uiHelp(context) {
.on('end', function() {
d3_select(this).style('display', 'none');
});
- selection.on('mousedown.help-inside', null);
}
}
}
@@ -375,13 +377,14 @@ export function uiHelp(context) {
.title(uiTooltipHtml(t('help.title'), key)),
button = selection.append('button')
.attr('tabindex', -1)
- .on('click', toggle)
+ .on('click', togglePane)
.call(svgIcon('#icon-help', 'light'))
.call(tooltipBehavior),
shown = false;
- var toc = pane.append('ul')
+ var toc = pane
+ .append('ul')
.attr('class', 'toc');
var menuItems = toc.selectAll('li')
@@ -424,26 +427,34 @@ export function uiHelp(context) {
.text(t('splash.walkthrough'));
- var content = pane.append('div')
+ var content = pane
+ .append('div')
.attr('class', 'left-content');
- var doctitle = content.append('h2')
+ var doctitle = content
+ .append('h2')
.text(t('help.title'));
- var body = content.append('div')
+ var body = content
+ .append('div')
.attr('class', 'body');
- var nav = content.append('div')
+ var nav = content
+ .append('div')
.attr('class', 'nav');
clickHelp(docs[0], 0);
var keybinding = d3_keybinding('help')
- .on(key, toggle)
- .on([t('background.key'), t('map_data.key')], hide);
+ .on(key, togglePane)
+ .on([t('background.key'), t('map_data.key')], hidePane);
d3_select(document)
.call(keybinding);
+
+ uiHelp.hidePane = hidePane;
+ uiHelp.togglePane = togglePane;
+ uiHelp.setVisible = setVisible;
}
return help;
diff --git a/modules/ui/index.js b/modules/ui/index.js
index 742aa7b1c..8a3dd80c6 100644
--- a/modules/ui/index.js
+++ b/modules/ui/index.js
@@ -2,6 +2,8 @@ export { uiInit } from './init';
export { uiAccount } from './account';
export { uiAttribution } from './attribution';
export { uiBackground } from './background';
+export { uiBackgroundDisplayOptions } from './background_display_options';
+export { uiBackgroundOffset } from './background_offset';
export { uiChangesetEditor } from './changeset_editor';
export { uiCmd } from './cmd';
export { uiCommit } from './commit';
diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js
index fb522c098..aef91c01b 100644
--- a/modules/ui/map_data.js
+++ b/modules/ui/map_data.js
@@ -5,380 +5,423 @@ import {
import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
-import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
-import { uiTooltipHtml } from './tooltipHtml';
+import { t, textDirection } from '../util/locale';
import { tooltip } from '../util/tooltip';
+import { uiBackground } from './background';
+import { uiDisclosure } from './disclosure';
+import { uiHelp } from './help';
+import { uiTooltipHtml } from './tooltipHtml';
export function uiMapData(context) {
- var key = t('map_data.key'),
- features = context.features().keys(),
- layers = context.layers(),
- fills = ['wireframe', 'partial', 'full'],
- fillDefault = context.storage('area-fill') || 'partial',
- fillSelected = fillDefault;
+ var key = t('map_data.key');
+ var features = context.features().keys();
+ var layers = context.layers();
+ var fills = ['wireframe', 'partial', 'full'];
+
+ var _fillDefault = context.storage('area-fill') || 'partial';
+ var _fillSelected = _fillDefault;
+ var _shown = false;
+ var _dataLayerContainer = d3_select(null);
+ var _fillList = d3_select(null);
+ var _featureList = d3_select(null);
- function map_data(selection) {
+ function showsFeature(d) {
+ return context.features().enabled(d);
+ }
- function showsFeature(d) {
- return context.features().enabled(d);
+
+ function autoHiddenFeature(d) {
+ return context.features().autoHidden(d);
+ }
+
+
+ function clickFeature(d) {
+ context.features().toggle(d);
+ update();
+ }
+
+
+ function showsFill(d) {
+ return _fillSelected === d;
+ }
+
+
+ function setFill(d) {
+ fills.forEach(function(opt) {
+ context.surface().classed('fill-' + opt, Boolean(opt === d));
+ });
+
+ _fillSelected = d;
+ if (d !== 'wireframe') {
+ _fillDefault = d;
+ context.storage('area-fill', d);
}
+ update();
+ }
- function autoHiddenFeature(d) {
- return context.features().autoHidden(d);
+ function showsLayer(which) {
+ var layer = layers.layer(which);
+ if (layer) {
+ return layer.enabled();
}
+ return false;
+ }
- function clickFeature(d) {
- context.features().toggle(d);
+ function setLayer(which, enabled) {
+ var layer = layers.layer(which);
+ if (layer) {
+ layer.enabled(enabled);
update();
}
+ }
- function showsFill(d) {
- return fillSelected === d;
+ function toggleLayer(which) {
+ setLayer(which, !showsLayer(which));
+ }
+
+
+ function drawPhotoItems(selection) {
+ var photoKeys = ['mapillary-images', 'mapillary-signs', 'openstreetcam-images'];
+ var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
+ var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
+
+ function layerSupported(d) {
+ return d.layer && d.layer.supported();
+ }
+ function layerEnabled(d) {
+ return layerSupported(d) && d.layer.enabled();
}
+ var ul = selection
+ .selectAll('.layer-list-photos')
+ .data([0]);
- function setFill(d) {
- fills.forEach(function(opt) {
- context.surface().classed('fill-' + opt, Boolean(opt === d));
+ ul = ul.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-list-photos')
+ .merge(ul);
+
+ var li = ul.selectAll('.list-item-photos')
+ .data(data);
+
+ li.exit()
+ .remove();
+
+ var liEnter = li.enter()
+ .append('li')
+ .attr('class', function(d) { return 'list-item-photos list-item-' + d.id; });
+
+ var labelEnter = liEnter
+ .append('label')
+ .each(function(d) {
+ d3_select(this)
+ .call(tooltip()
+ .title(t(d.id.replace('-', '_') + '.tooltip'))
+ .placement('top')
+ );
});
- fillSelected = d;
- if (d !== 'wireframe') {
- fillDefault = d;
- context.storage('area-fill', d);
- }
- update();
- }
+ labelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function(d) { toggleLayer(d.id); });
+
+ labelEnter
+ .append('span')
+ .text(function(d) { return t(d.id.replace('-', '_') + '.title'); });
- function showsLayer(which) {
- var layer = layers.layer(which);
- if (layer) {
- return layer.enabled();
- }
- return false;
- }
+ // Update
+ li = li
+ .merge(liEnter);
+
+ li
+ .classed('active', layerEnabled)
+ .selectAll('input')
+ .property('checked', layerEnabled);
+ }
- function setLayer(which, enabled) {
- var layer = layers.layer(which);
- if (layer) {
- layer.enabled(enabled);
- update();
- }
- }
+ function drawOsmItem(selection) {
+ var osm = layers.layer('osm'),
+ showsOsm = osm.enabled();
+
+ var ul = selection
+ .selectAll('.layer-list-osm')
+ .data(osm ? [0] : []);
+
+ // Exit
+ ul.exit()
+ .remove();
+
+ // Enter
+ var ulEnter = ul.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-list-osm');
+
+ var liEnter = ulEnter
+ .append('li')
+ .attr('class', 'list-item-osm');
+
+ var labelEnter = liEnter
+ .append('label')
+ .call(tooltip()
+ .title(t('map_data.layers.osm.tooltip'))
+ .placement('top')
+ );
+
+ labelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function() { toggleLayer('osm'); });
+
+ labelEnter
+ .append('span')
+ .text(t('map_data.layers.osm.title'));
+
+ // Update
+ ul = ul
+ .merge(ulEnter);
+
+ ul.selectAll('.list-item-osm')
+ .classed('active', showsOsm)
+ .selectAll('input')
+ .property('checked', showsOsm);
+ }
- function toggleLayer(which) {
- setLayer(which, !showsLayer(which));
- }
+ function drawGpxItem(selection) {
+ var gpx = layers.layer('gpx'),
+ hasGpx = gpx && gpx.hasGpx(),
+ showsGpx = hasGpx && gpx.enabled();
+ var ul = selection
+ .selectAll('.layer-list-gpx')
+ .data(gpx ? [0] : []);
- function drawPhotoItems(selection) {
- var photoKeys = ['mapillary-images', 'mapillary-signs', 'openstreetcam-images'];
- var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
- var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
+ // Exit
+ ul.exit()
+ .remove();
- function layerSupported(d) {
- return d.layer && d.layer.supported();
- }
- function layerEnabled(d) {
- return layerSupported(d) && d.layer.enabled();
- }
+ // Enter
+ var ulEnter = ul.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-list-gpx');
- var ul = selection
- .selectAll('.layer-list-photos')
- .data([0]);
+ var liEnter = ulEnter
+ .append('li')
+ .attr('class', 'list-item-gpx');
- ul = ul.enter()
- .append('ul')
- .attr('class', 'layer-list layer-list-photos')
- .merge(ul);
+ liEnter
+ .append('button')
+ .attr('class', 'list-item-gpx-extent')
+ .call(tooltip()
+ .title(t('gpx.zoom'))
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ )
+ .on('click', function() {
+ d3_event.preventDefault();
+ d3_event.stopPropagation();
+ gpx.fitZoom();
+ })
+ .call(svgIcon('#icon-search'));
- var li = ul.selectAll('.list-item-photos')
- .data(data);
-
- li.exit()
- .remove();
-
- var liEnter = li.enter()
- .append('li')
- .attr('class', function(d) { return 'list-item-photos list-item-' + d.id; });
-
- var labelEnter = liEnter
- .append('label')
- .each(function(d) {
- d3_select(this)
- .call(tooltip()
- .title(t(d.id.replace('-', '_') + '.tooltip'))
- .placement('top')
- );
- });
-
- labelEnter
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function(d) { toggleLayer(d.id); });
-
- labelEnter
- .append('span')
- .text(function(d) { return t(d.id.replace('-', '_') + '.title'); });
-
-
- // Update
- li = li
- .merge(liEnter);
-
- li
- .classed('active', layerEnabled)
- .selectAll('input')
- .property('checked', layerEnabled);
- }
-
-
- function drawOsmItem(selection) {
- var osm = layers.layer('osm'),
- showsOsm = osm.enabled();
-
- var ul = selection
- .selectAll('.layer-list-osm')
- .data(osm ? [0] : []);
-
- // Exit
- ul.exit()
- .remove();
-
- // Enter
- var ulEnter = ul.enter()
- .append('ul')
- .attr('class', 'layer-list layer-list-osm');
-
- var liEnter = ulEnter
- .append('li')
- .attr('class', 'list-item-osm');
-
- var labelEnter = liEnter
- .append('label')
- .call(tooltip()
- .title(t('map_data.layers.osm.tooltip'))
- .placement('top')
- );
-
- labelEnter
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function() { toggleLayer('osm'); });
-
- labelEnter
- .append('span')
- .text(t('map_data.layers.osm.title'));
-
- // Update
- ul = ul
- .merge(ulEnter);
-
- ul.selectAll('.list-item-osm')
- .classed('active', showsOsm)
- .selectAll('input')
- .property('checked', showsOsm);
- }
-
-
- function drawGpxItem(selection) {
- var gpx = layers.layer('gpx'),
- hasGpx = gpx && gpx.hasGpx(),
- showsGpx = hasGpx && gpx.enabled();
-
- var ul = selection
- .selectAll('.layer-list-gpx')
- .data(gpx ? [0] : []);
-
- // Exit
- ul.exit()
- .remove();
-
- // Enter
- var ulEnter = ul.enter()
- .append('ul')
- .attr('class', 'layer-list layer-list-gpx');
-
- var liEnter = ulEnter
- .append('li')
- .attr('class', 'list-item-gpx');
-
- liEnter
- .append('button')
- .attr('class', 'list-item-gpx-extent')
- .call(tooltip()
- .title(t('gpx.zoom'))
- .placement((textDirection === 'rtl') ? 'right' : 'left'))
- .on('click', function() {
- d3_event.preventDefault();
- d3_event.stopPropagation();
- gpx.fitZoom();
- })
- .call(svgIcon('#icon-search'));
-
- liEnter
- .append('button')
- .attr('class', 'list-item-gpx-browse')
- .call(tooltip()
- .title(t('gpx.browse'))
- .placement((textDirection === 'rtl') ? 'right' : 'left')
- )
- .on('click', function() {
- d3_select(document.createElement('input'))
- .attr('type', 'file')
- .on('change', function() {
- gpx.files(d3_event.target.files);
- })
- .node().click();
- })
- .call(svgIcon('#icon-geolocate'));
-
- var labelEnter = liEnter
- .append('label')
- .call(tooltip()
- .title(t('gpx.drag_drop'))
- .placement('top')
- );
-
- labelEnter
- .append('input')
- .attr('type', 'checkbox')
- .on('change', function() { toggleLayer('gpx'); });
-
- labelEnter
- .append('span')
- .text(t('gpx.local_layer'));
-
- // Update
- ul = ul
- .merge(ulEnter);
-
- ul.selectAll('.list-item-gpx')
- .classed('active', showsGpx)
- .selectAll('label')
- .classed('deemphasize', !hasGpx)
- .selectAll('input')
- .property('disabled', !hasGpx)
- .property('checked', showsGpx);
- }
-
-
- function drawList(selection, data, type, name, change, active) {
- var items = selection.selectAll('li')
- .data(data);
-
- // Exit
- items.exit()
- .remove();
-
- // Enter
- var enter = items.enter()
- .append('li')
- .attr('class', 'layer')
- .call(tooltip()
- .html(true)
- .title(function(d) {
- var tip = t(name + '.' + d + '.tooltip'),
- key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
-
- if (name === 'feature' && autoHiddenFeature(d)) {
- var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden');
- tip += '' + msg + '
';
- }
- return uiTooltipHtml(tip, key);
+ liEnter
+ .append('button')
+ .attr('class', 'list-item-gpx-browse')
+ .call(tooltip()
+ .title(t('gpx.browse'))
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ )
+ .on('click', function() {
+ d3_select(document.createElement('input'))
+ .attr('type', 'file')
+ .on('change', function() {
+ gpx.files(d3_event.target.files);
})
- .placement('top')
- );
+ .node().click();
+ })
+ .call(svgIcon('#icon-geolocate'));
- var label = enter
- .append('label');
+ var labelEnter = liEnter
+ .append('label')
+ .call(tooltip()
+ .title(t('gpx.drag_drop'))
+ .placement('top')
+ );
- label
- .append('input')
- .attr('type', type)
- .attr('name', name)
- .on('change', change);
+ labelEnter
+ .append('input')
+ .attr('type', 'checkbox')
+ .on('change', function() { toggleLayer('gpx'); });
- label
- .append('span')
- .text(function(d) { return t(name + '.' + d + '.description'); });
+ labelEnter
+ .append('span')
+ .text(t('gpx.local_layer'));
- // Update
- items = items
- .merge(enter);
+ // Update
+ ul = ul
+ .merge(ulEnter);
- items
- .classed('active', active)
- .selectAll('input')
- .property('checked', active)
- .property('indeterminate', function(d) {
- return (name === 'feature' && autoHiddenFeature(d));
- });
+ ul.selectAll('.list-item-gpx')
+ .classed('active', showsGpx)
+ .selectAll('label')
+ .classed('deemphasize', !hasGpx)
+ .selectAll('input')
+ .property('disabled', !hasGpx)
+ .property('checked', showsGpx);
+ }
+
+
+ function drawListItems(selection, data, type, name, change, active) {
+ var items = selection.selectAll('li')
+ .data(data);
+
+ // Exit
+ items.exit()
+ .remove();
+
+ // Enter
+ var enter = items.enter()
+ .append('li')
+ .attr('class', 'layer')
+ .call(tooltip()
+ .html(true)
+ .title(function(d) {
+ var tip = t(name + '.' + d + '.tooltip'),
+ key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
+
+ if (name === 'feature' && autoHiddenFeature(d)) {
+ var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden');
+ tip += '' + msg + '
';
+ }
+ return uiTooltipHtml(tip, key);
+ })
+ .placement('top')
+ );
+
+ var label = enter
+ .append('label');
+
+ label
+ .append('input')
+ .attr('type', type)
+ .attr('name', name)
+ .on('change', change);
+
+ label
+ .append('span')
+ .text(function(d) { return t(name + '.' + d + '.description'); });
+
+ // Update
+ items = items
+ .merge(enter);
+
+ items
+ .classed('active', active)
+ .selectAll('input')
+ .property('checked', active)
+ .property('indeterminate', function(d) {
+ return (name === 'feature' && autoHiddenFeature(d));
+ });
+ }
+
+
+ function renderDataLayers(selection) {
+ var container = selection.selectAll('data-layer-container')
+ .data([0]);
+
+ _dataLayerContainer = container.enter()
+ .append('div')
+ .attr('class', 'data-layer-container')
+ .merge(container);
+ }
+
+
+ function renderFillList(selection) {
+ var container = selection.selectAll('layer-fill-list')
+ .data([0]);
+
+ _fillList = container.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-fill-list')
+ .merge(container);
+ }
+
+
+ function renderFeatureList(selection) {
+ var container = selection.selectAll('layer-feature-list')
+ .data([0]);
+
+ _featureList = container.enter()
+ .append('ul')
+ .attr('class', 'layer-list layer-feature-list')
+ .merge(container);
+ }
+
+
+ function update() {
+ _dataLayerContainer
+ .call(drawOsmItem)
+ .call(drawPhotoItems)
+ .call(drawGpxItem);
+
+ _fillList
+ .call(drawListItems, fills, 'radio', 'area_fill', setFill, showsFill);
+
+ _featureList
+ .call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature);
+ }
+
+
+ function toggleWireframe() {
+ if (d3_event) {
+ d3_event.preventDefault();
+ d3_event.stopPropagation();
}
+ setFill((_fillSelected === 'wireframe' ? _fillDefault : 'wireframe'));
+ context.map().pan([0,0]); // trigger a redraw
+ }
- function update() {
- dataLayerContainer
- .call(drawOsmItem)
- .call(drawPhotoItems)
- .call(drawGpxItem);
+ function mapData(selection) {
- fillList
- .call(drawList, fills, 'radio', 'area_fill', setFill, showsFill);
-
- featureList
- .call(drawList, features, 'checkbox', 'feature', clickFeature, showsFeature);
- }
-
-
- function hidePanel() {
+ function hidePane() {
setVisible(false);
}
-
- function togglePanel() {
+ function togglePane() {
if (d3_event) d3_event.preventDefault();
- tooltipBehavior.hide(button);
+ paneTooltip.hide(button);
setVisible(!button.classed('active'));
}
-
- function toggleWireframe() {
- if (d3_event) {
- d3_event.preventDefault();
- d3_event.stopPropagation();
- }
- setFill((fillSelected === 'wireframe' ? fillDefault : 'wireframe'));
- context.map().pan([0,0]); // trigger a redraw
- }
-
-
function setVisible(show) {
- if (show !== shown) {
+ if (show !== _shown) {
button.classed('active', show);
- shown = show;
+ _shown = show;
if (show) {
+ uiBackground.hidePane();
+ uiHelp.hidePane();
update();
- selection.on('mousedown.map_data-inside', function() {
- return d3_event.stopPropagation();
- });
- content.style('display', 'block')
+
+ pane
+ .style('display', 'block')
.style('right', '-300px')
.transition()
.duration(200)
.style('right', '0px');
+
} else {
- content.style('display', 'block')
+ pane
+ .style('display', 'block')
.style('right', '0px')
.transition()
.duration(200)
@@ -386,113 +429,80 @@ export function uiMapData(context) {
.on('end', function() {
d3_select(this).style('display', 'none');
});
- selection.on('mousedown.map_data-inside', null);
}
}
}
- var content = selection
- .append('div')
- .attr('class', 'fillL map-overlay col3 content hide'),
- tooltipBehavior = tooltip()
- .placement((textDirection === 'rtl') ? 'right' : 'left')
- .html(true)
- .title(uiTooltipHtml(t('map_data.description'), key)),
- button = selection
- .append('button')
- .attr('tabindex', -1)
- .on('click', togglePanel)
- .call(svgIcon('#icon-data', 'light'))
- .call(tooltipBehavior),
- shown = false;
+ var pane = selection
+ .append('div')
+ .attr('class', 'fillL map-overlay col3 content hide');
- content
- .append('h4')
+ var paneTooltip = tooltip()
+ .placement((textDirection === 'rtl') ? 'right' : 'left')
+ .html(true)
+ .title(uiTooltipHtml(t('map_data.description'), key));
+
+ var button = selection
+ .append('button')
+ .attr('tabindex', -1)
+ .on('click', togglePane)
+ .call(svgIcon('#icon-data', 'light'))
+ .call(paneTooltip);
+
+
+ pane
+ .append('h2')
.text(t('map_data.title'));
// data layers
- content
- .append('a')
- .text(t('map_data.data_layers'))
- .attr('href', '#')
- .classed('hide-toggle', true)
- .classed('expanded', true)
- .on('click', function() {
- var exp = d3_select(this).classed('expanded');
- dataLayerContainer.style('display', exp ? 'none' : 'block');
- d3_select(this).classed('expanded', !exp);
- d3_event.preventDefault();
- });
-
- var dataLayerContainer = content
+ pane
.append('div')
- .attr('class', 'data-data-layers')
- .style('display', 'block');
-
+ .attr('class', 'map-data-data-layers')
+ .call(uiDisclosure(context, 'data_layers', true)
+ .title(t('map_data.data_layers'))
+ .content(renderDataLayers)
+ );
// area fills
- content
- .append('a')
- .text(t('map_data.fill_area'))
- .attr('href', '#')
- .classed('hide-toggle', true)
- .classed('expanded', false)
- .on('click', function() {
- var exp = d3_select(this).classed('expanded');
- fillContainer.style('display', exp ? 'none' : 'block');
- d3_select(this).classed('expanded', !exp);
- d3_event.preventDefault();
- });
-
- var fillContainer = content
+ pane
.append('div')
- .attr('class', 'data-area-fills')
- .style('display', 'none');
-
- var fillList = fillContainer
- .append('ul')
- .attr('class', 'layer-list layer-fill-list');
-
+ .attr('class', 'map-data-area-fills')
+ .call(uiDisclosure(context, 'fill_area', false)
+ .title(t('map_data.fill_area'))
+ .content(renderFillList)
+ );
// feature filters
- content
- .append('a')
- .text(t('map_data.map_features'))
- .attr('href', '#')
- .classed('hide-toggle', true)
- .classed('expanded', false)
- .on('click', function() {
- var exp = d3_select(this).classed('expanded');
- featureContainer.style('display', exp ? 'none' : 'block');
- d3_select(this).classed('expanded', !exp);
- d3_event.preventDefault();
- });
-
- var featureContainer = content
+ pane
.append('div')
- .attr('class', 'data-feature-filters')
- .style('display', 'none');
-
- var featureList = featureContainer
- .append('ul')
- .attr('class', 'layer-list layer-feature-list');
+ .attr('class', 'map-data-feature-filters')
+ .call(uiDisclosure(context, 'map_features', false)
+ .title(t('map_data.map_features'))
+ .content(renderFeatureList)
+ );
+ // add listeners
context.features()
.on('change.map_data-update', update);
- setFill(fillDefault);
+ update();
+ setFill(_fillDefault);
var keybinding = d3_keybinding('features')
- .on(key, togglePanel)
+ .on(key, togglePane)
.on(t('area_fill.wireframe.key'), toggleWireframe)
- .on([t('background.key'), t('help.key')], hidePanel);
+ .on([t('background.key'), t('help.key')], hidePane);
d3_select(document)
.call(keybinding);
+
+ uiMapData.hidePane = hidePane;
+ uiMapData.togglePane = togglePane;
+ uiMapData.setVisible = setVisible;
}
- return map_data;
+ return mapData;
}
diff --git a/modules/ui/map_in_map.js b/modules/ui/map_in_map.js
index 08ca8b44d..3e056e4b0 100644
--- a/modules/ui/map_in_map.js
+++ b/modules/ui/map_in_map.js
@@ -284,9 +284,10 @@ export function uiMapInMap(context) {
isHidden = !isHidden;
- var label = d3_select('.minimap-toggle');
- label.classed('active', !isHidden)
- .select('input').property('checked', !isHidden);
+ d3_select('.minimap-toggle-item')
+ .classed('active', !isHidden)
+ .select('input')
+ .property('checked', !isHidden);
if (isHidden) {
wrap
diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js
index 9944c8c97..602278507 100644
--- a/modules/ui/preset_editor.js
+++ b/modules/ui/preset_editor.js
@@ -16,7 +16,6 @@ import { utilRebind } from '../util';
export function uiPresetEditor(context) {
var dispatch = d3_dispatch('change'),
formFields = uiFormFields(context),
- expandedPreference = (context.storage('preset_fields.expanded') !== 'false'),
state,
fieldsArr,
preset,
@@ -25,17 +24,10 @@ export function uiPresetEditor(context) {
function presetEditor(selection) {
- selection.call(uiDisclosure()
+ selection.call(uiDisclosure(context, 'preset_fields', true)
.title(t('inspector.all_fields'))
- .expanded(expandedPreference)
- .on('toggled', toggled)
.content(render)
);
-
- function toggled(expanded) {
- expandedPreference = expanded;
- context.storage('preset_fields.expanded', expanded);
- }
}
diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js
index 73c7de159..e8e47b058 100644
--- a/modules/ui/raw_member_editor.js
+++ b/modules/ui/raw_member_editor.js
@@ -20,8 +20,8 @@ import {
export function uiRawMemberEditor(context) {
- var id,
- taginfo = services.taginfo;
+ var taginfo = services.taginfo,
+ _entityID;
function selectMember(d) {
@@ -53,7 +53,7 @@ export function uiRawMemberEditor(context) {
function rawMemberEditor(selection) {
- var entity = context.entity(id),
+ var entity = context.entity(_entityID),
memberships = [];
entity.members.slice(0, 1000).forEach(function(member, index) {
@@ -68,21 +68,17 @@ export function uiRawMemberEditor(context) {
});
var gt = entity.members.length > 1000 ? '>' : '';
- selection.call(uiDisclosure()
+ selection.call(uiDisclosure(context, 'raw_member_editor', true)
.title(t('inspector.all_members') + ' (' + gt + memberships.length + ')')
.expanded(true)
- .on('toggled', toggled)
+ .updatePreference(false)
+ .on('toggled', function(expanded) {
+ if (expanded) { selection.node().parentNode.scrollTop += 200; }
+ })
.content(content)
);
- function toggled(expanded) {
- if (expanded) {
- selection.node().parentNode.scrollTop += 200;
- }
- }
-
-
function content(wrap) {
var list = wrap.selectAll('.member-list')
.data([0]);
@@ -201,8 +197,8 @@ export function uiRawMemberEditor(context) {
rawMemberEditor.entityID = function(_) {
- if (!arguments.length) return id;
- id = _;
+ if (!arguments.length) return _entityID;
+ _entityID = _;
return rawMemberEditor;
};
diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js
index 21952ab08..9b67c79d6 100644
--- a/modules/ui/raw_membership_editor.js
+++ b/modules/ui/raw_membership_editor.js
@@ -28,7 +28,8 @@ import { utilDisplayName, utilNoAuto } from '../util';
export function uiRawMembershipEditor(context) {
var taginfo = services.taginfo,
- id, showBlank;
+ _entityID,
+ _showBlank;
function selectRelation(d) {
@@ -47,11 +48,13 @@ export function uiRawMembershipEditor(context) {
function addMembership(d, role) {
- showBlank = false;
+ _showBlank = false;
+
+ var member = { id: _entityID, type: context.entity(_entityID).type, role: role };
if (d.relation) {
context.perform(
- actionAddMember(d.relation.id, { id: id, type: context.entity(id).type, role: role }),
+ actionAddMember(d.relation.id, member),
t('operations.add_member.annotation')
);
@@ -59,7 +62,7 @@ export function uiRawMembershipEditor(context) {
var relation = osmRelation();
context.perform(
actionAddEntity(relation),
- actionAddMember(relation.id, { id: id, type: context.entity(id).type, role: role }),
+ actionAddMember(relation.id, member),
t('operations.add.annotation.relation')
);
@@ -77,15 +80,12 @@ export function uiRawMembershipEditor(context) {
function relations(q) {
- var newRelation = {
- relation: null,
- value: t('inspector.new_relation')
- },
- result = [],
- graph = context.graph();
+ var newRelation = { relation: null, value: t('inspector.new_relation') };
+ var result = [];
+ var graph = context.graph();
context.intersects(context.extent()).forEach(function(entity) {
- if (entity.type !== 'relation' || entity.id === id)
+ if (entity.type !== 'relation' || entity.id === _entityID)
return;
var matched = context.presets().match(entity, graph),
@@ -96,10 +96,7 @@ export function uiRawMembershipEditor(context) {
if (q && value.toLowerCase().indexOf(q.toLowerCase()) === -1)
return;
- result.push({
- relation: entity,
- value: value
- });
+ result.push({ relation: entity, value: value });
});
result.sort(function(a, b) {
@@ -124,7 +121,7 @@ export function uiRawMembershipEditor(context) {
function rawMembershipEditor(selection) {
- var entity = context.entity(id),
+ var entity = context.entity(_entityID),
parents = context.graph().parentRelations(entity),
memberships = [];
@@ -137,21 +134,17 @@ export function uiRawMembershipEditor(context) {
});
var gt = parents.length > 1000 ? '>' : '';
- selection.call(uiDisclosure()
+ selection.call(uiDisclosure(context, 'raw_membership_editor', true)
.title(t('inspector.all_relations') + ' (' + gt + memberships.length + ')')
.expanded(true)
- .on('toggled', toggled)
+ .updatePreference(false)
+ .on('toggled', function(expanded) {
+ if (expanded) { selection.node().parentNode.scrollTop += 200; }
+ })
.content(content)
);
- function toggled(expanded) {
- if (expanded) {
- selection.node().parentNode.scrollTop += 200;
- }
- }
-
-
function content(wrap) {
var list = wrap.selectAll('.member-list')
.data([0]);
@@ -218,7 +211,7 @@ export function uiRawMembershipEditor(context) {
var newrow = list.selectAll('.member-row-new')
- .data(showBlank ? [0] : []);
+ .data(_showBlank ? [0] : []);
newrow.exit()
.remove();
@@ -272,7 +265,7 @@ export function uiRawMembershipEditor(context) {
addrel
.call(svgIcon('#icon-plus', 'light'))
.on('click', function() {
- showBlank = true;
+ _showBlank = true;
content(wrap);
list.selectAll('.member-entity-input').node().focus();
});
@@ -308,7 +301,7 @@ export function uiRawMembershipEditor(context) {
taginfo.roles({
debounce: true,
rtype: rtype || '',
- geometry: context.geometry(id),
+ geometry: context.geometry(_entityID),
query: role
}, function(err, data) {
if (!err) callback(sort(role, data));
@@ -328,8 +321,8 @@ export function uiRawMembershipEditor(context) {
rawMembershipEditor.entityID = function(_) {
- if (!arguments.length) return id;
- id = _;
+ if (!arguments.length) return _entityID;
+ _entityID = _;
return rawMembershipEditor;
};
diff --git a/modules/ui/raw_tag_editor.js b/modules/ui/raw_tag_editor.js
index e3cf7b6e4..9e5bccc70 100644
--- a/modules/ui/raw_tag_editor.js
+++ b/modules/ui/raw_tag_editor.js
@@ -25,34 +25,36 @@ import {
export function uiRawTagEditor(context) {
var taginfo = services.taginfo,
dispatch = d3_dispatch('change'),
- expandedPreference = (context.storage('raw_tag_editor.expanded') === 'true'),
- expandedCurrent = expandedPreference,
- updatePreference = true,
- readOnlyTags = [],
- showBlank = false,
- newRow,
- state,
- preset,
- tags,
- id;
+ _readOnlyTags = [],
+ _showBlank = false,
+ _updatePreference = true,
+ _expanded = false,
+ _newRow,
+ _state,
+ _preset,
+ _tags,
+ _entityID;
function rawTagEditor(selection) {
- var count = Object.keys(tags).filter(function(d) { return d; }).length;
+ var count = Object.keys(_tags).filter(function(d) { return d; }).length;
- selection.call(uiDisclosure()
+ var disclosure = uiDisclosure(context, 'raw_tag_editor', false)
.title(t('inspector.all_tags') + ' (' + count + ')')
- .expanded(expandedCurrent)
.on('toggled', toggled)
- .content(content)
- );
+ .updatePreference(_updatePreference)
+ .content(content);
+
+ // Sometimes we want to force the raw_tag_editor to be opened/closed..
+ // When undefined, uiDisclosure will use the user's stored preference.
+ if (_expanded !== undefined) {
+ disclosure.expanded(_expanded);
+ }
+
+ selection.call(disclosure);
function toggled(expanded) {
- expandedCurrent = expanded;
- if (updatePreference) {
- expandedPreference = expanded;
- context.storage('raw_tag_editor.expanded', expanded);
- }
+ _expanded = expanded;
if (expanded) {
selection.node().parentNode.scrollTop += 200;
}
@@ -61,14 +63,14 @@ export function uiRawTagEditor(context) {
function content(wrap) {
- var entries = _map(tags, function(v, k) {
+ var entries = _map(_tags, function(v, k) {
return { key: k, value: v };
});
- if (!entries.length || showBlank) {
- showBlank = false;
+ if (!entries.length || _showBlank) {
+ _showBlank = false;
entries.push({key: '', value: ''});
- newRow = '';
+ _newRow = '';
}
var list = wrap.selectAll('.tag-list')
@@ -138,8 +140,8 @@ export function uiRawTagEditor(context) {
items = items
.merge(enter)
.sort(function(a, b) {
- return (a.key === newRow && b.key !== newRow) ? 1
- : (a.key !== newRow && b.key === newRow) ? -1
+ return (a.key === _newRow && b.key !== _newRow) ? 1
+ : (a.key !== _newRow && b.key === _newRow) ? -1
: d3_ascending(a.key, b.key);
});
@@ -149,11 +151,11 @@ export function uiRawTagEditor(context) {
key = row.select('input.key'), // propagate bound data to child
value = row.select('input.value'); // propagate bound data to child
- if (id && taginfo) {
+ if (_entityID && taginfo) {
bindTypeahead(key, value);
}
- var isRelation = (id && context.entity(id).type === 'relation'),
+ var isRelation = (_entityID && context.entity(_entityID).type === 'relation'),
reference;
if (isRelation && tag.key === 'type') {
@@ -162,7 +164,7 @@ export function uiRawTagEditor(context) {
reference = uiTagReference({ key: tag.key, value: tag.value }, context);
}
- if (state === 'hover') {
+ if (_state === 'hover') {
reference.showing(false);
}
@@ -187,8 +189,8 @@ export function uiRawTagEditor(context) {
function isReadOnly(d) {
- for (var i = 0; i < readOnlyTags.length; i++) {
- if (d.key.match(readOnlyTags[i]) !== null) {
+ for (var i = 0; i < _readOnlyTags.length; i++) {
+ if (d.key.match(_readOnlyTags[i]) !== null) {
return true;
}
}
@@ -206,7 +208,7 @@ export function uiRawTagEditor(context) {
function bindTypeahead(key, value) {
if (isReadOnly({ key: key })) return;
- var geometry = context.geometry(id);
+ var geometry = context.geometry(_entityID);
key.call(d3_combobox()
.container(context.container())
@@ -275,7 +277,7 @@ export function uiRawTagEditor(context) {
var match = kNew.match(/^(.*?)(?:_(\d+))?$/),
base = match[1],
suffix = +(match[2] || 1);
- while (tags[kNew]) { // rename key if already in use
+ while (_tags[kNew]) { // rename key if already in use
kNew = base + '_' + suffix++;
}
}
@@ -284,8 +286,8 @@ export function uiRawTagEditor(context) {
d.key = kNew; // Maintain DOM identity through the subsequent update.
- if (newRow === kOld) { // see if this row is still a new row
- newRow = ((d.value === '' || kNew === '') ? kNew : undefined);
+ if (_newRow === kOld) { // see if this row is still a new row
+ _newRow = ((d.value === '' || kNew === '') ? kNew : undefined);
}
this.value = kNew;
@@ -298,8 +300,8 @@ export function uiRawTagEditor(context) {
var tag = {};
tag[d.key] = this.value;
- if (newRow === d.key && d.key !== '' && d.value !== '') { // not a new row anymore
- newRow = undefined;
+ if (_newRow === d.key && d.key !== '' && d.value !== '') { // not a new row anymore
+ _newRow = undefined;
}
dispatch.call('change', this, tag);
@@ -320,7 +322,7 @@ export function uiRawTagEditor(context) {
// handler. Without the setTimeout, the call to `content` would
// wipe out the pending value change.
setTimeout(function() {
- showBlank = true;
+ _showBlank = true;
content(wrap);
list.selectAll('li:last-child input.key').node().focus();
}, 0);
@@ -329,51 +331,51 @@ export function uiRawTagEditor(context) {
rawTagEditor.state = function(_) {
- if (!arguments.length) return state;
- state = _;
+ if (!arguments.length) return _state;
+ _state = _;
return rawTagEditor;
};
rawTagEditor.preset = function(_) {
- if (!arguments.length) return preset;
- preset = _;
- if (preset.isFallback()) {
- expandedCurrent = true;
- updatePreference = false;
+ if (!arguments.length) return _preset;
+ _preset = _;
+ if (_preset.isFallback()) {
+ _expanded = true;
+ _updatePreference = false;
} else {
- expandedCurrent = expandedPreference;
- updatePreference = true;
+ _expanded = undefined;
+ _updatePreference = true;
}
return rawTagEditor;
};
rawTagEditor.tags = function(_) {
- if (!arguments.length) return tags;
- tags = _;
+ if (!arguments.length) return _tags;
+ _tags = _;
return rawTagEditor;
};
rawTagEditor.entityID = function(_) {
- if (!arguments.length) return id;
- id = _;
+ if (!arguments.length) return _entityID;
+ _entityID = _;
return rawTagEditor;
};
rawTagEditor.expanded = function(_) {
- if (!arguments.length) return expandedCurrent;
- expandedCurrent = _;
- updatePreference = false;
+ if (!arguments.length) return _expanded;
+ _expanded = _;
+ _updatePreference = false;
return rawTagEditor;
};
rawTagEditor.readOnlyTags = function(_) {
- if (!arguments.length) return readOnlyTags;
- readOnlyTags = _;
+ if (!arguments.length) return _readOnlyTags;
+ _readOnlyTags = _;
return rawTagEditor;
};