Merge branch 'edit_menu'

This commit is contained in:
Bryan Housel
2017-02-25 15:29:01 -05:00
40 changed files with 812 additions and 258 deletions
+152 -48
View File
@@ -11,7 +11,7 @@ html, body {
}
body {
font: normal 12px/1.6667 -apple-system, BlinkMacSystemFont,
font: normal 12px/1.6667 "-apple-system", BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue", "Arial",
sans-serif;
@@ -564,6 +564,7 @@ button.save.has-count .count::before {
min-width: 768px;
}
/* Header for modals / panes
------------------------------------------------------- */
@@ -2499,14 +2500,14 @@ img.tile-removing {
bottom:0;
border-radius: 0;
pointer-events: none;
display: flex;
flex-direction: column;
}
#attrib {
width: 100%;
height: 20px;
margin-bottom: 5px;
float: left;
clear: both;
}
#attrib * { pointer-events: all; }
@@ -2531,24 +2532,80 @@ img.tile-removing {
}
.source-image {
height:20px;
height: 20px;
vertical-align:top;
}
#footer {
width: 100%;
float: left;
clear: both;
pointer-events: all;
display: block;
height: 30px;
}
#flash-wrap {
display: flex;
flex: 0 0 100%;
flex-flow: row nowrap;
justify-content: space-between;
max-height: 30px;
position: absolute;
right: 0;
left: 0;
}
#flash-wrap .content {
display: flex;
flex: 1 0 auto;
flex-flow: row nowrap;
align-items: center;
padding: 2px;
height: 30px;
}
#flash-wrap svg.operation-icon {
flex: 0 0 auto;
width: 20px;
height: 20px;
margin: 0 8px;
}
#flash-wrap div.operation-tip {
flex: 1 1 auto;
}
#footer-wrap {
display: flex;
flex: 0 0 100%;
flex-flow: row nowrap;
justify-content: space-between;
max-height: 30px;
position: absolute;
right: 0;
left: 0;
}
.footer-show {
bottom: 0px;
transition: bottom 75ms linear;
-moz-transition: bottom 75ms linear;
-webkit-transition: bottom 75ms linear;
}
.footer-hide {
bottom: -35px;
transition: bottom 75ms linear;
-moz-transition: bottom 75ms linear;
-webkit-transition: bottom 75ms linear;
}
#scale-block {
display: table-cell;
vertical-align: bottom;
width: 250px;
max-height: 30px;
float: left;
clear: left;
flex: 0 0 250px;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
@@ -2557,13 +2614,16 @@ img.tile-removing {
#info-block {
max-height: 30px;
clear: right;
flex: 1 1 auto;
}
#scale {
height: 30px;
width: 100%;
}
[dir='rtl'] #scale {
transform: scaleX(-1);
}
#scale:hover {
cursor: pointer;
@@ -2575,6 +2635,9 @@ img.tile-removing {
fill: #ccc;
text-anchor: start;
}
[dir='rtl'] #scale text {
transform: scaleX(-1);
}
#scale path {
fill: none;
@@ -2583,12 +2646,19 @@ img.tile-removing {
shape-rendering: crispEdges;
}
#about-list {
text-align: right;
margin-right: 10px;
clear: right;
overflow: hidden;
}
[dir='rtl'] #about-list {
text-align: left;
clear: left;
margin-left: 10px;
margin-right: 0;
}
#about-list li {
float: right;
@@ -2596,12 +2666,24 @@ img.tile-removing {
padding: 5px 0 5px 5px;
margin-left: 5px;
}
[dir='rtl'] #about-list li {
float: left;
border-left: none;
border-right: 1px solid rgba(255,255,255,.5);
margin-left: 0;
margin-right: 5px;
padding: 5px 5px 5px 0;
}
#about-list li:last-child {
border-left: 0;
margin-left: 0;
padding-left: 0;
}
[dir='rtl'] #about-list li:last-child {
border-right: none;
}
.source-switch a {
padding: 2px 4px 4px 4px;
@@ -2624,12 +2706,13 @@ img.tile-removing {
}
.api-status {
float: right;
clear: both;
text-align: right;
width: 100%;
padding: 0px 10px;
color: #eee;
flex: 1 1 auto;
}
[dir='rtl'] .api-status {
text-align: left;
}
.api-status.offline,
@@ -2645,6 +2728,7 @@ img.tile-removing {
color: #ccf;
}
/* Modals
------------------------------------------------------- */
@@ -3095,15 +3179,25 @@ img.tile-removing {
border-width: 0 5px 5px;
}
.tooltip-heading {
font-weight: bold;
background: #F6F6F6;
padding: 10px;
margin: -10px -10px 10px -10px;
border-radius: 3px 3px 0 0;
font-size: 14px;
}
.keyhint-wrap {
background: #F6F6F6;
padding: 10px;
margin: 10px -10px -10px;
margin: 10px -10px -10px -10px;
border-radius: 0 0 3px 3px;
}
.tooltip-inner .keyhint {
font-weight: bold;
margin-left: 5px;
}
/* Exceptions for tooltip layouts */
@@ -3134,6 +3228,7 @@ img.tile-removing {
}
.map-overlay .tooltip-inner,
.map-overlay .tooltip-heading,
.map-overlay .keyhint-wrap,
.entity-editor-pane .tooltip-inner,
.warning-section .tooltip-inner {
@@ -3159,6 +3254,8 @@ img.tile-removing {
left: 60px;
}
/* radial menu (deprecated) */
.radial-menu-tooltip {
opacity: 0.8;
display: none;
@@ -3196,6 +3293,46 @@ img.tile-removing {
color: rgba(40,40,40,.5);
}
/* edit menu */
.edit-menu-tooltip {
display: none;
position: absolute;
width: 200px;
}
.edit-menu-background {
fill: #eee;
}
.edit-menu-item rect {
fill: #eee;
}
.edit-menu-item rect:active,
.edit-menu-item rect:hover {
fill: #ccc;
}
.edit-menu-item.disabled rect {
cursor: not-allowed;
}
.edit-menu-item.disabled rect:hover {
cursor: not-allowed;
fill: #eee;
}
.edit-menu-item use {
fill: #222;
color: #79f;
}
.edit-menu-item.disabled use {
fill: rgba(32,32,32,.2);
color: rgba(40,40,40,.2);
}
.lasso-path {
fill-opacity:0.3;
stroke: #fff;
@@ -3677,39 +3814,6 @@ img.tile-removing {
-ms-filter: "FlipH";
}
/* footer */
[dir='rtl'] #scale-block {
float: right;
clear: right;
}
[dir='rtl'] #info-block {
clear: left;
}
[dir='rtl'] #about-list {
text-align: left;
clear: left;
margin-left: 10px;
margin-right: 0;
}
[dir='rtl'] #about-list li {
float: left;
border-left: none;
border-right: 1px solid rgba(255,255,255,.5);
margin-left: 0;
margin-right: 5px;
padding: 5px 5px 5px 0;
}
[dir='rtl'] #about-list li:last-child {
border-right: none;
}
[dir='rtl'] #scale text {
text-anchor: end;
}
/* increment / decrement control - code by Naoufel Razouane */
+7 -5
View File
@@ -146,7 +146,9 @@ en:
single: This feature can't be moved because it is connected to a hidden feature.
multiple: These features can't be moved because some are connected to hidden features.
reflect:
title: reflect
title:
long: Reflect Long
short: Reflect Short
description:
long:
single: Reflect this feature across its long axis.
@@ -159,10 +161,10 @@ en:
short: Y
annotation:
long:
single: Reflected an feature across its long axis.
single: Reflected a feature across its long axis.
multiple: Reflected multiple features across their long axis.
short:
single: Reflected an feature across its short axis.
single: Reflected a feature across its short axis.
multiple: Reflected multiple features across their short axis.
incomplete_relation:
single: This feature can't be reflected because it hasn't been fully downloaded.
@@ -812,8 +814,8 @@ en:
close: "The feature editor will remember all of your changes automatically. When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**"
reselect: "Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**"
fixname: "**Change the name, then click the {button} button to close the feature editor.**"
reselect_delete: "All features on the map can be deleted. **Click to select the point you created.**"
delete: "The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"
rightclick: "You can right-click on features to see the list of operations that can be performed on them. **Right-click to select the point you created.**"
delete: "**Click on the {button} button to delete the point.**"
areas:
title: "Areas"
add: "Areas are used to show the boundaries of features like lakes, buildings, and residential areas. They can be also be used for more detailed mapping of many features you might normally map as points. **Click the {button} Area button to add a new area.**"
+8 -5
View File
@@ -188,7 +188,10 @@
}
},
"reflect": {
"title": "reflect",
"title": {
"long": "Reflect Long",
"short": "Reflect Short"
},
"description": {
"long": {
"single": "Reflect this feature across its long axis.",
@@ -205,11 +208,11 @@
},
"annotation": {
"long": {
"single": "Reflected an feature across its long axis.",
"single": "Reflected a feature across its long axis.",
"multiple": "Reflected multiple features across their long axis."
},
"short": {
"single": "Reflected an feature across its short axis.",
"single": "Reflected a feature across its short axis.",
"multiple": "Reflected multiple features across their short axis."
}
},
@@ -666,8 +669,8 @@
"close": "The feature editor will remember all of your changes automatically. When you change a feature, the close button will change to a checkmark. **Click the {button} button to close the feature editor**",
"reselect": "Often points will already exist, but have mistakes or be incomplete. We can edit existing points. **Click to select the point you just created.**",
"fixname": "**Change the name, then click the {button} button to close the feature editor.**",
"reselect_delete": "All features on the map can be deleted. **Click to select the point you created.**",
"delete": "The menu around the point contains operations that can be performed on it, including delete. **Click on the {button} button to delete the point.**"
"rightclick": "You can right-click on features to see the list of operations that can be performed on them. **Right-click to select the point you created.**",
"delete": "**Click on the {button} button to delete the point.**"
},
"areas": {
"title": "Areas",
+1 -3
View File
@@ -246,9 +246,7 @@ export function behaviorDrawWay(context, wayId, index, mode, baseGraph) {
}, 1000);
if (context.hasEntity(wayId)) {
context.enter(
modeSelect(context, [wayId]).suppressMenu(true).newFeature(true)
);
context.enter(modeSelect(context, [wayId]).newFeature(true));
} else {
context.enter(modeBrowse(context));
}
+44 -2
View File
@@ -1,5 +1,6 @@
import * as d3 from 'd3';
import { d3keybinding } from '../lib/d3.keybinding.js';
import { uiFlash } from '../ui';
/* Creates a keybinding behavior for an operation */
@@ -7,12 +8,53 @@ export function behaviorOperation(context) {
var which, keybinding;
function drawIcon(selection) {
var button = selection
.append('svg')
.attr('class', 'operation-icon')
.append('g')
.attr('class', 'radial-menu-item radial-menu-item-' + which.id)
.attr('transform', 'translate(10,10)')
.classed('disabled', which.disabled());
button
.append('circle')
.attr('r', 9);
button
.append('use')
.attr('transform', 'translate(-7,-7)')
.attr('width', '14')
.attr('height', '14')
.attr('xlink:href', '#operation-' + which.id);
return selection;
}
var behavior = function () {
if (which) {
if (which && which.available() && !context.inIntro()) {
keybinding = d3keybinding('behavior.key.' + which.id);
keybinding.on(which.keys, function() {
d3.event.preventDefault();
if (which.available() && !which.disabled() && !context.inIntro()) {
var disabled = which.disabled();
if (disabled) {
uiFlash(3000)
.html('')
.call(drawIcon)
.append('div')
.attr('class', 'operation-tip')
.text(which.tooltip);
} else {
uiFlash(1500)
.html('')
.call(drawIcon)
.append('div')
.attr('class', 'operation-tip')
.text(which.annotation() || which.title);
which();
}
});
+97 -22
View File
@@ -1,10 +1,15 @@
import * as d3 from 'd3';
import _ from 'lodash';
import { modeBrowse, modeSelect } from '../modes/index';
import { osmEntity } from '../osm/index';
import { geoEuclideanDistance } from '../geo';
import { modeBrowse, modeSelect } from '../modes';
import { osmEntity } from '../osm';
export function behaviorSelect(context) {
var suppressMenu = true,
tolerance = 4,
p1 = null;
function keydown() {
if (d3.event && d3.event.shiftKey) {
@@ -22,31 +27,96 @@ export function behaviorSelect(context) {
}
function point() {
return d3.mouse(context.container().node());
}
function contextmenu() {
if (!p1) p1 = point();
d3.event.preventDefault();
suppressMenu = false;
click();
}
function mousedown() {
if (!p1) p1 = point();
d3.select(window)
.on('mouseup.select', mouseup, true);
var isShowAlways = +context.storage('edit-menu-show-always') === 1;
suppressMenu = !isShowAlways;
}
function mouseup() {
click();
}
function click() {
var datum = d3.event.target.__data__,
lasso = d3.select('#surface .lasso').node(),
d3.select(window)
.on('mouseup.select', null, true);
if (!p1) return;
var p2 = point(),
dist = geoEuclideanDistance(p1, p2);
p1 = null;
if (dist > tolerance) {
return;
}
var isMultiselect = d3.event.shiftKey || d3.select('#surface .lasso').node(),
isShowAlways = +context.storage('edit-menu-show-always') === 1,
datum = d3.event.target.__data__,
mode = context.mode();
if (datum.type === 'midpoint') {
// do nothing
} else if (!(datum instanceof osmEntity)) {
if (!d3.event.shiftKey && !lasso && mode.id !== 'browse')
context.enter(modeBrowse(context));
} else if (!d3.event.shiftKey && !lasso) {
// Avoid re-entering Select mode with same entity.
if (context.selectedIDs().length !== 1 || context.selectedIDs()[0] !== datum.id) {
context.enter(modeSelect(context, [datum.id]));
} else {
mode.suppressMenu(false).reselect();
if (datum.type === 'midpoint') {
// clicked midpoint, do nothing..
} else if (!(datum instanceof osmEntity)) {
// clicked nothing..
if (!isMultiselect && mode.id !== 'browse') {
context.enter(modeBrowse(context));
}
} else if (context.selectedIDs().indexOf(datum.id) >= 0) {
var selectedIDs = _.without(context.selectedIDs(), datum.id);
context.enter(selectedIDs.length ? modeSelect(context, selectedIDs) : modeBrowse(context));
} else {
context.enter(modeSelect(context, context.selectedIDs().concat([datum.id])));
// clicked an entity..
var selectedIDs = context.selectedIDs();
if (!isMultiselect) {
if (selectedIDs.length > 1 && (!suppressMenu && !isShowAlways)) {
// multiple things already selected, just show the menu...
mode.suppressMenu(false).reselect();
} else {
// select a single thing..
context.enter(modeSelect(context, [datum.id]).suppressMenu(suppressMenu));
}
} else {
if (selectedIDs.indexOf(datum.id) !== -1) {
// clicked entity is already in the selectedIDs list..
if (!suppressMenu && !isShowAlways) {
// don't deselect clicked entity, just show the menu.
mode.suppressMenu(false).reselect();
} else {
// deselect clicked entity, then reenter select mode or return to browse mode..
selectedIDs = _.without(selectedIDs, datum.id);
context.enter(selectedIDs.length ? modeSelect(context, selectedIDs) : modeBrowse(context));
}
} else {
// clicked entity is not in the selected list, add it..
selectedIDs = selectedIDs.concat([datum.id]);
context.enter(modeSelect(context, selectedIDs).suppressMenu(suppressMenu));
}
}
}
// reset for next time..
suppressMenu = true;
}
@@ -55,7 +125,9 @@ export function behaviorSelect(context) {
.on('keydown.select', keydown)
.on('keyup.select', keyup);
selection.on('click.select', click);
selection
.on('mousedown.select', mousedown)
.on('contextmenu.select', contextmenu);
keydown();
};
@@ -64,9 +136,12 @@ export function behaviorSelect(context) {
behavior.off = function(selection) {
d3.select(window)
.on('keydown.select', null)
.on('keyup.select', null);
.on('keyup.select', null)
.on('mouseup.select', null, true);
selection.on('click.select', null);
selection
.on('mousedown.select', null)
.on('contextmenu.select', null);
keyup();
};
+1 -1
View File
@@ -32,7 +32,7 @@ export function modeAddPoint(context) {
);
context.enter(
modeSelect(context, [node.id]).suppressMenu(true).newFeature(true)
modeSelect(context, [node.id]).newFeature(true)
);
}
+1 -1
View File
@@ -222,7 +222,7 @@ export function modeDragNode(context) {
});
if (reselection.length) {
context.enter(modeSelect(context, reselection).suppressMenu(true));
context.enter(modeSelect(context, reselection));
} else {
context.enter(modeBrowse(context));
}
+2 -2
View File
@@ -115,7 +115,7 @@ export function modeMove(context, entityIDs, baseGraph) {
function finish() {
d3.event.stopPropagation();
context.enter(modeSelect(context, entityIDs).suppressMenu(true));
context.enter(modeSelect(context, entityIDs));
stopNudge();
}
@@ -126,7 +126,7 @@ export function modeMove(context, entityIDs, baseGraph) {
context.enter(modeBrowse(context));
} else {
context.pop();
context.enter(modeSelect(context, entityIDs).suppressMenu(true));
context.enter(modeSelect(context, entityIDs));
}
stopNudge();
}
+2 -2
View File
@@ -92,13 +92,13 @@ export function modeRotate(context, entityIDs) {
function finish() {
d3.event.stopPropagation();
context.enter(modeSelect(context, entityIDs).suppressMenu(true));
context.enter(modeSelect(context, entityIDs));
}
function cancel() {
context.pop();
context.enter(modeSelect(context, entityIDs).suppressMenu(true));
context.enter(modeSelect(context, entityIDs));
}
+48 -36
View File
@@ -28,9 +28,12 @@ import {
import { modeBrowse } from './browse';
import { modeDragNode } from './drag_node';
import * as Operations from '../operations/index';
import { uiRadialMenu, uiSelectionList } from '../ui/index';
import { uiEditMenu, uiSelectionList } from '../ui';
import { uiCmd } from '../ui/cmd';
import { utilEntityOrMemberSelector, utilEntitySelector } from '../util/index';
import { utilEntityOrMemberSelector, utilEntitySelector } from '../util';
// deprecation warning - Radial Menu to be removed in iD v3
import { uiRadialMenu } from '../ui';
var relatedParent;
@@ -54,9 +57,9 @@ export function modeSelect(context, selectedIDs) {
modeDragNode(context).selectedIDs(selectedIDs).behavior
],
inspector,
radialMenu,
editMenu,
newFeature = false,
suppressMenu = false,
suppressMenu = true,
follow = false;
@@ -139,27 +142,24 @@ export function modeSelect(context, selectedIDs) {
function closeMenu() {
if (radialMenu) {
context.surface().call(radialMenu.close);
if (editMenu) {
context.surface().call(editMenu.close);
}
}
function positionMenu() {
if (suppressMenu || !radialMenu) { return; }
if (suppressMenu || !editMenu) { return; }
var entity = singular();
if (entity && context.geometry(entity.id) === 'relation') {
suppressMenu = true;
} else if (entity && entity.type === 'node') {
radialMenu.center(context.projection(entity.loc));
} else {
var point = context.mouse(),
viewport = geoExtent(context.projection.clipExtent()).polygon();
if (geoPointInPolygon(point, viewport)) {
radialMenu.center(point);
} else {
suppressMenu = true;
editMenu.center(point);
}
}
}
@@ -167,14 +167,15 @@ export function modeSelect(context, selectedIDs) {
function showMenu() {
closeMenu();
if (!suppressMenu && radialMenu) {
context.surface().call(radialMenu);
if (editMenu) {
context.surface().call(editMenu);
}
}
function toggleMenu() {
if (d3.select('.radial-menu').empty()) {
// deprecation warning - Radial Menu to be removed in iD v3
if (d3.select('.edit-menu, .radial-menu').empty()) {
showMenu();
} else {
closeMenu();
@@ -196,7 +197,9 @@ export function modeSelect(context, selectedIDs) {
}
positionMenu();
showMenu();
if (!suppressMenu) {
showMenu();
}
};
@@ -245,6 +248,7 @@ export function modeSelect(context, selectedIDs) {
d3.event.preventDefault();
d3.event.stopPropagation();
} else if (datum.type === 'midpoint') {
context.perform(
actionAddMidpoint({loc: datum.loc, edge: datum.edge}, osmNode()),
@@ -306,7 +310,7 @@ export function modeSelect(context, selectedIDs) {
if (parent) {
var way = context.entity(parent);
context.enter(
modeSelect(context, [way.first()]).follow(true).suppressMenu(true)
modeSelect(context, [way.first()]).follow(true)
);
}
}
@@ -318,7 +322,7 @@ export function modeSelect(context, selectedIDs) {
if (parent) {
var way = context.entity(parent);
context.enter(
modeSelect(context, [way.last()]).follow(true).suppressMenu(true)
modeSelect(context, [way.last()]).follow(true)
);
}
}
@@ -342,7 +346,7 @@ export function modeSelect(context, selectedIDs) {
if (index !== -1) {
context.enter(
modeSelect(context, [way.nodes[index]]).follow(true).suppressMenu(true)
modeSelect(context, [way.nodes[index]]).follow(true)
);
}
}
@@ -366,7 +370,7 @@ export function modeSelect(context, selectedIDs) {
if (index !== -1) {
context.enter(
modeSelect(context, [way.nodes[index]]).follow(true).suppressMenu(true)
modeSelect(context, [way.nodes[index]]).follow(true)
);
}
}
@@ -401,7 +405,14 @@ export function modeSelect(context, selectedIDs) {
.map(function(o) { return o(selectedIDs, context); })
.filter(function(o) { return o.available(); });
operations.unshift(Operations.operationDelete(selectedIDs, context));
// deprecation warning - Radial Menu to be removed in iD v3
var isRadialMenu = context.storage('edit-menu-style') === 'radial';
if (isRadialMenu) {
operations = operations.slice(0,7);
operations.unshift(Operations.operationDelete(selectedIDs, context));
} else {
operations.push(Operations.operationDelete(selectedIDs, context));
}
operations.forEach(function(operation) {
if (operation.behavior) {
@@ -425,7 +436,11 @@ export function modeSelect(context, selectedIDs) {
d3.select(document)
.call(keybinding);
radialMenu = uiRadialMenu(context, operations);
// deprecation warning - Radial Menu to be removed in iD v3
editMenu = isRadialMenu
? uiRadialMenu(context, operations)
: uiEditMenu(context, operations);
context.ui().sidebar
.select(singular() ? singular().id : null, newFeature);
@@ -438,12 +453,15 @@ export function modeSelect(context, selectedIDs) {
.on('move.select', closeMenu)
.on('drawn.select', selectElements);
context.surface()
.on('dblclick.select', dblclick);
selectElements();
var show = d3.event && !suppressMenu;
if (show) {
positionMenu();
if (selectedIDs.length > 1) {
var entities = uiSelectionList(context, selectedIDs);
context.ui().sidebar.show(entities);
}
if (follow) {
@@ -459,18 +477,12 @@ export function modeSelect(context, selectedIDs) {
}
timeout = window.setTimeout(function() {
if (show) {
positionMenu();
if (!suppressMenu) {
showMenu();
}
}, 270); /* after any centerEase completes */
context.surface()
.on('dblclick.select', dblclick);
}, 200);
if (selectedIDs.length > 1) {
var entities = uiSelectionList(context, selectedIDs);
context.ui().sidebar.show(entities);
}
};
@@ -485,7 +497,7 @@ export function modeSelect(context, selectedIDs) {
keybinding.off();
closeMenu();
radialMenu = undefined;
editMenu = undefined;
context.history()
.on('undone.select', null)
+6 -1
View File
@@ -13,7 +13,7 @@ export function operationCircularize(selectedIDs, context) {
var operation = function() {
context.perform(action, t('operations.circularize.annotation.' + geometry));
context.perform(action, operation.annotation());
};
@@ -43,6 +43,11 @@ export function operationCircularize(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.circularize.annotation.' + geometry);
};
operation.id = 'circularize';
operation.keys = [t('operations.circularize.key')];
operation.title = t('operations.circularize.title');
+5
View File
@@ -53,6 +53,11 @@ export function operationContinue(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.continue.annotation.line');
};
operation.id = 'continue';
operation.keys = [t('operations.continue.key')];
operation.title = t('operations.continue.title');
+11 -12
View File
@@ -13,21 +13,15 @@ export function operationDelete(selectedIDs, context) {
var operation = function() {
var annotation,
nextSelectedID;
var nextSelectedID;
if (selectedIDs.length > 1) {
annotation = t('operations.delete.annotation.multiple', { n: selectedIDs.length });
} else {
if (selectedIDs.length === 1) {
var id = selectedIDs[0],
entity = context.entity(id),
geometry = context.geometry(id),
parents = context.graph().parentWays(entity),
parent = parents[0];
annotation = t('operations.delete.annotation.' + geometry);
// Select the next closest node in the way.
if (geometry === 'vertex' && parent.nodes.length > 2) {
var nodes = parent.nodes,
@@ -47,12 +41,10 @@ export function operationDelete(selectedIDs, context) {
}
}
context.perform(action, annotation);
context.perform(action, operation.annotation());
if (nextSelectedID && context.hasEntity(nextSelectedID)) {
context.enter(
modeSelect(context, [nextSelectedID]).follow(true).suppressMenu(true)
);
context.enter(modeSelect(context, [nextSelectedID]).follow(true));
} else {
context.enter(modeBrowse(context));
}
@@ -108,6 +100,13 @@ export function operationDelete(selectedIDs, context) {
};
operation.annotation = function() {
return selectedIDs.length === 1 ?
t('operations.delete.annotation.' + context.geometry(selectedIDs[0])) :
t('operations.delete.annotation.multiple', { n: selectedIDs.length });
};
operation.id = 'delete';
operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
operation.title = t('operations.delete.title');
+6 -1
View File
@@ -18,7 +18,7 @@ export function operationDisconnect(selectedIDs, context) {
var operation = function() {
context.perform(action, t('operations.disconnect.annotation'));
context.perform(action, operation.annotation());
};
@@ -44,6 +44,11 @@ export function operationDisconnect(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.disconnect.annotation');
};
operation.id = 'disconnect';
operation.keys = [t('operations.disconnect.key')];
operation.title = t('operations.disconnect.title');
+8 -4
View File
@@ -15,8 +15,7 @@ export function operationMerge(selectedIDs, context) {
mergePolygon = actionMergePolygon(selectedIDs);
var operation = function() {
var annotation = t('operations.merge.annotation', {n: selectedIDs.length}),
action;
var action;
if (!join.disabled(context.graph())) {
action = join;
@@ -26,12 +25,12 @@ export function operationMerge(selectedIDs, context) {
action = mergePolygon;
}
context.perform(action, annotation);
context.perform(action, operation.annotation());
var ids = selectedIDs.filter(function(id) {
var entity = context.hasEntity(id);
return entity && entity.type !== 'node';
});
context.enter(modeSelect(context, ids).suppressMenu(true));
context.enter(modeSelect(context, ids));
};
@@ -69,6 +68,11 @@ export function operationMerge(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.merge.annotation', { n: selectedIDs.length });
};
operation.id = 'merge';
operation.keys = [t('operations.merge.key')];
operation.title = t('operations.merge.title');
+7
View File
@@ -49,6 +49,13 @@ export function operationMove(selectedIDs, context) {
};
operation.annotation = function() {
return selectedIDs.length === 1 ?
t('operations.move.annotation.' + context.geometry(selectedIDs[0])) :
t('operations.move.annotation.multiple');
};
operation.id = 'move';
operation.keys = [t('operations.move.key')];
operation.title = t('operations.move.title');
+6 -1
View File
@@ -13,7 +13,7 @@ export function operationOrthogonalize(selectedIDs, context) {
var operation = function() {
context.perform(action, t('operations.orthogonalize.annotation.' + geometry));
context.perform(action, operation.annotation());
};
@@ -44,6 +44,11 @@ export function operationOrthogonalize(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.orthogonalize.annotation.' + geometry);
};
operation.id = 'orthogonalize';
operation.keys = [t('operations.orthogonalize.key')];
operation.title = t('operations.orthogonalize.title');
+7 -2
View File
@@ -26,7 +26,7 @@ export function operationReflect(selectedIDs, context, axis) {
var operation = function() {
var action = actionReflect(selectedIDs, context.projection)
.useLongAxis(Boolean(axis === 'long'));
context.perform(action, t('operations.reflect.annotation.' + axis + '.' + multi));
context.perform(action, operation.annotation());
};
@@ -67,9 +67,14 @@ export function operationReflect(selectedIDs, context, axis) {
};
operation.annotation = function() {
return t('operations.reflect.annotation.' + axis + '.' + multi);
};
operation.id = 'reflect-' + axis;
operation.keys = [t('operations.reflect.key.' + axis)];
operation.title = t('operations.reflect.title');
operation.title = t('operations.reflect.title.' + axis);
operation.behavior = behaviorOperation(context).which(operation);
return operation;
+6 -1
View File
@@ -7,7 +7,7 @@ export function operationReverse(selectedIDs, context) {
var entityId = selectedIDs[0];
var operation = function() {
context.perform(actionReverse(entityId), t('operations.reverse.annotation'));
context.perform(actionReverse(entityId), operation.annotation());
};
@@ -26,6 +26,11 @@ export function operationReverse(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.reverse.annotation');
};
operation.id = 'reverse';
operation.keys = [t('operations.reverse.key')];
operation.title = t('operations.reverse.title');
+7
View File
@@ -54,6 +54,13 @@ export function operationRotate(selectedIDs, context) {
};
operation.annotation = function() {
return selectedIDs.length === 1 ?
t('operations.rotate.annotation.' + context.geometry(selectedIDs[0])) :
t('operations.rotate.annotation.multiple');
};
operation.id = 'rotate';
operation.keys = [t('operations.rotate.key')];
operation.title = t('operations.rotate.title');
+15 -15
View File
@@ -11,24 +11,19 @@ export function operationSplit(selectedIDs, context) {
});
var entityId = vertices[0],
action = actionSplit(entityId);
action = actionSplit(entityId),
ways = [];
if (selectedIDs.length > 1) {
action.limitWays(_.without(selectedIDs, entityId));
if (vertices.length === 1) {
if (selectedIDs.length > 1) {
action.limitWays(_.without(selectedIDs, entityId));
}
ways = action.ways(context.graph());
}
var operation = function() {
var annotation;
var ways = action.ways(context.graph());
if (ways.length === 1) {
annotation = t('operations.split.annotation.' + context.geometry(ways[0].id));
} else {
annotation = t('operations.split.annotation.multiple', {n: ways.length});
}
var difference = context.perform(action, annotation);
var difference = context.perform(action, operation.annotation());
context.enter(modeSelect(context, difference.extantIDs()));
};
@@ -52,8 +47,6 @@ export function operationSplit(selectedIDs, context) {
if (disable) {
return t('operations.split.' + disable);
}
var ways = action.ways(context.graph());
if (ways.length === 1) {
return t('operations.split.description.' + context.geometry(ways[0].id));
} else {
@@ -62,6 +55,13 @@ export function operationSplit(selectedIDs, context) {
};
operation.annotation = function() {
return ways.length === 1 ?
t('operations.split.annotation.' + context.geometry(ways[0].id)) :
t('operations.split.annotation.multiple', { n: ways.length });
};
operation.id = 'split';
operation.keys = [t('operations.split.key')];
operation.title = t('operations.split.title');
+6 -1
View File
@@ -10,7 +10,7 @@ export function operationStraighten(selectedIDs, context) {
function operation() {
context.perform(action, t('operations.straighten.annotation'));
context.perform(action, operation.annotation());
}
@@ -40,6 +40,11 @@ export function operationStraighten(selectedIDs, context) {
};
operation.annotation = function() {
return t('operations.straighten.annotation');
};
operation.id = 'straighten';
operation.keys = [t('operations.straighten.key')];
operation.title = t('operations.straighten.title');
+32 -9
View File
@@ -36,6 +36,7 @@ export function rendererMap(context) {
dblclickEnabled = true,
redrawEnabled = true,
transformStart = projection.transform(),
transformLast,
transformed = false,
minzoom = 0,
drawLayers = svgLayers(projection, context),
@@ -77,7 +78,7 @@ export function rendererMap(context) {
if (Array.isArray(stack.selectedIDs)) {
followSelected = (stack.selectedIDs.length === 1 && stack.selectedIDs[0][0] === 'n');
context.enter(
modeSelect(context, stack.selectedIDs).suppressMenu(true).follow(followSelected)
modeSelect(context, stack.selectedIDs).follow(followSelected)
);
}
if (!followSelected && stack.transform) {
@@ -275,7 +276,9 @@ export function rendererMap(context) {
function zoomPan(manualEvent) {
var eventTransform = (manualEvent || d3.event).transform;
var event = (manualEvent || d3.event),
source = event.sourceEvent,
eventTransform = event.transform;
if (transformStart.x === eventTransform.x &&
transformStart.y === eventTransform.y &&
@@ -283,11 +286,31 @@ export function rendererMap(context) {
return; // no change
}
// Normalize mousewheel - #3029
// If wheel delta is provided in LINE units, recalculate it in PIXEL units
// We are essentially redoing the calculations that occur here:
// https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
// See this for more info:
// https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
if (source && source.type === 'wheel' && source.deltaMode === 1 /* LINE */) {
// pick sensible scroll amount if user scrolling fast or slow..
var lines = Math.abs(source.deltaY),
scroll = lines > 2 ? 40 : lines * 10;
var t0 = transformed ? transformLast : transformStart,
p0 = mouse(source),
p1 = t0.invert(p0),
k2 = t0.k * Math.pow(2, -source.deltaY * scroll / 500),
x2 = p0[0] - p1[0] * k2,
y2 = p0[1] - p1[1] * k2;
eventTransform = d3.zoomIdentity.translate(x2,y2).scale(k2);
_selection.node().__zoom = eventTransform;
}
if (ktoz(eventTransform.k * 2 * Math.PI) < minzoom) {
surface.interrupt();
uiFlash(context.container())
.select('.content')
.text(t('cannot_zoom'));
uiFlash().text(t('cannot_zoom'));
setZoom(context.minEditableZoom(), true);
queueRedraw();
dispatch.call('move', this, map);
@@ -301,6 +324,7 @@ export function rendererMap(context) {
tY = (eventTransform.y / scale - transformStart.y) * scale;
transformed = true;
transformLast = eventTransform;
utilSetTransform(supersurface, tX, tY, scale);
queueRedraw();
@@ -311,7 +335,8 @@ export function rendererMap(context) {
function resetTransform() {
if (!transformed) return false;
surface.selectAll('.radial-menu').interrupt().remove();
// deprecation warning - Radial Menu to be removed in iD v3
surface.selectAll('.edit-menu, .radial-menu').interrupt().remove();
utilSetTransform(supersurface, 0, 0);
transformed = false;
return true;
@@ -569,9 +594,7 @@ export function rendererMap(context) {
if (z2 < minzoom) {
surface.interrupt();
uiFlash(context.container())
.select('.content')
.text(t('cannot_zoom'));
uiFlash().text(t('cannot_zoom'));
z2 = context.minEditableZoom();
}
+2 -5
View File
@@ -274,9 +274,7 @@ export function uiCommit(context) {
function warningClick(d) {
if (d.entity) {
context.map().zoomTo(d.entity);
context.enter(
modeSelect(context, [d.entity.id]).suppressMenu(true)
);
context.enter(modeSelect(context, [d.entity.id]));
}
}
@@ -286,8 +284,7 @@ export function uiCommit(context) {
if (change.changeType !== 'deleted' &&
context.graph().entity(entity.id).geometry(context.graph()) !== 'vertex') {
context.map().zoomTo(entity);
context.surface().selectAll(
utilEntityOrMemberSelector([entity.id], context.graph()))
context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
.classed('hover', true);
}
}
+180
View File
@@ -0,0 +1,180 @@
import * as d3 from 'd3';
import { geoRoundCoords } from '../geo/index';
import { textDirection } from '../util/locale';
import { uiTooltipHtml } from './tooltipHtml';
export function uiEditMenu(context, operations) {
var menu,
center = [0, 0],
offset = [0, 0],
tooltip;
var p = 8, // top padding
m = 4, // top margin
h = 15, // height of icon
vpBottomMargin = 45, // viewport bottom margin
vpSideMargin = 35, // viewport side margin
buttonWidth = 44,
buttonHeight = (2 * p + h),
menuWidth = buttonWidth,
menuHeight = (2 * m) + operations.length * buttonHeight,
menuSideMargin = 10,
tooltipWidth = 200,
tooltipHeight = 200; // a reasonable guess, real height depends on tooltip contents
var editMenu = function (selection) {
if (!operations.length) return;
selection.node().parentNode.focus();
var isRTL = textDirection === 'rtl',
viewport = context.surfaceRect();
if (!isRTL && (center[0] + menuSideMargin + menuWidth) > (viewport.width - vpSideMargin)) {
// menu is going left-to-right and near right viewport edge, go left instead
isRTL = true;
} else if (isRTL && (center[0] - menuSideMargin - menuWidth) < vpSideMargin) {
// menu is going right-to-left and near left viewport edge, go right instead
isRTL = false;
}
offset[0] = (isRTL ? -1 * (menuSideMargin + menuWidth) : menuSideMargin);
if (center[1] + menuHeight > (viewport.height - vpBottomMargin)) {
// menu is near bottom viewport edge, shift upwards
offset[1] = -1 * (center[1] + menuHeight - viewport.height + vpBottomMargin);
}
var origin = [ center[0] + offset[0], center[1] + offset[1] ];
menu = selection
.append('g')
.attr('class', 'edit-menu')
.attr('transform', 'translate(' + origin + ')')
.attr('opacity', 0);
menu
.transition()
.attr('opacity', 1);
menu
.append('rect')
.attr('class', 'edit-menu-background')
.attr('x', 4)
.attr('rx', 4)
.attr('ry', 4)
.attr('width', menuWidth)
.attr('height', menuHeight)
.attr('stroke-linecap', 'round');
var button = menu.selectAll('.edit-menu-item')
.data(operations)
.enter()
.append('g')
.attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
.classed('disabled', function (d) { return d.disabled(); })
.attr('transform', function (d, i) {
return 'translate(' + geoRoundCoords([
0,
m + i * buttonHeight
]).join(',') + ')';
});
button
.append('rect')
.attr('x', 4)
.attr('width', buttonWidth)
.attr('height', buttonHeight)
.on('click', click)
.on('mousedown', mousedown)
.on('mouseover', mouseover)
.on('mouseout', mouseout);
button
.append('use')
.attr('width', '20')
.attr('height', '20')
.attr('transform', function () {
return 'translate(' + [2 * p, 5] + ')';
})
.attr('xlink:href', function (d) { return '#operation-' + d.id; });
tooltip = d3.select(document.body)
.append('div')
.attr('class', 'tooltip-inner edit-menu-tooltip');
function click(operation) {
d3.event.stopPropagation();
if (operation.disabled()) return;
operation();
editMenu.close();
}
function mousedown() {
d3.event.stopPropagation(); // https://github.com/openstreetmap/iD/issues/1869
}
function mouseover(d, i) {
var tipX, tipY;
if (!isRTL) {
tipX = viewport.left + origin[0] + menuSideMargin + menuWidth;
} else {
tipX = viewport.left + origin[0] - 4 - tooltipWidth;
}
if (tipX + tooltipWidth > viewport.right) {
// tip is going left-to-right and near right viewport edge, go left instead
tipX = viewport.left + origin[0] - 4 - tooltipWidth;
} else if (tipX < viewport.left) {
// tip is going right-to-left and near left viewport edge, go right instead
tipX = viewport.left + origin[0] + menuSideMargin + menuWidth;
}
tipY = viewport.top + origin[1] + (i * buttonHeight);
if (tipY + tooltipHeight > viewport.bottom) {
// tip is near bottom viewport edge, shift upwards
tipY -= tipY + tooltipHeight - viewport.bottom;
}
tooltip
.style('left', tipX + 'px')
.style('top', tipY + 'px')
.style('display', 'block')
.html(uiTooltipHtml(d.tooltip(), d.keys[0], d.title));
}
function mouseout() {
tooltip.style('display', 'none');
}
};
editMenu.close = function () {
if (menu) {
menu
.style('pointer-events', 'none')
.transition()
.attr('opacity', 0)
.remove();
}
if (tooltip) {
tooltip.remove();
}
};
editMenu.center = function (_) {
if (!arguments.length) return center;
center = _;
return editMenu;
};
return editMenu;
}
+1 -1
View File
@@ -272,7 +272,7 @@ export function uiFeatureList(context) {
edge = geoChooseEdge(context.childNodes(d.entity), center, context.projection);
context.map().center(edge.loc);
}
context.enter(modeSelect(context, [d.entity.id]).suppressMenu(true));
context.enter(modeSelect(context, [d.entity.id]));
} else {
context.zoomToEntity(d.id);
}
+26 -17
View File
@@ -1,26 +1,35 @@
import { uiModal } from './modal';
import * as d3 from 'd3';
var timer;
export function uiFlash(selection) {
var modalSelection = uiModal(selection);
export function uiFlash(showDuration) {
showDuration = showDuration || 1500;
modalSelection.select('.modal')
.classed('modal-flash', true);
if (timer) {
timer.stop();
}
modalSelection.select('.content')
.classed('modal-section', true)
d3.select('#footer-wrap')
.attr('class', 'footer-hide');
d3.select('#flash-wrap')
.attr('class', 'footer-show');
var content = d3.select('#flash-wrap').selectAll('.content')
.data([0]);
content = content.enter()
.append('div')
.attr('class', 'description');
.attr('class', 'content')
.merge(content);
modalSelection.on('click.flash', function() {
modalSelection.remove();
});
setTimeout(function() {
modalSelection.remove();
return true;
}, 1500);
timer = d3.timeout(function() {
timer = null;
d3.select('#footer-wrap')
.attr('class', 'footer-show');
d3.select('#flash-wrap')
.attr('class', 'footer-hide');
}, showDuration);
return modalSelection;
return content;
}
+2
View File
@@ -47,3 +47,5 @@ export { uiTooltipHtml } from './tooltipHtml';
export { uiUndoRedo } from './undo_redo';
export { uiViewOnOSM } from './view_on_osm';
export { uiZoom } from './zoom';
export { uiEditMenu } from './edit_menu';
+17 -4
View File
@@ -116,6 +116,7 @@ export function uiInit(context) {
.attr('class', 'spinner')
.call(uiSpinner(context));
var controls = bar
.append('div')
.attr('class', 'map-controls');
@@ -145,6 +146,7 @@ export function uiInit(context) {
.attr('class', 'map-control help-control')
.call(uiHelp(context));
var about = content
.append('div')
.attr('id', 'about');
@@ -155,6 +157,12 @@ export function uiInit(context) {
.attr('dir', 'ltr')
.call(uiAttribution(context));
about
.append('div')
.attr('class', 'api-status')
.call(uiStatus(context));
var footer = about
.append('div')
.attr('id', 'footer')
@@ -162,15 +170,20 @@ export function uiInit(context) {
footer
.append('div')
.attr('class', 'api-status')
.call(uiStatus(context));
.attr('id', 'flash-wrap')
.attr('class', 'footer-hide');
footer
var footerWrap = footer
.append('div')
.attr('id', 'footer-wrap')
.attr('class', 'footer-show');
footerWrap
.append('div')
.attr('id', 'scale-block')
.call(uiScale(context));
var aboutList = footer
var aboutList = footerWrap
.append('div')
.attr('id', 'info-block')
.append('ul')
+12 -7
View File
@@ -31,7 +31,7 @@ export function uiIntroPoint(context, reveal) {
t('intro.points.add', { button: icon('#icon-point', 'pre-text') }),
{ tooltipClass: 'intro-points-add' });
var corner = [-85.632481,41.944094];
var corner = [-85.632481, 41.944094];
context.on('enter.intro', addPoint);
@@ -125,11 +125,11 @@ export function uiIntroPoint(context, reveal) {
context.on('enter.intro', enterDelete);
var pointBox = pad(corner, 150, context);
reveal(pointBox, t('intro.points.reselect_delete'));
reveal(pointBox, t('intro.points.rightclick'));
context.map().on('move.intro', function() {
pointBox = pad(corner, 150, context);
reveal(pointBox, t('intro.points.reselect_delete'), {duration: 0});
reveal(pointBox, t('intro.points.rightclick'), {duration: 0});
});
}
@@ -143,10 +143,15 @@ export function uiIntroPoint(context, reveal) {
context.history().on('change.intro', deleted);
setTimeout(function() {
var node = d3.select('.radial-menu-item-delete').node();
var pointBox = pad(node.getBoundingClientRect(), 50, context);
reveal(pointBox,
t('intro.points.delete', { button: icon('#operation-delete', 'pre-text') }));
// deprecation warning - Radial Menu to be removed in iD v3
var node = d3.select('.edit-menu-item-delete, .radial-menu-item-delete').node();
if (!node) {
deletePoint();
} else {
var pointBox = pad(node.getBoundingClientRect(), 50, context);
reveal(pointBox,
t('intro.points.delete', { button: icon('#operation-delete', 'pre-text') }));
}
}, 300);
}
+1 -1
View File
@@ -21,7 +21,7 @@ export function uiRawMemberEditor(context) {
function selectMember(d) {
d3.event.preventDefault();
context.enter(modeSelect(context, [d.id]).suppressMenu(true));
context.enter(modeSelect(context, [d.id]));
}
+2 -2
View File
@@ -25,7 +25,7 @@ export function uiRawMembershipEditor(context) {
function selectRelation(d) {
d3.event.preventDefault();
context.enter(modeSelect(context, [d.relation.id]).suppressMenu(true));
context.enter(modeSelect(context, [d.relation.id]));
}
@@ -55,7 +55,7 @@ export function uiRawMembershipEditor(context) {
t('operations.add.annotation.relation')
);
context.enter(modeSelect(context, [relation.id]).suppressMenu(true));
context.enter(modeSelect(context, [relation.id]));
}
}
+15 -7
View File
@@ -63,12 +63,13 @@ export function uiScale(context) {
loc2 = projection.invert([maxLength, dims[1]]),
scale = scaleDefs(loc1, loc2);
selection.select('#scalepath')
selection.select('#scale-path')
.attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
selection.select('#scaletext')
.attr('x', scale.px + 8)
.attr('y', tickHeight)
selection.select('#scale-textgroup')
.attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
selection.select('#scale-text')
.text(scale.text);
}
@@ -79,14 +80,21 @@ export function uiScale(context) {
selection.call(update);
}
var g = selection.append('svg')
var scalegroup = selection.append('svg')
.attr('id', 'scale')
.on('click', switchUnits)
.append('g')
.attr('transform', 'translate(10,11)');
g.append('path').attr('id', 'scalepath');
g.append('text').attr('id', 'scaletext');
scalegroup
.append('path')
.attr('id', 'scale-path');
scalegroup
.append('g')
.attr('id', 'scale-textgroup')
.append('text')
.attr('id', 'scale-text');
selection.call(update);
+2 -2
View File
@@ -9,7 +9,7 @@ import { utilDisplayName } from '../util/index';
export function uiSelectionList(context, selectedIDs) {
function selectEntity(entity) {
context.enter(modeSelect(context, [entity.id]).suppressMenu(true));
context.enter(modeSelect(context, [entity.id]));
}
@@ -19,7 +19,7 @@ export function uiSelectionList(context, selectedIDs) {
if (index > -1) {
selectedIDs.splice(index, 1);
}
context.enter(modeSelect(context, selectedIDs).suppressMenu(true));
context.enter(modeSelect(context, selectedIDs));
}
+13 -6
View File
@@ -1,11 +1,18 @@
import { t } from '../util/locale';
export function uiTooltipHtml(text, key) {
var s = '<span>' + text + '</span>';
if (key) {
s += '<div class="keyhint-wrap">' +
'<span> ' + (t('tooltip_keyhint')) + ' </span>' +
'<span class="keyhint"> ' + key + '</span></div>';
export function uiTooltipHtml(text, key, heading) {
var s = '';
if (heading) {
s += '<div class="tooltip-heading"><span>' + heading + '</span></div>';
}
if (text) {
s += '<div class="tooltip-text"><span>' + text + '</span></div>';
}
if (key) {
s += '<div class="keyhint-wrap"><span>' + t('tooltip_keyhint') + '</span>' +
'<span class="keyhint">' + key + '</span></div>';
}
return s;
}
+5 -5
View File
@@ -43,14 +43,14 @@ describe('iD.behaviorHash', function () {
location.hash = 'map=20.00/38.87952/-77.02405';
});
it('stores the current zoom and coordinates in location.hash on map move events', function () {
it('stores the current zoom and coordinates in location.hash on map move events', function (done) {
location.hash = '';
hash();
var clock = sinon.useFakeTimers();
context.map().center([-77.0, 38.9]);
context.map().zoom(2.0);
clock.tick(500);
expect(location.hash).to.equal('#map=2.00/38.9/-77.0');
clock.restore();
window.setTimeout(function() {
expect(location.hash).to.equal('#map=2.00/38.9/-77.0');
done();
}, 300);
});
});
+18 -6
View File
@@ -44,37 +44,49 @@ describe('iD.behaviorSelect', function() {
});
specify('click on entity selects the entity', function() {
happen.click(context.surface().selectAll('.' + a.id).node());
var el = context.surface().selectAll('.' + a.id).node();
happen.mousedown(el);
happen.mouseup(el);
expect(context.selectedIDs()).to.eql([a.id]);
});
specify('click on empty space clears the selection', function() {
context.enter(iD.modeSelect(context, [a.id]));
happen.click(context.surface().node());
var el = context.surface().node();
happen.mousedown(el);
happen.mouseup(el);
expect(context.mode().id).to.eql('browse');
});
specify('shift-click on unselected entity adds it to the selection', function() {
context.enter(iD.modeSelect(context, [a.id]));
happen.click(context.surface().selectAll('.' + b.id).node(), {shiftKey: true});
var el = context.surface().selectAll('.' + b.id).node();
happen.mousedown(el, { shiftKey: true });
happen.mouseup(el, { shiftKey: true });
expect(context.selectedIDs()).to.eql([a.id, b.id]);
});
specify('shift-click on selected entity removes it from the selection', function() {
context.enter(iD.modeSelect(context, [a.id, b.id]));
happen.click(context.surface().selectAll('.' + b.id).node(), {shiftKey: true});
var el = context.surface().selectAll('.' + b.id).node();
happen.mousedown(el, { shiftKey: true });
happen.mouseup(el, { shiftKey: true });
expect(context.selectedIDs()).to.eql([a.id]);
});
specify('shift-click on last selected entity clears the selection', function() {
context.enter(iD.modeSelect(context, [a.id]));
happen.click(context.surface().selectAll('.' + a.id).node(), {shiftKey: true});
var el = context.surface().selectAll('.' + a.id).node();
happen.mousedown(el, { shiftKey: true });
happen.mouseup(el, { shiftKey: true });
expect(context.mode().id).to.eql('browse');
});
specify('shift-click on empty space leaves the selection unchanged', function() {
context.enter(iD.modeSelect(context, [a.id]));
happen.click(context.surface().node(), {shiftKey: true});
var el = context.surface().node();
happen.mousedown(el, { shiftKey: true });
happen.mouseup(el, { shiftKey: true });
expect(context.selectedIDs()).to.eql([a.id]);
});
});
+30 -15
View File
@@ -1,25 +1,40 @@
describe('iD.uiFlash', function () {
var clock;
var elem;
beforeEach(function() {
elem = d3.select('body').append('div');
});
afterEach(function() { elem.remove(); });
beforeEach(function () {
clock = sinon.useFakeTimers();
d3.select('body')
.append('div')
.attr('id', 'flash-wrap')
.append('div')
.attr('id', 'footer-wrap');
});
afterEach(function () {
clock.restore();
d3.select('body > div').remove();
});
it('leaves after 1000 ms', function () {
var flash = iD.uiFlash(elem);
clock.tick(1610);
expect(flash.node().parentNode).to.be.null;
it('returns a selection', function () {
var content = iD.uiFlash(200);
expect(content.size()).to.eql(1);
expect(content.classed('content')).to.be.ok;
});
it('flash is shown', function () {
iD.uiFlash(200);
var flashWrap = d3.selectAll('#flash-wrap');
var footerWrap = d3.selectAll('#footer-wrap');
expect(flashWrap.classed('footer-show')).to.be.ok;
expect(footerWrap.classed('footer-hide')).to.be.ok;
});
it('flash goes away', function (done) {
iD.uiFlash(200);
window.setTimeout(function() {
var flashWrap = d3.selectAll('#flash-wrap');
var footerWrap = d3.selectAll('#footer-wrap');
expect(flashWrap.classed('footer-hide')).to.be.ok;
expect(footerWrap.classed('footer-show')).to.be.ok;
done();
}, 500);
});
});
+1 -6
View File
@@ -1,12 +1,7 @@
describe('iD.utilSessionMutex', function() {
var clock, a, b;
beforeEach(function () {
clock = sinon.useFakeTimers(Date.now());
});
var a, b;
afterEach(function() {
clock.restore();
if (a) a.unlock();
if (b) b.unlock();
});