diff --git a/Makefile b/Makefile index 1db4f2efb..598bce278 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/index.html b/index.html index c8ddd7562..306575804 100644 --- a/index.html +++ b/index.html @@ -122,9 +122,10 @@ + + - diff --git a/js/id/behavior/add_way.js b/js/id/behavior/add_way.js index 89ba7f1cd..207c88566 100644 --- a/js/id/behavior/add_way.js +++ b/js/id/behavior/add_way.js @@ -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'); diff --git a/js/id/behavior/drag_midpoint.js b/js/id/behavior/drag_midpoint.js index 6041946cd..42099963c 100644 --- a/js/id/behavior/drag_midpoint.js +++ b/js/id/behavior/drag_midpoint.js @@ -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')); }); diff --git a/js/id/behavior/drag_node.js b/js/id/behavior/drag_node.js index b9846617d..b62162e94 100644 --- a/js/id/behavior/drag_node.js +++ b/js/id/behavior/drag_node.js @@ -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)); }); diff --git a/js/id/behavior/draw.js b/js/id/behavior/draw.js index bce4470b6..8405ac341 100644 --- a/js/id/behavior/draw.js +++ b/js/id/behavior/draw.js @@ -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) diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index 1cc62b755..28a8fbb38 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -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'); diff --git a/js/id/behavior/hash.js b/js/id/behavior/hash.js index 65e748e28..871b73a35 100644 --- a/js/id/behavior/hash.js +++ b/js/id/behavior/hash.js @@ -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); diff --git a/js/id/behavior/select.js b/js/id/behavior/select.js index cee2cc940..e078fdbf1 100644 --- a/js/id/behavior/select.js +++ b/js/id/behavior/select.js @@ -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)); } } diff --git a/js/id/context.js b/js/id/context.js new file mode 100644 index 000000000..60f878b32 --- /dev/null +++ b/js/id/context.js @@ -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; +}; diff --git a/js/id/controller.js b/js/id/controller.js index 6c4e0fd1e..af1e7c388 100644 --- a/js/id/controller.js +++ b/js/id/controller.js @@ -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); diff --git a/js/id/id.js b/js/id/id.js index 3e8aabcc3..3c2d0c482 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -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(); diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index bda4e0daa..ae2e008ee 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -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; diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index 92e9a64d7..79c5f3031 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -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; diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index 6dd524bb5..c3dc7f075 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -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; diff --git a/js/id/modes/browse.js b/js/id/modes/browse.js index 7652576ea..33add1262 100644 --- a/js/id/modes/browse.js +++ b/js/id/modes/browse.js @@ -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); }); }; diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index 9801a092c..aee2f153c 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -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; diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index 5ae8c2703..9fd07c937 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -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; diff --git a/js/id/modes/move_way.js b/js/id/modes/move_way.js index 5c262e259..30c473406 100644 --- a/js/id/modes/move_way.js +++ b/js/id/modes/move_way.js @@ -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(); }; diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 8d29450d6..df445862d 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -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; diff --git a/js/id/operations/circularize.js b/js/id/operations/circularize.js index 4b0f27bf9..419a6e9eb 100644 --- a/js/id/operations/circularize.js +++ b/js/id/operations/circularize.js @@ -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"; diff --git a/js/id/operations/delete.js b/js/id/operations/delete.js index 12df22a09..d631a2ed7 100644 --- a/js/id/operations/delete.js +++ b/js/id/operations/delete.js @@ -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'); }; diff --git a/js/id/operations/move.js b/js/id/operations/move.js index 10405cc53..d9da39f64 100644 --- a/js/id/operations/move.js +++ b/js/id/operations/move.js @@ -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() { diff --git a/js/id/operations/reverse.js b/js/id/operations/reverse.js index 78b8789dd..77bacc64e 100644 --- a/js/id/operations/reverse.js +++ b/js/id/operations/reverse.js @@ -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() { diff --git a/js/id/operations/split.js b/js/id/operations/split.js index 4274fc41b..0d7ff6ae4 100644 --- a/js/id/operations/split.js +++ b/js/id/operations/split.js @@ -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"; diff --git a/js/id/operations/unjoin.js b/js/id/operations/unjoin.js index 2a40ec067..d4fcc1426 100644 --- a/js/id/operations/unjoin.js +++ b/js/id/operations/unjoin.js @@ -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"; diff --git a/js/id/ui/save.js b/js/id/ui/save.js index b8085e21a..6098519da 100644 --- a/js/id/ui/save.js +++ b/js/id/ui/save.js @@ -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; }; diff --git a/test/index.html b/test/index.html index b85eb1b56..b56645ee7 100644 --- a/test/index.html +++ b/test/index.html @@ -119,6 +119,7 @@ + @@ -150,9 +151,6 @@ - - - @@ -162,8 +160,6 @@ - - @@ -189,6 +185,11 @@ + + + + + diff --git a/test/index_packaged.html b/test/index_packaged.html index 60979e236..11f8368f4 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -44,9 +44,6 @@ - - - @@ -56,8 +53,6 @@ - - @@ -83,6 +78,11 @@ + + + + + diff --git a/test/spec/behavior/hash.js b/test/spec/behavior/hash.js index 7616bf536..5aceb53fc 100644 --- a/test/spec/behavior/hash.js +++ b/test/spec/behavior/hash.js @@ -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"); }); }); diff --git a/test/spec/modes/add_point.js b/test/spec/modes/add_point.js index a284cf682..5c544c0c5 100644 --- a/test/spec/modes/add_point.js +++ b/test/spec/modes/add_point.js @@ -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'); }); }); }); diff --git a/test/spec/ui/confirm.js b/test/spec/ui/confirm.js index a9380e1cc..428a8aa6c 100644 --- a/test/spec/ui/confirm.js +++ b/test/spec/ui/confirm.js @@ -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 }); }); diff --git a/test/spec/ui/modal.js b/test/spec/ui/modal.js index 32a42878a..d7f818d80 100644 --- a/test/spec/ui/modal.js +++ b/test/spec/ui/modal.js @@ -4,5 +4,6 @@ describe("iD.ui.modal", function () { .select('.content') .text('foo'); expect(modal).to.be.ok; + happen.keydown(document, {keyCode: 27}); // dismiss }); });