Add iD.Context

This is a facade interface that ties together a bunch of
different internal objects and will make it easier to write
tests for behaviors, modes, and operations.
This commit is contained in:
John Firebaugh
2013-01-31 15:50:00 -05:00
parent ab7290a865
commit 7e68e8e114
33 changed files with 384 additions and 410 deletions
+1
View File
@@ -27,6 +27,7 @@ all: \
js/lib/sha.js \
js/id/start.js \
js/id/id.js \
js/id/context.js \
js/id/connection.js \
js/id/oauth.js \
js/id/services/*.js \
+2 -1
View File
@@ -122,9 +122,10 @@
<script src='js/id/graph/relation.js'></script>
<script src='js/id/graph/way.js'></script>
<script src='js/id/connection.js'></script>
<script src='js/id/context.js'></script>
<script src='js/id/controller.js'></script>
<script src='js/id/validate.js'></script>
<script src='js/id/connection.js'></script>
<script src='locale/locale.js'></script>
<script src='locale/en.js'></script>
+9 -9
View File
@@ -1,8 +1,6 @@
iD.behavior.AddWay = function(mode) {
var map = mode.map,
controller = mode.controller,
event = d3.dispatch('start', 'startFromWay', 'startFromNode', 'startFromMidpoint'),
draw = iD.behavior.Draw(map);
iD.behavior.AddWay = function(context) {
var event = d3.dispatch('start', 'startFromWay', 'startFromNode', 'startFromMidpoint'),
draw = iD.behavior.Draw(context);
var addWay = function(surface) {
draw.on('click', event.start)
@@ -12,7 +10,8 @@ iD.behavior.AddWay = function(mode) {
.on('cancel', addWay.cancel)
.on('finish', addWay.cancel);
map.fastEnable(false)
context.map()
.fastEnable(false)
.minzoom(16)
.dblclickEnable(false);
@@ -20,19 +19,20 @@ iD.behavior.AddWay = function(mode) {
};
addWay.off = function(surface) {
map.fastEnable(true)
context.map()
.fastEnable(true)
.minzoom(0)
.tail(false);
window.setTimeout(function() {
map.dblclickEnable(true);
context.map().dblclickEnable(true);
}, 1000);
surface.call(draw.off);
};
addWay.cancel = function() {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
};
return d3.rebind(addWay, event, 'on');
+6 -9
View File
@@ -1,16 +1,13 @@
iD.behavior.DragMidpoint = function(mode) {
var history = mode.history,
projection = mode.map.projection;
iD.behavior.DragMidpoint = function(context) {
var behavior = iD.behavior.drag()
.delegate(".midpoint")
.origin(function(d) {
return projection(d.loc);
return context.projection(d.loc);
})
.on('start', function(d) {
var node = iD.Node();
history.perform(iD.actions.AddMidpoint(d, node));
context.perform(iD.actions.AddMidpoint(d, node));
var vertex = d3.selectAll('.vertex')
.filter(function(data) { return data.id === node.id; });
@@ -19,11 +16,11 @@ iD.behavior.DragMidpoint = function(mode) {
})
.on('move', function(d) {
d3.event.sourceEvent.stopPropagation();
history.replace(
iD.actions.MoveNode(d.id, projection.invert(d3.event.point)));
context.replace(
iD.actions.MoveNode(d.id, context.projection.invert(d3.event.point)));
})
.on('end', function() {
history.replace(
context.replace(
iD.actions.Noop(),
t('operations.add.annotation.vertex'));
});
+10 -12
View File
@@ -1,8 +1,6 @@
iD.behavior.DragNode = function(mode) {
var history = mode.history,
size = mode.map.size(),
nudgeInterval,
projection = mode.map.projection;
iD.behavior.DragNode = function(context) {
var size = context.map().size(),
nudgeInterval;
function edge(point) {
var pad = [30, 100, 30, 100];
@@ -16,7 +14,7 @@ iD.behavior.DragNode = function(mode) {
function startNudge(nudge) {
if (nudgeInterval) window.clearInterval(nudgeInterval);
nudgeInterval = window.setInterval(function() {
mode.map.pan(nudge).redraw();
context.map().pan(nudge).redraw();
}, 50);
}
@@ -26,16 +24,16 @@ iD.behavior.DragNode = function(mode) {
}
function annotation(entity) {
return t('operations.move.annotation.' + entity.geometry(mode.history.graph()));
return t('operations.move.annotation.' + entity.geometry(context.graph()));
}
return iD.behavior.drag()
.delegate(".node")
.origin(function(entity) {
return projection(entity.loc);
return context.projection(entity.loc);
})
.on('start', function() {
history.perform(
context.perform(
iD.actions.Noop());
})
.on('move', function(entity) {
@@ -45,13 +43,13 @@ iD.behavior.DragNode = function(mode) {
if (nudge) startNudge(nudge);
else stopNudge();
history.replace(
iD.actions.MoveNode(entity.id, projection.invert(d3.event.point)),
context.replace(
iD.actions.MoveNode(entity.id, context.projection.invert(d3.event.point)),
annotation(entity));
})
.on('end', function(entity) {
stopNudge();
history.replace(
context.replace(
iD.actions.Noop(),
annotation(entity));
});
+12 -12
View File
@@ -1,7 +1,8 @@
iD.behavior.Draw = function(map) {
iD.behavior.Draw = function(context) {
var event = d3.dispatch('move', 'click', 'clickWay', 'clickNode', 'clickMidpoint', 'undo', 'cancel', 'finish'),
keybinding = d3.keybinding('draw'),
down, surface, hover;
hover = iD.behavior.Hover(),
down;
function datum() {
if (d3.event.altKey) {
@@ -28,7 +29,7 @@ iD.behavior.Draw = function(map) {
function click() {
var d = datum();
if (d.type === 'way') {
var choice = iD.geo.chooseIndex(d, d3.mouse(map.surface.node()), map);
var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context.map());
event.clickWay(d, choice.loc, choice.index);
} else if (d.type === 'node') {
@@ -38,19 +39,19 @@ iD.behavior.Draw = function(map) {
event.clickMidpoint(d);
} else {
event.click(map.mouseCoordinates());
event.click(context.map().mouseCoordinates());
}
}
function keydown() {
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
surface.call(hover.off);
context.uninstall(hover);
}
}
function keyup() {
if (d3.event.keyCode === d3.keybinding.modifierCodes.alt) {
surface.call(hover);
context.install(hover);
}
}
@@ -70,8 +71,7 @@ iD.behavior.Draw = function(map) {
}
function draw(selection) {
surface = selection;
hover = iD.behavior.Hover();
context.install(hover);
keybinding
.on('⌫', backspace)
@@ -83,8 +83,7 @@ iD.behavior.Draw = function(map) {
.on('mousedown.draw', mousedown)
.on('mouseup.draw', mouseup)
.on('mousemove.draw', mousemove)
.on('click.draw', click)
.call(hover);
.on('click.draw', click);
d3.select(document)
.call(keybinding)
@@ -95,12 +94,13 @@ iD.behavior.Draw = function(map) {
}
draw.off = function(selection) {
context.uninstall(hover);
selection
.on('mousedown.draw', null)
.on('mouseup.draw', null)
.on('mousemove.draw', null)
.on('click.draw', null)
.call(hover.off);
.on('click.draw', null);
d3.select(document)
.call(keybinding.off)
+35 -34
View File
@@ -1,35 +1,32 @@
iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
var map = mode.map,
history = mode.history,
controller = mode.controller,
way = history.graph().entity(wayId),
iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
var way = context.entity(wayId),
finished = false,
annotation = t((way.isDegenerate() ?
'operations.start.annotation.' :
'operations.continue.annotation.') + way.geometry(history.graph())),
draw = iD.behavior.Draw(map);
'operations.continue.annotation.') + context.geometry(wayId)),
draw = iD.behavior.Draw(context);
var node = iD.Node({loc: map.mouseCoordinates()}),
var node = iD.Node({loc: context.map().mouseCoordinates()}),
nodeId = node.id;
history[way.isDegenerate() ? 'replace' : 'perform'](
context[way.isDegenerate() ? 'replace' : 'perform'](
iD.actions.AddEntity(node),
iD.actions.AddVertex(wayId, node.id, index));
function move(datum) {
var loc = map.mouseCoordinates();
var loc = context.map().mouseCoordinates();
if (datum.type === 'node' || datum.type === 'midpoint') {
loc = datum.loc;
} else if (datum.type === 'way') {
loc = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map).loc;
loc = iD.geo.chooseIndex(datum, d3.mouse(context.surface().node()), context.map()).loc;
}
history.replace(iD.actions.MoveNode(nodeId, loc));
context.replace(iD.actions.MoveNode(nodeId, loc));
}
function undone() {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
}
var drawWay = function(surface) {
@@ -38,11 +35,12 @@ iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
.on('clickWay', drawWay.addWay)
.on('clickNode', drawWay.addNode)
.on('clickMidpoint', drawWay.addMidpoint)
.on('undo', history.undo)
.on('undo', context.undo)
.on('cancel', drawWay.cancel)
.on('finish', drawWay.finish);
map.fastEnable(false)
context.map()
.fastEnable(false)
.minzoom(16)
.dblclickEnable(false);
@@ -51,26 +49,29 @@ iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
.filter(function (d) { return d.id === wayId || d.id === nodeId; })
.classed('active', true);
history.on('undone.draw', undone);
context.history()
.on('undone.draw', undone);
};
drawWay.off = function(surface) {
if (!finished)
history.pop();
context.pop();
map.fastEnable(true)
context.map()
.fastEnable(true)
.minzoom(0)
.tail(false);
window.setTimeout(function() {
map.dblclickEnable(true);
context.map().dblclickEnable(true);
}, 1000);
surface.call(draw.off)
.selectAll('.way, .node')
.classed('active', false);
history.on('undone.draw', null);
context.history()
.on('undone.draw', null);
};
function ReplaceTemporaryNode(newNode) {
@@ -85,74 +86,74 @@ iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
drawWay.add = function(loc) {
var newNode = iD.Node({loc: loc});
history.replace(
context.replace(
iD.actions.AddEntity(newNode),
ReplaceTemporaryNode(newNode),
annotation);
finished = true;
controller.enter(mode);
context.enter(mode);
};
// Connect the way to an existing way.
drawWay.addWay = function(way, loc, wayIndex) {
var newNode = iD.Node({loc: loc});
history.perform(
context.perform(
iD.actions.AddEntity(newNode),
iD.actions.AddVertex(way.id, newNode.id, wayIndex),
ReplaceTemporaryNode(newNode),
annotation);
finished = true;
controller.enter(mode);
context.enter(mode);
};
// Connect the way to an existing node and continue drawing.
drawWay.addNode = function(node) {
history.perform(
context.perform(
ReplaceTemporaryNode(node),
annotation);
finished = true;
controller.enter(mode);
context.enter(mode);
};
// Add a midpoint, connect the way to it, and continue drawing.
drawWay.addMidpoint = function(midpoint) {
var node = iD.Node();
history.perform(
context.perform(
iD.actions.AddMidpoint(midpoint, node),
ReplaceTemporaryNode(node),
annotation);
finished = true;
controller.enter(mode);
context.enter(mode);
};
// Finish the draw operation, removing the temporary node. If the way has enough
// nodes to be valid, it's selected. Otherwise, return to browse mode.
drawWay.finish = function() {
history.pop();
context.pop();
finished = true;
var way = history.graph().entity(wayId);
var way = context.entity(wayId);
if (way) {
controller.enter(iD.modes.Select([way.id], true));
context.enter(iD.modes.Select(context, [way.id], true));
} else {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
}
};
// Cancel the draw operation and return to browse, deleting everything drawn.
drawWay.cancel = function() {
history.perform(
context.perform(
d3.functor(baseGraph),
t('operations.cancel_draw.annotation'));
finished = true;
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
};
return d3.rebind(drawWay, event, 'on');
+15 -14
View File
@@ -1,4 +1,4 @@
iD.behavior.Hash = function(controller, map) {
iD.behavior.Hash = function(context) {
var s0 = null, // cached location.hash
lat = 90 - 1e-8; // allowable latitude range
@@ -27,13 +27,13 @@ iD.behavior.Hash = function(controller, map) {
};
var move = _.throttle(function() {
var s1 = formatter(map);
var s1 = formatter(context.map());
if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
}, 100);
function hashchange() {
if (location.hash === s0) return; // ignore spurious hashchange events
if (parser(map, (s0 = location.hash).substring(1))) {
if (parser(context.map(), (s0 = location.hash).substring(1))) {
move(); // replace bogus hash
}
}
@@ -42,24 +42,24 @@ iD.behavior.Hash = function(controller, map) {
// do so before any features are loaded. thus wait for the feature to
// be loaded and then select
function willselect(id) {
map.on('drawn.hash', function() {
var entity = map.history().graph().entity(id);
if (entity === undefined) return;
else selectoff();
controller.enter(iD.modes.Select([entity.id]));
map.on('drawn.hash', null);
context.map().on('drawn.hash', function() {
if (!context.entity(id)) return;
selectoff();
context.enter(iD.modes.Select([id]));
});
controller.on('enter.hash', function() {
if (controller.mode.id !== 'browse') selectoff();
context.controller().on('enter.hash', function() {
if (context.mode().id !== 'browse') selectoff();
});
}
function selectoff() {
map.on('drawn.hash', null);
context.map().on('drawn.hash', null);
}
function hash() {
map.on('move.hash', move);
context.map()
.on('move.hash', move);
d3.select(window)
.on('hashchange.hash', hashchange);
@@ -75,7 +75,8 @@ iD.behavior.Hash = function(controller, map) {
}
hash.off = function() {
map.on('move.hash', null);
context.map()
.on('move.hash', null);
d3.select(window)
.on('hashchange.hash', null);
+3 -5
View File
@@ -1,12 +1,10 @@
iD.behavior.Select = function(mode) {
var controller = mode.controller;
iD.behavior.Select = function(context) {
function click() {
var datum = d3.select(d3.event.target).datum();
if (datum instanceof iD.Entity) {
controller.enter(iD.modes.Select([datum.id]));
context.enter(iD.modes.Select(context, [datum.id]));
} else {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
}
}
+44
View File
@@ -0,0 +1,44 @@
iD.Context = function() {
var history = iD.History(),
connection = iD.Connection(),
controller = iD.Controller(),
container,
map = iD.Map().connection(connection).history(history);
var context = {};
context.container = function (_) {
if (!arguments.length) return container;
container = _;
return context;
};
context.connection = function () { return connection; };
context.history = function () { return history; };
context.graph = history.graph;
context.perform = history.perform;
context.replace = history.replace;
context.pop = history.pop;
context.undo = history.undo;
context.redo = history.undo;
context.changes = history.changes;
context.entity = function (id) { return history.graph().entity(id); };
context.geometry = function (id) { return context.entity(id).geometry(history.graph()); };
context.controller = function () { return controller; };
context.enter = controller.enter;
context.mode = function () { return controller.mode; };
context.install = function (behavior) { context.surface().call(behavior); };
context.uninstall = function (behavior) { context.surface().call(behavior.off); };
context.map = function () { return map; };
context.background = function () { return map.background; };
context.surface = function () { return map.surface; };
context.projection = map.projection;
context.tail = map.tail;
return context;
};
+1 -5
View File
@@ -1,14 +1,10 @@
// A controller holds a single action at a time and calls `.enter` and `.exit`
// to bind and unbind actions.
iD.Controller = function(map, history) {
iD.Controller = function() {
var event = d3.dispatch('enter', 'exit');
var controller = { mode: null };
controller.enter = function(mode) {
mode.controller = controller;
mode.history = history;
mode.map = map;
if (controller.mode) {
controller.mode.exit();
event.exit(controller.mode);
+25 -14
View File
@@ -2,17 +2,22 @@ window.iD = function(container) {
// the reported, displayed version of iD.
var version = '0.0.0-alpha1';
var connection = iD.Connection()
.version(version),
history = iD.History(),
map = iD.Map()
.connection(connection)
.history(history),
controller = iD.Controller(map, history);
var context = iD.Context();
map.background.source(iD.BackgroundSource.Bing);
var connection = context.connection(),
history = context.history(),
map = context.map(),
controller = context.controller();
context.connection()
.version(version);
context.background()
.source(iD.BackgroundSource.Bing);
function editor(container) {
context.container(container);
if (!iD.supported()) {
container.html('This editor is supported in Firefox, Chrome, Safari, Opera, ' +
'and Internet Explorer 9 and above. Please upgrade your browser ' +
@@ -39,8 +44,14 @@ window.iD = function(container) {
var buttons_joined = limiter.append('div')
.attr('class', 'button-wrap joined col4');
var modes = [
iD.modes.Browse(context),
iD.modes.AddPoint(context),
iD.modes.AddLine(context),
iD.modes.AddArea(context)];
var buttons = buttons_joined.selectAll('button.add-button')
.data([iD.modes.Browse(), iD.modes.AddPoint(), iD.modes.AddLine(), iD.modes.AddArea()])
.data(modes)
.enter().append('button')
.attr('tabindex', -1)
.attr('class', function (mode) { return mode.title + ' add-button col3'; })
@@ -57,7 +68,7 @@ window.iD = function(container) {
} else {
buttons.attr('disabled', 'disabled');
notice.message(true);
controller.enter(iD.modes.Browse());
controller.enter(iD.modes.Browse(context));
}
}
@@ -106,7 +117,7 @@ window.iD = function(container) {
var save_button = limiter.append('div').attr('class','button-wrap col1').append('button')
.attr('class', 'save col12')
.call(iD.ui.save().map(map).controller(controller));
.call(iD.ui.save(context));
var zoom = container.append('div')
.attr('class', 'zoombuttons map-control')
@@ -227,14 +238,14 @@ window.iD = function(container) {
.on('⌃+⇧+Z', function() { history.redo(); })
.on('⌫', function() { d3.event.preventDefault(); });
[iD.modes.Browse(), iD.modes.AddPoint(), iD.modes.AddLine(), iD.modes.AddArea()].forEach(function(m) {
modes.forEach(function(m) {
keybinding.on(m.key, function() { if (map.editable()) controller.enter(m); });
});
d3.select(document)
.call(keybinding);
var hash = iD.behavior.Hash(controller, map);
var hash = iD.behavior.Hash(context);
hash();
@@ -246,7 +257,7 @@ window.iD = function(container) {
.on('logout.editor', connection.logout)
.on('login.editor', connection.authenticate));
controller.enter(iD.modes.Browse());
controller.enter(iD.modes.Browse(context));
if (!localStorage.sawSplash) {
iD.ui.splash();
+17 -21
View File
@@ -1,4 +1,4 @@
iD.modes.AddArea = function() {
iD.modes.AddArea = function(context) {
var mode = {
id: 'add-area',
button: 'area',
@@ -11,77 +11,73 @@ iD.modes.AddArea = function() {
defaultTags = {area: 'yes'};
mode.enter = function() {
var map = mode.map,
history = mode.history,
controller = mode.controller;
function start(loc) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node({loc: loc}),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawArea(way.id, graph));
context.enter(iD.modes.DrawArea(context, way.id, graph));
}
function startFromWay(other, loc, index) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node({loc: loc}),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(other.id, node.id, index));
controller.enter(iD.modes.DrawArea(way.id, graph));
context.enter(iD.modes.DrawArea(context, way.id, graph));
}
function startFromNode(node) {
var graph = history.graph(),
var graph = context.graph(),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawArea(way.id, graph));
context.enter(iD.modes.DrawArea(context, way.id, graph));
}
function startFromMidpoint(midpoint) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node(),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddMidpoint(midpoint, node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawArea(way.id, graph));
context.enter(iD.modes.DrawArea(context, way.id, graph));
}
behavior = iD.behavior.AddWay(mode)
behavior = iD.behavior.AddWay(context)
.on('start', start)
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode)
.on('startFromMidpoint', startFromMidpoint);
mode.map.surface.call(behavior);
mode.map.tail(t('modes.add_area.tail'));
context.install(behavior);
context.tail(t('modes.add_area.tail'));
};
mode.exit = function() {
mode.map.surface.call(behavior.off);
context.uninstall(behavior);
};
return mode;
+19 -23
View File
@@ -1,4 +1,4 @@
iD.modes.AddLine = function() {
iD.modes.AddLine = function(context) {
var mode = {
id: 'add-line',
button: 'line',
@@ -11,84 +11,80 @@ iD.modes.AddLine = function() {
defaultTags = {highway: 'residential'};
mode.enter = function() {
var map = mode.map,
history = mode.history,
controller = mode.controller;
function start(loc) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node({loc: loc}),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
}
function startFromWay(other, loc, index) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node({loc: loc}),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id),
iD.actions.AddVertex(other.id, node.id, index));
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
}
function startFromNode(node) {
var graph = history.graph(),
var graph = context.graph(),
parent = graph.parentWays(node)[0],
isLine = parent && parent.geometry(graph) === 'line';
if (isLine && parent.first() === node.id) {
controller.enter(iD.modes.DrawLine(parent.id, 'backward', graph));
context.enter(iD.modes.DrawLine(context, parent.id, 'backward', graph));
} else if (isLine && parent.last() === node.id) {
controller.enter(iD.modes.DrawLine(parent.id, 'forward', graph));
context.enter(iD.modes.DrawLine(context, parent.id, 'forward', graph));
} else {
var way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
}
}
function startFromMidpoint(midpoint) {
var graph = history.graph(),
var graph = context.graph(),
node = iD.Node(),
way = iD.Way({tags: defaultTags});
history.perform(
context.perform(
iD.actions.AddMidpoint(midpoint, node),
iD.actions.AddEntity(way),
iD.actions.AddVertex(way.id, node.id));
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
context.enter(iD.modes.DrawLine(context, way.id, 'forward', graph));
}
behavior = iD.behavior.AddWay(mode)
behavior = iD.behavior.AddWay(context)
.on('start', start)
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode)
.on('startFromMidpoint', startFromMidpoint);
mode.map.surface.call(behavior);
mode.map.tail(t('modes.add_line.tail'));
context.install(behavior);
context.tail(t('modes.add_line.tail'));
};
mode.exit = function() {
mode.map.surface.call(behavior.off);
context.uninstall(behavior);
};
return mode;
+9 -16
View File
@@ -1,4 +1,4 @@
iD.modes.AddPoint = function() {
iD.modes.AddPoint = function(context) {
var mode = {
id: 'add-point',
title: t('modes.add_point.title'),
@@ -9,18 +9,14 @@ iD.modes.AddPoint = function() {
var behavior;
mode.enter = function() {
var map = mode.map,
history = mode.history,
controller = mode.controller;
function add(loc) {
var node = iD.Node({loc: loc});
history.perform(
context.perform(
iD.actions.AddEntity(node),
t('operations.add.annotation.point'));
controller.enter(iD.modes.Select([node.id], true));
context.enter(iD.modes.Select(context, [node.id], true));
}
function addWay(way, loc, index) {
@@ -32,10 +28,10 @@ iD.modes.AddPoint = function() {
}
function cancel() {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
}
behavior = iD.behavior.Draw(map)
behavior = iD.behavior.Draw(context)
.on('click', add)
.on('clickWay', addWay)
.on('clickNode', addNode)
@@ -43,16 +39,13 @@ iD.modes.AddPoint = function() {
.on('cancel', cancel)
.on('finish', cancel);
mode.map.surface.call(behavior);
mode.map.tail(t('modes.add_point.tail'));
context.install(behavior);
context.tail(t('modes.add_point.tail'));
};
mode.exit = function() {
var map = mode.map,
surface = map.surface;
map.tail(false);
behavior.off(surface);
context.tail(false);
context.uninstall(behavior);
};
return mode;
+8 -14
View File
@@ -1,4 +1,4 @@
iD.modes.Browse = function() {
iD.modes.Browse = function(context) {
var mode = {
button: 'browse',
id: 'browse',
@@ -7,27 +7,21 @@ iD.modes.Browse = function() {
key: t('modes.browse.key')
};
var behaviors;
var behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(context),
iD.behavior.DragNode(context),
iD.behavior.DragMidpoint(context)];
mode.enter = function() {
var surface = mode.map.surface;
behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(mode),
iD.behavior.DragNode(mode),
iD.behavior.DragMidpoint(mode)];
behaviors.forEach(function(behavior) {
behavior(surface);
context.install(behavior);
});
};
mode.exit = function() {
var surface = mode.map.surface;
behaviors.forEach(function(behavior) {
behavior.off(surface);
context.uninstall(behavior);
});
};
+6 -6
View File
@@ -1,4 +1,4 @@
iD.modes.DrawArea = function(wayId, baseGraph) {
iD.modes.DrawArea = function(context, wayId, baseGraph) {
var mode = {
button: 'area',
id: 'draw-area'
@@ -7,11 +7,11 @@ iD.modes.DrawArea = function(wayId, baseGraph) {
var behavior;
mode.enter = function() {
var way = mode.history.graph().entity(wayId),
var way = context.entity(wayId),
headId = way.nodes[way.nodes.length - 2],
tailId = way.first();
behavior = iD.behavior.DrawWay(wayId, -1, mode, baseGraph);
behavior = iD.behavior.DrawWay(context, wayId, -1, mode, baseGraph);
var addNode = behavior.addNode;
@@ -23,12 +23,12 @@ iD.modes.DrawArea = function(wayId, baseGraph) {
}
};
mode.map.surface.call(behavior);
mode.map.tail(t('modes.draw_area.tail'));
context.install(behavior);
context.tail(t('modes.draw_area.tail'));
};
mode.exit = function() {
mode.map.surface.call(behavior.off);
context.uninstall(behavior);
};
return mode;
+6 -6
View File
@@ -1,4 +1,4 @@
iD.modes.DrawLine = function(wayId, direction, baseGraph) {
iD.modes.DrawLine = function(context, wayId, direction, baseGraph) {
var mode = {
button: 'line',
id: 'draw-line'
@@ -7,11 +7,11 @@ iD.modes.DrawLine = function(wayId, direction, baseGraph) {
var behavior;
mode.enter = function() {
var way = mode.history.graph().entity(wayId),
var way = context.entity(wayId),
index = (direction === 'forward') ? undefined : 0,
headId = (direction === 'forward') ? way.last() : way.first();
behavior = iD.behavior.DrawWay(wayId, index, mode, baseGraph);
behavior = iD.behavior.DrawWay(context, wayId, index, mode, baseGraph);
var addNode = behavior.addNode;
@@ -23,12 +23,12 @@ iD.modes.DrawLine = function(wayId, direction, baseGraph) {
}
};
mode.map.surface.call(behavior);
mode.map.tail(t('modes.draw_line.tail'));
context.install(behavior);
context.tail(t('modes.draw_line.tail'));
};
mode.exit = function() {
mode.map.surface.call(behavior.off);
context.uninstall(behavior);
};
return mode;
+21 -26
View File
@@ -1,4 +1,4 @@
iD.modes.MoveWay = function(wayId) {
iD.modes.MoveWay = function(context, wayId) {
var mode = {
id: 'move-way'
};
@@ -6,55 +6,53 @@ iD.modes.MoveWay = function(wayId) {
var keybinding = d3.keybinding('move-way');
mode.enter = function() {
var map = mode.map,
history = mode.history,
graph = history.graph(),
selection = map.surface,
controller = mode.controller,
projection = map.projection,
way = graph.entity(wayId),
origin = d3.mouse(selection.node()),
annotation = t('operations.move.annotation.' + way.geometry(graph));
var origin = point(),
annotation = t('operations.move.annotation.' + context.geometry(wayId));
// If intiated via keyboard
if (!origin[0] && !origin[1]) origin = null;
history.perform(
context.perform(
iD.actions.Noop(),
annotation);
function point() {
return d3.mouse(context.surface().node());
}
function move() {
var p = d3.mouse(selection.node()),
var p = point(),
delta = origin ?
[p[0] - origin[0], p[1] - origin[1]] :
[0, 0];
origin = p;
history.replace(
iD.actions.MoveWay(wayId, delta, projection),
context.replace(
iD.actions.MoveWay(wayId, delta, context.projection),
annotation);
}
function finish() {
d3.event.stopPropagation();
controller.enter(iD.modes.Select([way.id], true));
context.enter(iD.modes.Select(context, [wayId], true));
}
function cancel() {
history.pop();
controller.enter(iD.modes.Select([way.id], true));
context.pop();
context.enter(iD.modes.Select(context, [wayId], true));
}
function undone() {
controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
}
selection
context.selection()
.on('mousemove.move-way', move)
.on('click.move-way', finish);
history.on('undone.move-way', undone);
context.history()
.on('undone.move-way', undone);
keybinding
.on('⎋', cancel)
@@ -65,15 +63,12 @@ iD.modes.MoveWay = function(wayId) {
};
mode.exit = function() {
var map = mode.map,
history = mode.history,
selection = map.surface;
selection
context.selection()
.on('mousemove.move-way', null)
.on('click.move-way', null);
history.on('undone.move-way', null);
context.history()
.on('undone.move-way', null);
keybinding.off();
};
+38 -43
View File
@@ -1,4 +1,4 @@
iD.modes.Select = function(selection, initial) {
iD.modes.Select = function(context, selection, initial) {
var mode = {
id: 'select',
button: 'browse'
@@ -6,12 +6,16 @@ iD.modes.Select = function(selection, initial) {
var inspector = iD.ui.inspector().initial(!!initial),
keybinding = d3.keybinding('select'),
behaviors,
behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(context),
iD.behavior.DragNode(context),
iD.behavior.DragMidpoint(context)],
radialMenu;
function changeTags(d, tags) {
if (!_.isEqual(singular().tags, tags)) {
mode.history.perform(
context.perform(
iD.actions.ChangeTags(d.id, tags),
t('operations.change_tags.annotation'));
}
@@ -19,7 +23,7 @@ iD.modes.Select = function(selection, initial) {
function singular() {
if (selection.length === 1) {
return mode.map.history().graph().entity(selection[0]);
return context.entity(selection[0]);
}
}
@@ -28,24 +32,14 @@ iD.modes.Select = function(selection, initial) {
};
mode.enter = function() {
var map = mode.map,
history = map.history(),
graph = history.graph(),
surface = map.surface,
entity = singular();
behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(mode),
iD.behavior.DragNode(mode),
iD.behavior.DragMidpoint(mode)];
var entity = singular();
behaviors.forEach(function(behavior) {
behavior(surface);
context.install(behavior);
});
var operations = d3.values(iD.operations)
.map(function (o) { return o(selection, mode); })
.map(function (o) { return o(selection, context); })
.filter(function (o) { return o.available(); });
operations.forEach(function(operation) {
@@ -62,9 +56,10 @@ iD.modes.Select = function(selection, initial) {
}), true));
if (entity) {
inspector.graph(graph);
inspector.graph(context.graph());
d3.select('.inspector-wrap')
context.container()
.select('.inspector-wrap')
.style('display', 'block')
.style('opacity', 1)
.datum(entity)
@@ -73,38 +68,38 @@ iD.modes.Select = function(selection, initial) {
if (d3.event) {
// Pan the map if the clicked feature intersects with the position
// of the inspector
var inspector_size = d3.select('.inspector-wrap').size(),
map_size = mode.map.size(),
var inspector_size = context.container().select('.inspector-wrap').size(),
map_size = context.map().size(),
offset = 50,
shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset,
center = (map_size[0] / 2) + shift_left + offset;
if (shift_left > 0 && inspector_size[1] > d3.event.y) {
mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2]));
context.map().centerEase(context.projection.invert([center, map_size[1]/2]));
}
}
inspector
.on('changeTags', changeTags)
.on('close', function() { mode.controller.enter(iD.modes.Browse()); });
.on('close', function() { context.enter(iD.modes.Browse(context)); });
history.on('change.select', function() {
context.history().on('change.select', function() {
// Exit mode if selected entity gets undone
var oldEntity = entity,
newEntity = history.graph().entity(selection[0]);
newEntity = context.entity(selection[0]);
if (!newEntity) {
mode.controller.enter(iD.modes.Browse());
context.enter(iD.modes.Browse(context));
} else if (!_.isEqual(oldEntity.tags, newEntity.tags)) {
inspector.tags(newEntity.tags);
}
surface.call(radialMenu.close);
context.surface().call(radialMenu.close);
});
}
map.on('move.select', function() {
surface.call(radialMenu.close);
context.map().on('move.select', function() {
context.surface().call(radialMenu.close);
});
function dblclick() {
@@ -113,10 +108,10 @@ iD.modes.Select = function(selection, initial) {
if (datum instanceof iD.Way && !target.classed('fill')) {
var choice = iD.geo.chooseIndex(datum,
d3.mouse(mode.map.surface.node()), mode.map),
d3.mouse(context.surface().node()), context.map()),
node = iD.Node({ loc: choice.loc });
history.perform(
context.perform(
iD.actions.AddEntity(node),
iD.actions.AddVertex(datum.id, node.id, choice.index),
t('operations.add.annotation.vertex'));
@@ -129,7 +124,8 @@ iD.modes.Select = function(selection, initial) {
d3.select(document)
.call(keybinding);
surface.on('dblclick.select', dblclick)
context.surface()
.on('dblclick.select', dblclick)
.selectAll("*")
.filter(function (d) { return d && selection.indexOf(d.id) >= 0; })
.classed('selected', true);
@@ -137,25 +133,23 @@ iD.modes.Select = function(selection, initial) {
radialMenu = iD.ui.RadialMenu(operations);
if (d3.event && !initial) {
var loc = map.mouseCoordinates();
var loc = context.map().mouseCoordinates();
if (entity && entity.type === 'node') {
loc = entity.loc;
}
surface.call(radialMenu, map.projection(loc));
context.surface().call(radialMenu, context.projection(loc));
}
};
mode.exit = function () {
var surface = mode.map.surface,
history = mode.history;
if (singular()) {
changeTags(singular(), inspector.tags());
}
d3.select('.inspector-wrap')
context.container()
.select('.inspector-wrap')
.style('display', 'none')
.html('');
@@ -164,7 +158,7 @@ iD.modes.Select = function(selection, initial) {
d3.selectAll('div.typeahead').remove();
behaviors.forEach(function(behavior) {
behavior.off(surface);
context.uninstall(behavior);
});
var q = iD.util.stringQs(location.hash.substring(1));
@@ -172,13 +166,14 @@ iD.modes.Select = function(selection, initial) {
keybinding.off();
history.on('change.select', null);
context.history()
.on('change.select', null);
surface.on('dblclick.select', null)
context.surface()
.call(radialMenu.close)
.on('dblclick.select', null)
.selectAll(".selected")
.classed('selected', false);
surface.call(radialMenu.close);
};
return mode;
+7 -13
View File
@@ -1,25 +1,19 @@
iD.operations.Circularize = function(selection, mode) {
iD.operations.Circularize = function(selection, context) {
var entityId = selection[0],
history = mode.map.history(),
action = iD.actions.Circularize(entityId, mode.map);
action = iD.actions.Circularize(entityId, context.map());
var operation = function() {
var graph = history.graph(),
entity = graph.entity(entityId),
annotation = t('operations.circularize.annotation.' + entity.geometry(graph));
history.perform(action, annotation);
var annotation = t('operations.circularize.annotation.' + context.geometry(entityId));
context.perform(action, annotation);
};
operation.available = function() {
var graph = history.graph(),
entity = graph.entity(entityId);
return selection.length === 1 && entity.type === 'way';
return selection.length === 1 &&
context.entity(entityId).type === 'way';
};
operation.enabled = function() {
var graph = history.graph();
return action.enabled(graph);
return action.enabled(context.graph());
};
operation.id = "circularize";
+6 -9
View File
@@ -1,21 +1,18 @@
iD.operations.Delete = function(selection, mode) {
var entityId = selection[0],
history = mode.map.history();
iD.operations.Delete = function(selection, context) {
var entityId = selection[0];
var operation = function() {
var graph = history.graph(),
entity = graph.entity(entityId),
var entity = context.entity(entityId),
action = {way: iD.actions.DeleteWay, node: iD.actions.DeleteNode}[entity.type],
annotation = t('operations.delete.annotation.' + entity.geometry(graph));
annotation = t('operations.delete.annotation.' + context.geometry(entityId));
history.perform(
context.perform(
action(entityId),
annotation);
};
operation.available = function() {
var graph = history.graph(),
entity = graph.entity(entityId);
var entity = context.entity(entityId);
return selection.length === 1 &&
(entity.type === 'way' || entity.type === 'node');
};
+4 -6
View File
@@ -1,15 +1,13 @@
iD.operations.Move = function(selection, mode) {
var entityId = selection[0],
history = mode.map.history();
iD.operations.Move = function(selection, context) {
var entityId = selection[0];
var operation = function() {
mode.controller.enter(iD.modes.MoveWay(entityId));
context.enter(iD.modes.MoveWay(context, entityId));
};
operation.available = function() {
var graph = history.graph();
return selection.length === 1 &&
graph.entity(entityId).type === 'way';
context.entity(entityId).type === 'way';
};
operation.enabled = function() {
+4 -7
View File
@@ -1,18 +1,15 @@
iD.operations.Reverse = function(selection, mode) {
var entityId = selection[0],
history = mode.map.history();
iD.operations.Reverse = function(selection, context) {
var entityId = selection[0];
var operation = function() {
history.perform(
context.perform(
iD.actions.ReverseWay(entityId),
t('operations.reverse.annotation'));
};
operation.available = function() {
var graph = history.graph(),
entity = graph.entity(entityId);
return selection.length === 1 &&
entity.geometry(graph) === 'line';
context.geometry(entityId) === 'line';
};
operation.enabled = function() {
+4 -8
View File
@@ -1,22 +1,18 @@
iD.operations.Split = function(selection, mode) {
iD.operations.Split = function(selection, context) {
var entityId = selection[0],
history = mode.map.history(),
action = iD.actions.SplitWay(entityId);
var operation = function() {
history.perform(action, t('operations.split.annotation'));
context.perform(action, t('operations.split.annotation'));
};
operation.available = function() {
var graph = history.graph(),
entity = graph.entity(entityId);
return selection.length === 1 &&
entity.geometry(graph) === 'vertex';
context.geometry(entityId) === 'vertex';
};
operation.enabled = function() {
var graph = history.graph();
return action.enabled(graph);
return action.enabled(context.graph());
};
operation.id = "split";
+4 -8
View File
@@ -1,22 +1,18 @@
iD.operations.Unjoin = function(selection, mode) {
iD.operations.Unjoin = function(selection, context) {
var entityId = selection[0],
history = mode.map.history(),
action = iD.actions.UnjoinNode(entityId);
var operation = function() {
history.perform(action, 'Unjoined lines.');
context.perform(action, 'Unjoined lines.');
};
operation.available = function() {
var graph = history.graph(),
entity = graph.entity(entityId);
return selection.length === 1 &&
entity.geometry(graph) === 'vertex';
context.geometry(entityId) === 'vertex';
};
operation.enabled = function() {
var graph = history.graph();
return action.enabled(graph);
return action.enabled(context.graph());
};
operation.id = "unjoin";
+6 -23
View File
@@ -1,11 +1,8 @@
iD.ui.save = function() {
var map, controller;
function save(selection) {
var history = map.history(),
connection = map.connection(),
iD.ui.save = function(context) {
return function (selection) {
var map = context.map(),
history = context.history(),
connection = context.connection(),
tooltip = bootstrap.tooltip()
.placement('bottom');
@@ -60,7 +57,7 @@ iD.ui.save = function() {
.on('fix', function(d) {
map.extent(d.entity.extent(map.history().graph()));
if (map.zoom() > 19) map.zoom(19);
controller.enter(iD.modes.Select([d.entity.id]));
context.enter(iD.modes.Select(context, [d.entity.id]));
modal.remove();
})
.on('save', commit));
@@ -88,19 +85,5 @@ iD.ui.save = function() {
selection.call(tooltip.hide);
}
});
}
save.map = function(_) {
if (!arguments.length) return map;
map = _;
return save;
};
save.controller = function(_) {
if (!arguments.length) return controller;
controller = _;
return save;
};
return save;
};
+6 -5
View File
@@ -119,6 +119,7 @@
<script src='../js/id/graph/way.js'></script>
<script src='../js/id/connection.js'></script>
<script src='../js/id/context.js'></script>
<script src='../js/id/controller.js'></script>
<script src='../locale/locale.js'></script>
@@ -150,9 +151,6 @@
<script src="spec/actions/split_way.js"></script>
<script src='spec/actions/unjoin_node.js'></script>
<script src="spec/behavior/hash.js"></script>
<script src="spec/behavior/hover.js"></script>
<script src="spec/geo/extent.js"></script>
<script src="spec/graph/graph.js"></script>
@@ -162,8 +160,6 @@
<script src="spec/graph/relation.js"></script>
<script src="spec/graph/history.js"></script>
<script src="spec/modes/add_point.js"></script>
<script src="spec/renderer/background.js"></script>
<script src="spec/renderer/map.js"></script>
@@ -189,6 +185,11 @@
<script src="spec/taginfo.js"></script>
<script src="spec/util.js"></script>
<script src="spec/behavior/hash.js"></script>
<script src="spec/behavior/hover.js"></script>
<script src="spec/modes/add_point.js"></script>
<script>
(window.mochaPhantomJS || window.mocha).run();
</script>
+5 -5
View File
@@ -44,9 +44,6 @@
<script src="spec/actions/split_way.js"></script>
<script src='spec/actions/unjoin_node.js'></script>
<script src="spec/behavior/hash.js"></script>
<script src="spec/behavior/hover.js"></script>
<script src="spec/geo/extent.js"></script>
<script src="spec/graph/graph.js"></script>
@@ -56,8 +53,6 @@
<script src="spec/graph/relation.js"></script>
<script src="spec/graph/history.js"></script>
<script src="spec/modes/add_point.js"></script>
<script src="spec/renderer/background.js"></script>
<script src="spec/renderer/map.js"></script>
@@ -83,6 +78,11 @@
<script src="spec/taginfo.js"></script>
<script src="spec/util.js"></script>
<script src="spec/behavior/hash.js"></script>
<script src="spec/behavior/hover.js"></script>
<script src="spec/modes/add_point.js"></script>
<script>
(window.mochaPhantomJS || window.mocha).run();
</script>
+33 -37
View File
@@ -1,19 +1,18 @@
describe("iD.behavior.Hash", function () {
var hash, map, controller;
mocha.globals('__onhashchange.hash');
var hash, context;
beforeEach(function () {
map = {
on: function () { return map; },
zoom: function () { return arguments.length ? map : 0; },
center: function () { return arguments.length ? map : [0, 0]; },
centerZoom: function () { return arguments.length ? map : [0, 0]; }
};
context = iD.Context();
controller = {
on: function () { return controller; }
};
// Neuter connection
context.connection().loadTiles = function () {};
hash = iD.behavior.Hash(controller, map);
hash = iD.behavior.Hash(context);
d3.select(document.createElement('div'))
.call(context.map());
});
afterEach(function () {
@@ -22,44 +21,41 @@ describe("iD.behavior.Hash", function () {
it("sets hadHash if location.hash is present", function () {
location.hash = "map=20.00/38.87952/-77.02405";
hash();
expect(hash.hadHash).to.be.true;
});
it("centerZooms map to requested level", function () {
location.hash = "map=20.00/38.87952/-77.02405";
sinon.spy(map, 'centerZoom');
hash();
expect(map.centerZoom).to.have.been.calledWith([-77.02405,38.87952], 20.0);
expect(context.map().center()[0]).to.be.closeTo(-77.02405, 0.1);
expect(context.map().center()[1]).to.be.closeTo(38.87952, 0.1);
expect(context.map().zoom()).to.equal(20.0);
});
describe("on window hashchange events", function () {
beforeEach(function () {
hash();
it("centerZooms map at requested coordinates on hash change", function (done) {
hash();
d3.select(window).one('hashchange', function () {
expect(context.map().center()[0]).to.be.closeTo(-77.02405, 0.1);
expect(context.map().center()[1]).to.be.closeTo(38.87952, 0.1);
expect(context.map().zoom()).to.equal(20.0);
done();
});
function onhashchange(fn) {
d3.select(window).one("hashchange", fn);
}
it("centerZooms map at requested coordinates", function (done) {
onhashchange(function () {
expect(map.centerZoom).to.have.been.calledWith([-77.02405,38.87952], 20.0);
done();
});
sinon.spy(map, 'centerZoom');
location.hash = "#map=20.00/38.87952/-77.02405";
});
location.hash = "#map=20.00/38.87952/-77.02405";
});
describe("on map move events", function () {
it("stores the current zoom and coordinates in location.hash", function () {
sinon.stub(map, 'on')
.withArgs("move.hash", sinon.match.instanceOf(Function))
.yields();
hash();
expect(location.hash).to.equal("#map=0.00/0/0");
});
it("stores the current zoom and coordinates in location.hash on map move events", function () {
hash();
context.map().center([38.9, -77.0]);
context.map().zoom(2.0);
expect(location.hash).to.equal("#map=2.00/-77.0/38.9");
});
});
+14 -19
View File
@@ -1,41 +1,36 @@
describe("iD.modes.AddPoint", function () {
var container, map, history, controller, mode;
var context;
beforeEach(function () {
container = d3.select('body').append('div');
history = iD.History();
map = iD.Map().history(history);
controller = iD.Controller(map, history);
var container = d3.select(document.createElement('div'));
container.call(map);
container.append('div')
context = iD.Context()
.container(container);
container.call(context.map())
.append('div')
.attr('class', 'inspector-wrap');
mode = iD.modes.AddPoint();
controller.enter(mode);
});
afterEach(function() {
container.remove();
context.enter(iD.modes.AddPoint(context));
});
describe("clicking the map", function () {
it("adds a node", function () {
happen.click(map.surface.node(), {});
expect(history.changes().created).to.have.length(1);
happen.click(context.surface().node(), {});
expect(context.changes().created).to.have.length(1);
});
it("selects the node", function () {
happen.click(map.surface.node(), {});
expect(controller.mode.id).to.equal('select');
expect(controller.mode.selection()).to.eql([history.changes().created[0].id]);
happen.click(context.surface().node(), {});
expect(context.mode().id).to.equal('select');
expect(context.mode().selection()).to.eql([context.changes().created[0].id]);
});
});
describe("pressing ⎋", function () {
it("exits to browse mode", function () {
happen.keydown(document, {keyCode: 27});
expect(controller.mode.id).to.equal('browse');
expect(context.mode().id).to.equal('browse');
});
});
});
+3
View File
@@ -2,10 +2,13 @@ describe("iD.ui.confirm", function () {
it('can be instantiated', function () {
var confirm = iD.ui.confirm();
expect(confirm).to.be.ok;
happen.keydown(document, {keyCode: 27}); // dismiss
});
it('can be dismissed', function () {
var confirm = iD.ui.confirm();
happen.click(confirm.select('button').node());
expect(confirm.node().parentNode).to.be.null;
happen.keydown(document, {keyCode: 27}); // dismiss
});
});
+1
View File
@@ -4,5 +4,6 @@ describe("iD.ui.modal", function () {
.select('.content')
.text('foo');
expect(modal).to.be.ok;
happen.keydown(document, {keyCode: 27}); // dismiss
});
});