mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-12 16:52:50 +00:00
Move the edit menu logic to uiInit
Make context the first argument of operation objects Add Paste operation to edit menu when opening the context menu on a blank area of the map (close #2508)
This commit is contained in:
@@ -88,6 +88,15 @@ en:
|
||||
annotation:
|
||||
single: Copied a feature.
|
||||
multiple: "Copied {n} features."
|
||||
paste:
|
||||
title: Paste
|
||||
description:
|
||||
single: "Add a duplicate {feature} here."
|
||||
multiple: "Add {n} duplicate features here."
|
||||
annotation:
|
||||
single: Pasted a feature.
|
||||
multiple: "Pasted {n} features."
|
||||
nothing_copied: No features have been copied.
|
||||
circularize:
|
||||
title: Circularize
|
||||
description:
|
||||
|
||||
12
dist/locales/en.json
vendored
12
dist/locales/en.json
vendored
@@ -114,6 +114,18 @@
|
||||
"multiple": "Copied {n} features."
|
||||
}
|
||||
},
|
||||
"paste": {
|
||||
"title": "Paste",
|
||||
"description": {
|
||||
"single": "Add a duplicate {feature} here.",
|
||||
"multiple": "Add {n} duplicate features here."
|
||||
},
|
||||
"annotation": {
|
||||
"single": "Pasted a feature.",
|
||||
"multiple": "Pasted {n} features."
|
||||
},
|
||||
"nothing_copied": "No features have been copied."
|
||||
},
|
||||
"circularize": {
|
||||
"title": "Circularize",
|
||||
"description": {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { geoExtent, geoPointInPolygon, geoVecSubtract } from '../geo';
|
||||
import { modeMove } from '../modes/move';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
|
||||
|
||||
// see also `operationPaste`
|
||||
export function behaviorPaste(context) {
|
||||
|
||||
function doPaste() {
|
||||
@@ -22,13 +22,13 @@ export function behaviorPaste(context) {
|
||||
|
||||
if (!geoPointInPolygon(mouse, viewport)) return;
|
||||
|
||||
var extent = geoExtent();
|
||||
var oldIDs = context.copyIDs();
|
||||
if (!oldIDs.length) return;
|
||||
|
||||
var extent = geoExtent();
|
||||
var oldGraph = context.copyGraph();
|
||||
var newIDs = [];
|
||||
|
||||
if (!oldIDs.length) return;
|
||||
|
||||
var action = actionCopyEntities(oldIDs, oldGraph);
|
||||
context.perform(action);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { event as d3_event, select as d3_select } from 'd3-selection';
|
||||
|
||||
import { geoVecLength } from '../geo';
|
||||
import { prefs } from '../core/preferences';
|
||||
import { modeBrowse } from '../modes/browse';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { modeSelectData } from '../modes/select_data';
|
||||
@@ -12,8 +11,6 @@ import { utilFastMouse } from '../util/util';
|
||||
|
||||
|
||||
export function behaviorSelect(context) {
|
||||
// legacy option to show menu on every click
|
||||
var _alwaysShowMenu = +prefs('edit-menu-show-always') === 1;
|
||||
var _tolerancePx = 4;
|
||||
var _lastPointerEvent = null;
|
||||
var _showMenu = false;
|
||||
@@ -35,7 +32,7 @@ export function behaviorSelect(context) {
|
||||
function keydown() {
|
||||
|
||||
if (d3_event.keyCode === 32) {
|
||||
// don't react to spacebar events during text input
|
||||
// don't react to spacebar events during text input
|
||||
var activeNode = document.activeElement;
|
||||
if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
|
||||
}
|
||||
@@ -80,7 +77,6 @@ export function behaviorSelect(context) {
|
||||
} else if (d3_event.keyCode === 32) { // spacebar
|
||||
d3_event.preventDefault();
|
||||
_lastInteractionType = 'spacebar';
|
||||
_showMenu = _alwaysShowMenu;
|
||||
click();
|
||||
}
|
||||
}
|
||||
@@ -98,8 +94,6 @@ export function behaviorSelect(context) {
|
||||
|
||||
d3_select(window)
|
||||
.on(_pointerPrefix + 'up.select', pointerup, true);
|
||||
|
||||
_showMenu = _alwaysShowMenu;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +170,7 @@ export function behaviorSelect(context) {
|
||||
if (entity) datum = entity;
|
||||
|
||||
if (datum && datum.type === 'midpoint') {
|
||||
// treat targeting midpoints as if targeting the parent way
|
||||
datum = datum.parents[0];
|
||||
}
|
||||
|
||||
@@ -187,36 +182,27 @@ export function behaviorSelect(context) {
|
||||
context.selectedErrorID(null);
|
||||
|
||||
if (!isMultiselect) {
|
||||
if (selectedIDs.length > 1 && (_showMenu && !_alwaysShowMenu)) {
|
||||
// multiple things already selected, just show the menu...
|
||||
mode.reselect().showMenu(point, _lastInteractionType);
|
||||
} else {
|
||||
if (selectedIDs.length <= 1 || !_showMenu) {
|
||||
// always enter modeSelect even if the entity is already
|
||||
// selected since listeners may expect `context.enter` events,
|
||||
// e.g. in the walkthrough
|
||||
newMode = modeSelect(context, [datum.id]);
|
||||
context.enter(newMode);
|
||||
if (_showMenu) newMode.showMenu(point, _lastInteractionType);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (selectedIDs.indexOf(datum.id) !== -1) {
|
||||
// clicked entity is already in the selectedIDs list..
|
||||
if (_showMenu && !_alwaysShowMenu) {
|
||||
// don't deselect clicked entity, just show the menu.
|
||||
mode.reselect().showMenu(point, _lastInteractionType);
|
||||
} else {
|
||||
if (!_showMenu) {
|
||||
// deselect clicked entity, then reenter select mode or return to browse mode..
|
||||
selectedIDs = selectedIDs.filter(function(id) { return id !== 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]);
|
||||
newMode = modeSelect(context, selectedIDs);
|
||||
context.enter(newMode);
|
||||
if (_showMenu) newMode.showMenu(point, _lastInteractionType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,6 +229,11 @@ export function behaviorSelect(context) {
|
||||
}
|
||||
}
|
||||
|
||||
context.ui().closeEditMenu();
|
||||
|
||||
// always request to show the edit menu in case the mode needs it
|
||||
if (_showMenu) context.ui().showEditMenu(point, _lastInteractionType);
|
||||
|
||||
resetProperties();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { behaviorSelect } from '../behavior/select';
|
||||
import { modeDragNode } from './drag_node';
|
||||
import { modeDragNote } from './drag_note';
|
||||
|
||||
import { operationPaste } from '../operations/paste';
|
||||
|
||||
export function modeBrowse(context) {
|
||||
var mode = {
|
||||
@@ -60,5 +61,10 @@ export function modeBrowse(context) {
|
||||
};
|
||||
|
||||
|
||||
mode.operations = function() {
|
||||
return [operationPaste(context)];
|
||||
};
|
||||
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ export function modeMove(context, entityIDs, baseGraph) {
|
||||
var keybinding = utilKeybinding('move');
|
||||
var behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior,
|
||||
operationRotate(entityIDs, context).behavior
|
||||
operationCircularize(context, entityIDs).behavior,
|
||||
operationDelete(context, entityIDs).behavior,
|
||||
operationOrthogonalize(context, entityIDs).behavior,
|
||||
operationReflectLong(context, entityIDs).behavior,
|
||||
operationReflectShort(context, entityIDs).behavior,
|
||||
operationRotate(context, entityIDs).behavior
|
||||
];
|
||||
var annotation = entityIDs.length === 1 ?
|
||||
t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) :
|
||||
|
||||
@@ -34,12 +34,12 @@ export function modeRotate(context, entityIDs) {
|
||||
var keybinding = utilKeybinding('rotate');
|
||||
var behaviors = [
|
||||
behaviorEdit(context),
|
||||
operationCircularize(entityIDs, context).behavior,
|
||||
operationDelete(entityIDs, context).behavior,
|
||||
operationMove(entityIDs, context).behavior,
|
||||
operationOrthogonalize(entityIDs, context).behavior,
|
||||
operationReflectLong(entityIDs, context).behavior,
|
||||
operationReflectShort(entityIDs, context).behavior
|
||||
operationCircularize(context, entityIDs).behavior,
|
||||
operationDelete(context, entityIDs).behavior,
|
||||
operationMove(context, entityIDs).behavior,
|
||||
operationOrthogonalize(context, entityIDs).behavior,
|
||||
operationReflectLong(context, entityIDs).behavior,
|
||||
operationReflectShort(context, entityIDs).behavior
|
||||
];
|
||||
var annotation = entityIDs.length === 1 ?
|
||||
t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) :
|
||||
|
||||
@@ -17,7 +17,6 @@ import { modeDragNode } from './drag_node';
|
||||
import { modeDragNote } from './drag_note';
|
||||
import { osmNode, osmWay } from '../osm';
|
||||
import * as Operations from '../operations/index';
|
||||
import { uiEditMenu } from '../ui/edit_menu';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
import {
|
||||
utilArrayIntersection, utilDeepMemberSelector, utilEntityOrDeepMemberSelector,
|
||||
@@ -46,7 +45,6 @@ export function modeSelect(context, selectedIDs) {
|
||||
modeDragNote(context).behavior
|
||||
];
|
||||
var inspector; // unused?
|
||||
var _editMenu; // uiEditMenu
|
||||
var _newFeature = false;
|
||||
var _follow = false;
|
||||
|
||||
@@ -141,44 +139,6 @@ export function modeSelect(context, selectedIDs) {
|
||||
}
|
||||
|
||||
|
||||
function closeMenu() {
|
||||
// remove any existing menu no matter how it was added
|
||||
context.map().supersurface
|
||||
.select('.edit-menu').remove();
|
||||
}
|
||||
|
||||
mode.showMenu = function(anchorPoint, triggerType) {
|
||||
|
||||
// remove any displayed menu
|
||||
closeMenu();
|
||||
|
||||
// disable menu if in wide selection, for example
|
||||
if (!context.map().editableDataEnabled()) return;
|
||||
|
||||
// don't show the menu for relations alone
|
||||
if (selectedIDs.every(function(id) {
|
||||
return context.graph().geometry(id) === 'relation';
|
||||
})) return;
|
||||
|
||||
var surfaceNode = context.surface().node();
|
||||
if (surfaceNode.focus) { // FF doesn't support it
|
||||
// focus the surface or else clicking off the menu may not trigger modeBrowse
|
||||
surfaceNode.focus();
|
||||
}
|
||||
|
||||
// don't load the menu until it's needed
|
||||
if (!_editMenu) _editMenu = uiEditMenu(context);
|
||||
|
||||
_editMenu
|
||||
.anchorLoc(anchorPoint)
|
||||
.triggerType(triggerType)
|
||||
.operations(operations);
|
||||
|
||||
// render the menu
|
||||
context.map().supersurface.call(_editMenu);
|
||||
};
|
||||
|
||||
|
||||
mode.selectedIDs = function() {
|
||||
return selectedIDs;
|
||||
};
|
||||
@@ -189,12 +149,6 @@ export function modeSelect(context, selectedIDs) {
|
||||
};
|
||||
|
||||
|
||||
mode.reselect = function() {
|
||||
if (!checkSelectedIDs()) return;
|
||||
return mode;
|
||||
};
|
||||
|
||||
|
||||
mode.newFeature = function(val) {
|
||||
if (!arguments.length) return _newFeature;
|
||||
_newFeature = val;
|
||||
@@ -219,12 +173,12 @@ export function modeSelect(context, selectedIDs) {
|
||||
});
|
||||
|
||||
operations = Object.values(Operations)
|
||||
.map(function(o) { return o(selectedIDs, context); })
|
||||
.map(function(o) { return o(context, selectedIDs); })
|
||||
.filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade'; });
|
||||
|
||||
var downgradeOperation = Operations.operationDowngrade(selectedIDs, context);
|
||||
var downgradeOperation = Operations.operationDowngrade(context, selectedIDs);
|
||||
// don't allow delete if downgrade is available
|
||||
var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : Operations.operationDelete(selectedIDs, context);
|
||||
var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : Operations.operationDelete(context, selectedIDs);
|
||||
|
||||
operations.push(lastOperation);
|
||||
|
||||
@@ -235,9 +189,13 @@ export function modeSelect(context, selectedIDs) {
|
||||
});
|
||||
|
||||
// remove any displayed menu
|
||||
closeMenu();
|
||||
context.ui().closeEditMenu();
|
||||
}
|
||||
|
||||
mode.operations = function() {
|
||||
return operations;
|
||||
};
|
||||
|
||||
|
||||
mode.enter = function() {
|
||||
if (!checkSelectedIDs()) return;
|
||||
@@ -269,11 +227,10 @@ export function modeSelect(context, selectedIDs) {
|
||||
// reselect after change in case relation members were removed or added
|
||||
selectElements();
|
||||
})
|
||||
.on('undone.select', update)
|
||||
.on('redone.select', update);
|
||||
.on('undone.select', checkSelectedIDs)
|
||||
.on('redone.select', checkSelectedIDs);
|
||||
|
||||
context.map()
|
||||
.on('move.select', closeMenu)
|
||||
.on('drawn.select', selectElements)
|
||||
.on('crossEditableZoom.select', function() {
|
||||
selectElements();
|
||||
@@ -301,12 +258,6 @@ export function modeSelect(context, selectedIDs) {
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
closeMenu();
|
||||
checkSelectedIDs();
|
||||
}
|
||||
|
||||
|
||||
function didDoubleUp(loc) {
|
||||
if (!context.map().withinEditableZoom()) return;
|
||||
|
||||
@@ -500,7 +451,7 @@ export function modeSelect(context, selectedIDs) {
|
||||
d3_select(document)
|
||||
.call(keybinding.unbind);
|
||||
|
||||
closeMenu();
|
||||
context.ui().closeEditMenu();
|
||||
|
||||
context.history()
|
||||
.on('change.select', null)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationCircularize(selectedIDs, context) {
|
||||
export function operationCircularize(context, selectedIDs) {
|
||||
var _extent;
|
||||
var _actions = selectedIDs.map(getAction).filter(Boolean);
|
||||
var _amount = _actions.length === 1 ? 'single' : 'multiple';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { utilArrayGroupBy } from '../util';
|
||||
|
||||
|
||||
export function operationContinue(selectedIDs, context) {
|
||||
export function operationContinue(context, selectedIDs) {
|
||||
var graph = context.graph();
|
||||
var entities = selectedIDs.map(function(id) { return graph.entity(id); });
|
||||
var geometries = Object.assign(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
import { utilArrayGroupBy } from '../util';
|
||||
|
||||
export function operationCopy(selectedIDs, context) {
|
||||
export function operationCopy(context, selectedIDs) {
|
||||
|
||||
function getFilteredIdsToCopy() {
|
||||
return selectedIDs.filter(function(selectedID) {
|
||||
@@ -112,7 +112,7 @@ export function operationCopy(selectedIDs, context) {
|
||||
operation.annotation = function() {
|
||||
return selectedIDs.length === 1 ?
|
||||
t('operations.copy.annotation.single') :
|
||||
t('operations.copy.annotation.multiple', { n: selectedIDs.length });
|
||||
t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { uiCmd } from '../ui/cmd';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationDelete(selectedIDs, context) {
|
||||
export function operationDelete(context, selectedIDs) {
|
||||
var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
|
||||
var action = actionDeleteMultiple(selectedIDs);
|
||||
var nodes = utilGetAllNodes(selectedIDs, context.graph());
|
||||
|
||||
@@ -4,7 +4,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { utilGetAllNodes } from '../util/index';
|
||||
|
||||
|
||||
export function operationDisconnect(selectedIDs, context) {
|
||||
export function operationDisconnect(context, selectedIDs) {
|
||||
var vertexIDs = [];
|
||||
var wayIDs = [];
|
||||
var otherIDs = [];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { t } from '../core/localizer';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
import { presetManager } from '../presets';
|
||||
|
||||
export function operationDowngrade(selectedIDs, context) {
|
||||
export function operationDowngrade(context, selectedIDs) {
|
||||
var affectedFeatureCount = 0;
|
||||
var downgradeType;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { modeSelect } from '../modes/select';
|
||||
import { t } from '../core/localizer';
|
||||
import { presetManager } from '../presets';
|
||||
|
||||
export function operationExtract(selectedIDs, context) {
|
||||
export function operationExtract(context, selectedIDs) {
|
||||
var entityID = selectedIDs.length && selectedIDs[0];
|
||||
var action = actionExtract(entityID);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export { operationExtract } from './extract';
|
||||
export { operationMerge } from './merge';
|
||||
export { operationMove } from './move';
|
||||
export { operationOrthogonalize } from './orthogonalize';
|
||||
export { operationPaste } from './paste';
|
||||
export { operationReflectShort, operationReflectLong } from './reflect';
|
||||
export { operationReverse } from './reverse';
|
||||
export { operationRotate } from './rotate';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { presetManager } from '../presets';
|
||||
|
||||
export function operationMerge(selectedIDs, context) {
|
||||
export function operationMerge(context, selectedIDs) {
|
||||
|
||||
var join = actionJoin(selectedIDs);
|
||||
var merge = actionMerge(selectedIDs);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { modeMove } from '../modes/move';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationMove(selectedIDs, context) {
|
||||
export function operationMove(context, selectedIDs) {
|
||||
var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
|
||||
var nodes = utilGetAllNodes(selectedIDs, context.graph());
|
||||
var coords = nodes.map(function(n) { return n.loc; });
|
||||
|
||||
@@ -4,7 +4,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationOrthogonalize(selectedIDs, context) {
|
||||
export function operationOrthogonalize(context, selectedIDs) {
|
||||
var _extent;
|
||||
var _type;
|
||||
var _actions = selectedIDs.map(chooseAction).filter(Boolean);
|
||||
|
||||
95
modules/operations/paste.js
Normal file
95
modules/operations/paste.js
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
import { actionCopyEntities } from '../actions/copy_entities';
|
||||
import { actionMove } from '../actions/move';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { geoExtent, geoVecSubtract } from '../geo';
|
||||
import { t } from '../core/localizer';
|
||||
import { uiCmd } from '../ui/cmd';
|
||||
import { utilDisplayLabel } from '../util/util';
|
||||
|
||||
// see also `behaviorPaste`
|
||||
export function operationPaste(context) {
|
||||
|
||||
var _point;
|
||||
|
||||
var operation = function() {
|
||||
|
||||
if (!_point) return;
|
||||
|
||||
var oldIDs = context.copyIDs();
|
||||
if (!oldIDs.length) return;
|
||||
|
||||
var projection = context.projection;
|
||||
var extent = geoExtent();
|
||||
var oldGraph = context.copyGraph();
|
||||
var newIDs = [];
|
||||
|
||||
var action = actionCopyEntities(oldIDs, oldGraph);
|
||||
context.perform(action);
|
||||
|
||||
var copies = action.copies();
|
||||
var originals = new Set();
|
||||
Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
|
||||
|
||||
for (var id in copies) {
|
||||
var oldEntity = oldGraph.entity(id);
|
||||
var newEntity = copies[id];
|
||||
|
||||
extent._extend(oldEntity.extent(oldGraph));
|
||||
|
||||
// Exclude child nodes from newIDs if their parent way was also copied.
|
||||
var parents = context.graph().parentWays(newEntity);
|
||||
var parentCopied = parents.some(function(parent) {
|
||||
return originals.has(parent.id);
|
||||
});
|
||||
|
||||
if (!parentCopied) {
|
||||
newIDs.push(newEntity.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Put pasted objects where mouse pointer is..
|
||||
var center = projection(extent.center());
|
||||
var delta = geoVecSubtract(_point, center);
|
||||
|
||||
context.replace(actionMove(newIDs, delta, projection), operation.annotation());
|
||||
context.enter(modeSelect(context, newIDs));
|
||||
};
|
||||
|
||||
operation.point = function(val) {
|
||||
_point = val;
|
||||
return operation;
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
return context.mode().id === 'browse';
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
return !context.copyIDs().length;
|
||||
};
|
||||
|
||||
operation.tooltip = function() {
|
||||
var oldGraph = context.copyGraph();
|
||||
var ids = context.copyIDs();
|
||||
if (!ids.length) {
|
||||
return t('operations.paste.nothing_copied');
|
||||
}
|
||||
return ids.length === 1 ?
|
||||
t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
|
||||
t('operations.paste.description.multiple', { n: ids.length.toString() });
|
||||
};
|
||||
|
||||
operation.annotation = function() {
|
||||
var ids = context.copyIDs();
|
||||
return ids.length === 1 ?
|
||||
t('operations.paste.annotation.single') :
|
||||
t('operations.paste.annotation.multiple', { n: ids.length.toString() });
|
||||
};
|
||||
|
||||
operation.id = 'paste';
|
||||
operation.keys = [uiCmd('⌘V')];
|
||||
operation.title = t('operations.paste.title');
|
||||
|
||||
return operation;
|
||||
}
|
||||
@@ -5,17 +5,17 @@ import { geoExtent } from '../geo';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationReflectShort(selectedIDs, context) {
|
||||
return operationReflect(selectedIDs, context, 'short');
|
||||
export function operationReflectShort(context, selectedIDs) {
|
||||
return operationReflect(context, selectedIDs, 'short');
|
||||
}
|
||||
|
||||
|
||||
export function operationReflectLong(selectedIDs, context) {
|
||||
return operationReflect(selectedIDs, context, 'long');
|
||||
export function operationReflectLong(context, selectedIDs) {
|
||||
return operationReflect(context, selectedIDs, 'long');
|
||||
}
|
||||
|
||||
|
||||
export function operationReflect(selectedIDs, context, axis) {
|
||||
export function operationReflect(context, selectedIDs, axis) {
|
||||
axis = axis || 'long';
|
||||
var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
|
||||
var nodes = utilGetAllNodes(selectedIDs, context.graph());
|
||||
|
||||
@@ -3,7 +3,7 @@ import { actionReverse } from '../actions/reverse';
|
||||
import { behaviorOperation } from '../behavior/operation';
|
||||
|
||||
|
||||
export function operationReverse(selectedIDs, context) {
|
||||
export function operationReverse(context, selectedIDs) {
|
||||
|
||||
var operation = function() {
|
||||
context.perform(function combinedReverseAction(graph) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { modeRotate } from '../modes/rotate';
|
||||
import { utilGetAllNodes } from '../util';
|
||||
|
||||
|
||||
export function operationRotate(selectedIDs, context) {
|
||||
export function operationRotate(context, selectedIDs) {
|
||||
var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
|
||||
var nodes = utilGetAllNodes(selectedIDs, context.graph());
|
||||
var coords = nodes.map(function(n) { return n.loc; });
|
||||
|
||||
@@ -4,7 +4,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { modeSelect } from '../modes/select';
|
||||
|
||||
|
||||
export function operationSplit(selectedIDs, context) {
|
||||
export function operationSplit(context, selectedIDs) {
|
||||
var vertices = selectedIDs
|
||||
.filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
|
||||
var entityID = vertices[0];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { utilArrayDifference, utilGetAllNodes } from '../util/index';
|
||||
|
||||
|
||||
export function operationStraighten(selectedIDs, context) {
|
||||
export function operationStraighten(context, selectedIDs) {
|
||||
var wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
|
||||
var nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { utilGetDimensions } from '../util/dimensions';
|
||||
import { uiAccount } from './account';
|
||||
import { uiAttribution } from './attribution';
|
||||
import { uiContributors } from './contributors';
|
||||
import { uiEditMenu } from './edit_menu';
|
||||
import { uiFeatureInfo } from './feature_info';
|
||||
import { uiFlash } from './flash';
|
||||
import { uiFullScreen } from './full_screen';
|
||||
@@ -548,6 +549,48 @@ export function uiInit(context) {
|
||||
};
|
||||
|
||||
|
||||
var _editMenu; // uiEditMenu
|
||||
|
||||
ui.showEditMenu = function(anchorPoint, triggerType, operations) {
|
||||
|
||||
// remove any displayed menu
|
||||
ui.closeEditMenu();
|
||||
|
||||
if (!operations && context.mode().operations) operations = context.mode().operations();
|
||||
if (!operations || !operations.length) return;
|
||||
|
||||
// disable menu if in wide selection, for example
|
||||
if (!context.map().editableDataEnabled()) return;
|
||||
|
||||
var surfaceNode = context.surface().node();
|
||||
if (surfaceNode.focus) { // FF doesn't support it
|
||||
// focus the surface or else clicking off the menu may not trigger modeBrowse
|
||||
surfaceNode.focus();
|
||||
}
|
||||
|
||||
// don't load the menu until it's needed
|
||||
if (!_editMenu) _editMenu = uiEditMenu(context);
|
||||
|
||||
operations.forEach(function(operation) {
|
||||
if (operation.point) operation.point(anchorPoint);
|
||||
});
|
||||
|
||||
_editMenu
|
||||
.anchorLoc(anchorPoint)
|
||||
.triggerType(triggerType)
|
||||
.operations(operations);
|
||||
|
||||
// render the menu
|
||||
context.map().supersurface.call(_editMenu);
|
||||
};
|
||||
|
||||
ui.closeEditMenu = function() {
|
||||
// remove any existing menu no matter how it was added
|
||||
context.map().supersurface
|
||||
.select('.edit-menu').remove();
|
||||
};
|
||||
|
||||
|
||||
var _saveLoading = d3_select(null);
|
||||
|
||||
context.uploader()
|
||||
|
||||
@@ -53,7 +53,7 @@ export function uiPresetList(context) {
|
||||
d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
|
||||
d3_event.preventDefault();
|
||||
d3_event.stopPropagation();
|
||||
operationDelete(_entityIDs, context)();
|
||||
operationDelete(context, _entityIDs)();
|
||||
|
||||
// hack to let undo work when search is autofocused
|
||||
} else if (search.property('value').length === 0 &&
|
||||
|
||||
@@ -65,7 +65,7 @@ export function validationDisconnectedWay() {
|
||||
entityIds: [singleEntity.id],
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
var operation = operationDelete(context, [id]);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
|
||||
@@ -107,12 +107,12 @@ export function validationMissingTag(context) {
|
||||
var deleteOnClick;
|
||||
|
||||
var id = this.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
var operation = operationDelete(context, [id]);
|
||||
var disabledReasonID = operation.disabled();
|
||||
if (!disabledReasonID) {
|
||||
deleteOnClick = function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
var operation = operationDelete(context, [id]);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="20" height="20" viewBox="0 0 20 20">
|
||||
<path d="M12,11 C12.276,11 12.5,11.224 12.5,11.5 L12.5,12.5 L13.5,12.5 C13.776,12.5 14,12.724 14,13 C14,13.276 13.776,13.5 13.5,13.5 L12.5,13.5 L12.5,14.5 C12.5,14.776 12.276,15 12,15 C11.724,15 11.5,14.776 11.5,14.5 L11.5,13.5 L10.5,13.5 C10.224,13.5 10,13.276 10,13 C10,12.724 10.224,12.5 10.5,12.5 L11.5,12.5 L11.5,11.5 C11.5,11.224 11.724,11 12,11 z" fill="inherit"/>
|
||||
<path d="M16,8 C16.552,8 17,8.448 17,9 L17,17 C17,17.552 16.552,18 16,18 L8,18 C7.448,18 7,17.552 7,17 L7,9 C7,8.448 7.448,8 8,8 L16,8 z M15,10 L9,10 L9,16 L15,16 L15,10 z" fill="inherit"/>
|
||||
<path d="M16,8 C16.552,8 17,8.448 17,9 L17,17 C17,17.552 16.552,18 16,18 L8,18 C7.448,18 7,17.552 7,17 L7,9 C7,8.448 7.448,8 8,8 L16,8 z M15,10 L9,10 L9,16 L15,16 L15,10 z" fill="currentColor"/>
|
||||
<path d="M8.5,1.5 C9.26,1.654 8.943,1.478 9.466,2 L12,2 C12.552,2 13,2.448 13,3 L13,7 L11,7 L11,4 L9.466,4 C8.943,4.523 9.26,4.347 8.5,4.5 L6.5,4.5 C5.74,4.347 6.057,4.523 5.534,4 L4,4 L4,14 L6,14 L6,16 L3,16 C2.448,16 2,15.552 2,15 L2,3 C2,2.448 2.448,2 3,2 L5.534,2 C6.057,1.477 5.74,1.654 6.5,1.5 L8.5,1.5 z" fill="inherit"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -37,52 +37,52 @@ describe('iD.operationExtract', function () {
|
||||
});
|
||||
|
||||
it('is not available for no selected ids', function () {
|
||||
var result = iD.operationExtract([], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, []).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for two selected ids', function () {
|
||||
var result = iD.operationExtract(['a', 'b'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['a', 'b']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for unknown selected id', function () {
|
||||
var result = iD.operationExtract(['z'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['z']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected way', function () {
|
||||
var result = iD.operationExtract(['x'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['x']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with tags, no parent way', function () {
|
||||
var result = iD.operationExtract(['e'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['e']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, no parent way', function () {
|
||||
var result = iD.operationExtract(['f'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['f']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, parent way', function () {
|
||||
var result = iD.operationExtract(['c'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['c']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for selected node with no tags, two parent ways', function () {
|
||||
var result = iD.operationExtract(['d'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['d']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is available for selected node with tags, parent way', function () {
|
||||
var result = iD.operationExtract(['a'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['a']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for selected node with tags, two parent ways', function () {
|
||||
var result = iD.operationExtract(['b'], fakeContext).available();
|
||||
var result = iD.operationExtract(fakeContext, ['b']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
});
|
||||
@@ -96,7 +96,7 @@ describe('iD.operationExtract', function () {
|
||||
iD.osmNode(createFakeNode('c', false)),
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] })
|
||||
]);
|
||||
var result = iD.operationExtract(['b'], fakeContext).disabled();
|
||||
var result = iD.operationExtract(fakeContext, ['b']).disabled();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
@@ -108,7 +108,7 @@ describe('iD.operationExtract', function () {
|
||||
iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] }),
|
||||
iD.osmRelation({ id: 'r', members: [{ id: 'b', role: 'label' }] })
|
||||
]);
|
||||
var result = iD.operationExtract(['b'], fakeContext).disabled();
|
||||
var result = iD.operationExtract(fakeContext, ['b']).disabled();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('iD.operationExtract', function () {
|
||||
]
|
||||
})
|
||||
]);
|
||||
var result = iD.operationExtract(['d'], fakeContext).disabled();
|
||||
var result = iD.operationExtract(fakeContext, ['d']).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
});
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('iD.operationExtract', function () {
|
||||
]
|
||||
})
|
||||
]);
|
||||
var result = iD.operationExtract(['d'], fakeContext).disabled();
|
||||
var result = iD.operationExtract(fakeContext, ['d']).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,77 +42,77 @@ describe('iD.operationStraighten', function () {
|
||||
});
|
||||
|
||||
it('is not available for no selected ids', function () {
|
||||
var result = iD.operationStraighten([], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, []).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for way with only 2 nodes', function () {
|
||||
var result = iD.operationStraighten(['w1'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w1']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is available for way with only 2 nodes connected to another 2-node way', function () {
|
||||
var result = iD.operationStraighten(['w1', 'w1-2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w1', 'w1-2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is not available for non-continuous ways', function () {
|
||||
var result = iD.operationStraighten(['w2', 'w4'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w2', 'w4']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is available for selected way with more than 2 nodes', function () {
|
||||
var result = iD.operationStraighten(['w2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for selected, ordered, continuous ways', function () {
|
||||
var result = iD.operationStraighten(['w1', 'w2', 'w3'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w1', 'w2', 'w3']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for selected, un-ordered, continuous ways', function () {
|
||||
var result = iD.operationStraighten(['w1', 'w3', 'w2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w1', 'w3', 'w2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for selected, continuous ways with different way-directions', function () {
|
||||
var result = iD.operationStraighten(['w1', 'w3', 'w2-2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w1', 'w3', 'w2-2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for 2 selected nodes in the same way, more than one node apart', function () {
|
||||
var result = iD.operationStraighten(['w5', 'n9', 'n11'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w5', 'n9', 'n11']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for 2 selected nodes in adjacent ways, more than one node apart', function () {
|
||||
var result = iD.operationStraighten(['w2', 'w3', 'n5', 'n3'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w2', 'w3', 'n5', 'n3']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for 2 selected nodes in non-adjacent ways, providing inbetween ways are selected', function () {
|
||||
var result = iD.operationStraighten(['n2', 'n7', 'w4', 'w1', 'w3', 'w2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['n2', 'n7', 'w4', 'w1', 'w3', 'w2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for 2 selected nodes in non-adjacent, non-same-directional ways, providing inbetween ways are selected', function () {
|
||||
var result = iD.operationStraighten(['n2', 'n7', 'w4', 'w1', 'w3', 'w2-2'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['n2', 'n7', 'w4', 'w1', 'w3', 'w2-2']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is not available for nodes not on selected ways', function () {
|
||||
var result = iD.operationStraighten(['w5', 'n4', 'n11'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w5', 'n4', 'n11']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for one selected node', function () {
|
||||
var result = iD.operationStraighten(['w5', 'n9'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w5', 'n9']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for more than two selected nodes', function () {
|
||||
var result = iD.operationStraighten(['w5', 'n9', 'n11', 'n12'], fakeContext).available();
|
||||
var result = iD.operationStraighten(fakeContext, ['w5', 'n9', 'n11', 'n12']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user