diff --git a/index.html b/index.html index 122569faa..51ddec95d 100644 --- a/index.html +++ b/index.html @@ -53,7 +53,8 @@ - + + diff --git a/js/id/actions/move.js b/js/id/actions/move_node.js similarity index 64% rename from js/id/actions/move.js rename to js/id/actions/move_node.js index 21870c8d4..9204fcb67 100644 --- a/js/id/actions/move.js +++ b/js/id/actions/move_node.js @@ -1,8 +1,8 @@ // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as -iD.actions.Move = function(entityId, loc) { +iD.actions.MoveNode = function(nodeId, loc) { return function(graph) { - var entity = graph.entity(entityId); - return graph.replace(entity.update({loc: loc})); + var node = graph.entity(nodeId); + return graph.replace(node.update({loc: loc})); }; }; diff --git a/js/id/actions/move_way.js b/js/id/actions/move_way.js new file mode 100644 index 000000000..0ef14e4f3 --- /dev/null +++ b/js/id/actions/move_way.js @@ -0,0 +1,14 @@ +iD.actions.MoveWay = function(wayId, dxdy, projection) { + return function(graph) { + var way = graph.entity(wayId); + + _.uniq(way.nodes).forEach(function(id) { + var node = graph.entity(id), + start = projection(node.loc), + end = projection.invert([start[0] + dxdy[0], start[1] + dxdy[1]]); + graph = iD.actions.MoveNode(id, end)(graph); + }); + + return graph; + }; +}; diff --git a/js/id/id.js b/js/id/id.js index 071c0773d..8a65570e4 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -36,14 +36,36 @@ window.iD = function(container) { .call(bootstrap.tooltip().placement('bottom')) .on('click', function (mode) { controller.enter(mode); }); - map.on('move.disable-buttons', function() { + function disableTooHigh() { if (map.zoom() < 16) { buttons.attr('disabled', 'disabled'); controller.enter(iD.modes.Browse()); } else { buttons.attr('disabled', null); } - }); + } + + var showUsers = _.debounce(function() { + var users = {}, + entities = map.history().graph().entities; + for (var i in entities) { + users[entities[i].user] = true; + if (Object.keys(users).length > 10) break; + } + var u = Object.keys(users); + var l = d3.select('#user-list') + .selectAll('a.user-link').data(u); + l.enter().append('a') + .attr('class', 'user-link') + .attr('href', function(d) { + return 'http://api06.dev.openstreetmap.org/user/' + d; + }) + .text(String); + l.exit().remove(); + }, 1000); + + map.on('move.disable-buttons', disableTooHigh) + .on('move.show-users', showUsers); buttons.append('span') .attr('class', function(d) { @@ -143,12 +165,15 @@ window.iD = function(container) { .attr('class', 'inspector-wrap fillL') .style('display', 'none'); - this.append('div') + var about = this.append('div') .attr('id', 'about') .html("code " + "report a bug" + " "); + about.append('div') + .attr('id', 'user-list'); + history.on('change.buttons', function() { var undo = history.undoAnnotation(), redo = history.redoAnnotation(); @@ -166,7 +191,7 @@ window.iD = function(container) { map.size(m.size()); }; - var keybinding = d3.keybinding() + map.keybinding() .on('a', function(evt, mods) { controller.enter(iD.modes.AddArea()); }) @@ -180,8 +205,6 @@ window.iD = function(container) { if (mods === '⇧⌘') history.redo(); if (mods === '⌘') history.undo(); }); - d3.select(document).call(keybinding); - map.keybinding(keybinding); var hash = iD.Hash().map(map); diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index bf5172b4a..baa4364bc 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -15,7 +15,7 @@ iD.modes.AddLine = function() { map.dblclickEnable(false) .hint('Click on the map to start drawing an road, path, or route.'); - map.surface.on('click.addroad', function() { + map.surface.on('click.addline', function() { var datum = d3.select(d3.event.target).datum() || {}, way = iD.Way({ tags: { highway: 'residential' } }), direction = 'forward'; @@ -62,7 +62,7 @@ iD.modes.AddLine = function() { controller.enter(iD.modes.DrawLine(way.id, direction)); }); - map.keybinding().on('⎋.addroad', function() { + map.keybinding().on('⎋.addline', function() { controller.exit(); }); }; @@ -70,8 +70,8 @@ iD.modes.AddLine = function() { mode.exit = function() { mode.map.dblclickEnable(true); mode.map.hint(false); - mode.map.surface.on('click.addroad', null); - mode.map.keybinding().on('⎋.addroad', null); + mode.map.surface.on('click.addline', null); + mode.map.keybinding().on('⎋.addline', null); }; return mode; diff --git a/js/id/modes/drag_features.js b/js/id/modes/drag_features.js index 7a38883ed..d20119080 100644 --- a/js/id/modes/drag_features.js +++ b/js/id/modes/drag_features.js @@ -4,6 +4,7 @@ iD.modes._dragFeatures = function(mode) { var dragbehavior = d3.behavior.drag() .origin(function(entity) { var p = mode.map.projection(entity.loc); + d3.event.sourceEvent.stopPropagation(); return { x: p[0], y: p[1] }; }) .on('drag', function(entity) { @@ -20,11 +21,11 @@ iD.modes._dragFeatures = function(mode) { } else { dragging = entity; mode.history.perform( - iD.actions.Move(dragging.id, loc)); + iD.actions.MoveNode(dragging.id, loc)); } } - mode.history.replace(iD.actions.Move(dragging.id, loc)); + mode.history.replace(iD.actions.MoveNode(dragging.id, loc)); }) .on('dragend', function (entity) { if (!dragging) return; diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index ae5925ea0..c31544e5c 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -24,7 +24,7 @@ iD.modes.DrawArea = function(wayId) { iD.actions.AddWayNode(way.id, node.id, -1)); function mousemove() { - history.replace(iD.actions.Move(node.id, map.mouseCoordinates())); + history.replace(iD.actions.MoveNode(node.id, map.mouseCoordinates())); } function click() { @@ -95,24 +95,32 @@ iD.modes.DrawArea = function(wayId) { controller.enter(iD.modes.Browse()); } - map.surface.on('mousemove.drawarea', mousemove); - map.surface.on('click.drawarea', click); - map.keybinding().on('⎋.drawarea', esc) + map.surface + .on('mousemove.drawarea', mousemove) + .on('click.drawarea', click); + + map.keybinding() + .on('⎋.drawarea', esc) .on('⌫.drawarea', backspace) - .on('delete.drawarea', del) + .on('⌦.drawarea', del) .on('↩.drawarea', ret); }; mode.exit = function() { - mode.map.hint(false); - mode.map.fastEnable(true); + mode.map + .hint(false) + .fastEnable(true); + mode.map.surface .on('mousemove.drawarea', null) .on('click.drawarea', null); - mode.map.keybinding().on('⎋.drawarea', null) + + mode.map.keybinding() + .on('⎋.drawarea', null) .on('⌫.drawarea', null) - .on('delete.drawarea', null) + .on('⌦.drawarea', null) .on('↩.drawarea', null); + window.setTimeout(function() { mode.map.dblclickEnable(true); }, 1000); diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index 09789c8f7..74c16095f 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -24,11 +24,11 @@ iD.modes.DrawLine = function(wayId, direction) { iD.actions.AddNode(node), iD.actions.AddWayNode(wayId, node.id, index)); - map.surface.on('mousemove.drawline', function() { - history.replace(iD.actions.Move(node.id, map.mouseCoordinates())); - }); + function mousemove() { + history.replace(iD.actions.MoveNode(node.id, map.mouseCoordinates())); + } - map.surface.on('click.drawline', function() { + function click() { var datum = d3.select(d3.event.target).datum() || {}; if (datum.id === tailId) { @@ -72,7 +72,7 @@ iD.modes.DrawLine = function(wayId, direction) { controller.enter(iD.modes.DrawLine(wayId, direction)); } - }); + } function esc() { history.replace( @@ -108,23 +108,32 @@ iD.modes.DrawLine = function(wayId, direction) { controller.enter(iD.modes.Browse()); } - map.keybinding().on('⎋.drawline', esc) + map.surface + .on('mousemove.drawline', mousemove) + .on('click.drawline', click); + + map.keybinding() + .on('⎋.drawline', esc) .on('⌫.drawline', backspace) - .on('delete.drawline', del) + .on('⌦.drawline', del) .on('↩.drawline', ret); }; mode.exit = function() { - mode.map.hint(false); - mode.map.fastEnable(true); + mode.map + .hint(false) + .fastEnable(true); mode.map.surface .on('mousemove.drawline', null) .on('click.drawline', null); - mode.map.keybinding().on('⎋.drawline', null) + + mode.map.keybinding() + .on('⎋.drawline', null) .on('⌫.drawline', null) - .on('delete.drawline', null) + .on('⌦.drawline', null) .on('↩.drawline', null); + window.setTimeout(function() { mode.map.dblclickEnable(true); }, 1000); diff --git a/js/id/modes/select.js b/js/id/modes/select.js index daf5c9016..a94e54103 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -1,8 +1,11 @@ iD.modes.Select = function (entity) { var mode = { - button: '' - }, - inspector = iD.Inspector(), + id: 'select', + button: 'browse', + entity: entity + }; + + var inspector = iD.Inspector(), dragging, target; var dragWay = d3.behavior.drag() @@ -11,48 +14,45 @@ iD.modes.Select = function (entity) { return { x: p[0], y: p[1] }; }) .on('drag', function(entity) { - if (!mode.map.dragEnable()) return; - d3.event.sourceEvent.stopPropagation(); if (!dragging) { - dragging = iD.util.trueObj([entity.id].concat( - _.pluck(mode.history.graph().parentWays(entity.id), 'id'))); + dragging = true; mode.history.perform(iD.actions.Noop()); } - _.uniq(_.pluck(entity.nodes, 'id')) - .forEach(function(id) { - var node = mode.history.graph().entity(id), - start = mode.map.projection(node.loc), - end = mode.map.projection.invert([ - start[0] + d3.event.dx, - start[1] + d3.event.dy]); - mode.history.replace(iD.actions.Move(id, end)); - }); + mode.history.replace(iD.actions.MoveWay(entity.id, [d3.event.dx, d3.event.dy], mode.map.projection)); }) .on('dragend', function () { - if (!mode.map.dragEnable() || !dragging) return; + if (!dragging) return; dragging = undefined; mode.map.redraw(); }); function remove() { - switch (entity.type) { - case 'way': - mode.history.perform(iD.actions.DeleteWay(entity.id)); - break; - case 'node': - mode.history.perform(iD.actions.DeleteNode(entity.id)); + if (entity.type === 'way') { + mode.history.perform( + iD.actions.DeleteWay(entity.id), + 'deleted a way'); + } else if (entity.type === 'node') { + var parents = mode.history.graph().parentWays(entity.id), + operations = [iD.actions.DeleteNode(entity.id)]; + parents.forEach(function(parent) { + if (_.uniq(parent.nodes).length === 1) operations.push(iD.actions.DeleteWay(parent.id)); + }); + mode.history.perform.apply(mode.history, + operations.concat(['deleted a node'])); } mode.controller.exit(); } mode.enter = function () { - target = mode.map.surface.selectAll("*") + target = mode.map.surface.selectAll('*') .filter(function (d) { return d === entity; }); + iD.modes._dragFeatures(mode); + d3.select('.inspector-wrap') .style('display', 'block') .style('opacity', 1) @@ -60,11 +60,23 @@ iD.modes.Select = function (entity) { .call(inspector); inspector.on('changeTags', function(d, tags) { - mode.history.perform(iD.actions.ChangeEntityTags(d.id, tags)); + mode.history.perform( + iD.actions.ChangeEntityTags(d.id, tags), + 'changed tags'); + }).on('changeWayDirection', function(d) { - mode.history.perform(iD.actions.ReverseWay(d)); + mode.history.perform( + iD.actions.ReverseWay(d.id), + 'reversed a way'); + + }).on('splitWay', function(d) { + mode.history.perform( + iD.actions.SplitWay(d.id), + 'split a way on a node'); + }).on('remove', function() { remove(); + }).on('close', function() { mode.controller.exit(); }); @@ -73,7 +85,7 @@ iD.modes.Select = function (entity) { target.call(dragWay); } - mode.map.surface.on("click.browse", function () { + mode.map.surface.on('click.browse', function () { var datum = d3.select(d3.event.target).datum(); if (datum instanceof iD.Entity) { mode.controller.enter(iD.modes.Select(datum)); @@ -100,6 +112,7 @@ iD.modes.Select = function (entity) { } mode.map.surface.on("click.browse", null); + mode.map.surface.on('mousedown.latedrag', null); mode.map.keybinding().on('⌫.browse', null); mode.map.selection(null); diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 7bb1a08db..93a9dec86 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -4,7 +4,7 @@ iD.Map = function() { dispatch = d3.dispatch('move'), selection = null, hover = null, translateStart, - keybinding, + keybinding = d3.keybinding(), projection = d3.geo.mercator().scale(1024), zoom = d3.behavior.zoom() .translate(projection.translate()) @@ -12,7 +12,6 @@ iD.Map = function() { .scaleExtent([1024, 256 * Math.pow(2, 24)]) .on('zoom', zoomPan), dblclickEnabled = true, - dragging = false, fastEnabled = true, notice, background = iD.Background() @@ -63,6 +62,8 @@ iD.Map = function() { map.size(this.size()); map.surface = surface; + + d3.select(document).call(keybinding); } function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; } diff --git a/js/id/ui/inspector.js b/js/id/ui/inspector.js index c5a8991c6..014ed09a3 100644 --- a/js/id/ui/inspector.js +++ b/js/id/ui/inspector.js @@ -48,103 +48,109 @@ iD.Inspector = function() { var inspectorwrap = selection .append('ul').attr('class', 'inspector-inner tag-wrap fillL2'); - inspectorwrap.append('h4').text('Edit tags') + inspectorwrap.append('h4').text('Edit tags'); inspectorwrap .data(['tag', 'value', '']) .enter(); function removeTag(d) { - var tags = pad(grabtags()); - delete tags[d.key]; - draw(tags); + draw(grabtags().filter(function(t) { return t.key !== d.key; })); } function draw(data) { - var tr = inspectorwrap.selectAll('li') - .data(d3.entries(data), function(d) { return [d.key, d.value]; }); - tr.exit().remove(); - var row = tr.enter().append('li').attr('class','tag-row'); - var inputs = row.append('div').attr('class','input-wrap').selectAll('input') - .data(function(d) { return [d, d]; }); - inputs.enter().append('input') + + var li = inspectorwrap.selectAll('li') + .data(data, function(d) { return [d.key, d.value]; }); + + li.exit().remove(); + + var row = li.enter().append('li').attr('class','tag-row'); + var inputs = row.append('div').attr('class','input-wrap'); + + function setValue(d, i) { d.value = this.value; } + + function emptyTag(d) { return d.key === ''; } + + function pushMore(d, i) { + if (d3.event.keyCode === 9) { + var tags = grabtags(); + if (i == tags.length - 1 && !tags.filter(emptyTag).length) { + draw(tags.concat([{ key: '', value: '' }])); + } + } + } + + function bindTypeahead(d, i) { + var selection = d3.select(this); + selection.call(d3.typeahead() + .data(function(selection, callback) { + taginfo.values(selection.datum().key, function(err, data) { + callback(data.data); + }); + })); + } + + inputs.append('input') .property('type', 'text') - .attr('class', function(d, i) { - return i ? 'value' : 'key'; - }) - .property('value', function(d, i) { return d[i ? 'value' : 'key']; }) - .on('keyup.update', function(d, i) { - d[i ? 'value' : 'key'] = this.value; - update(); - }) - .each(function(d, i) { - if (!i) return; - var selection = d3.select(this); - selection.call(d3.typeahead() - .data(function(selection, callback) { - update(); - taginfo.values(selection.datum().key, function(err, data) { - callback(data.data); - }); - })); - }); - row.append('button') - .html("") + .attr('class', 'key') + .property('value', function(d, i) { return d.key; }) + .on('keyup.update', setValue); + + inputs.append('input') + .property('type', 'text') + .attr('class', 'value') + .property('value', function(d, i) { return d.value; }) + .on('keyup.update', setValue) + .on('keydown.push-more', pushMore) + .each(bindTypeahead); + + removeBtn = row.append('button') .attr('class','remove minor') .on('click', removeTag); - row.append('button').attr('class', 'tag-help minor').append('a') - .html("") + + removeBtn.append('span').attr('class', 'icon remove') + + helpBtn = row.append('button').attr('class', 'tag-help minor').append('a') .attr('target', '_blank') .attr('tabindex', -1) .attr('href', function(d) { return 'http://taginfo.openstreetmap.org/keys/' + d.key; }); - } - - // Remove any blank key-values - function clean(x) { - for (var i in x) { - // undefined is cast to a string as an object key - if (!i || i === 'undefined') delete x[i]; - } - return x; - } - - // Add a blank row for new tags - function pad(x) { - if (!x['']) x[''] = ''; - return x; + helpBtn.append('span').attr('class', 'icon inspect') } function grabtags() { - var grabbed = {}; - function grab(d) { if (d.key !== undefined) grabbed[d.key] = d.value; } - inspectorwrap.selectAll('input').each(grab); + var grabbed = []; + function grab(d) { grabbed.push(d); } + inspectorwrap.selectAll('li').each(grab); return grabbed; } - // fill values and add blank field if necessary - function update() { - draw(pad(grabtags())); + function unentries(entries) { + return d3.nest() + .key(function(d) { return d.key; }) + .rollup(function(v) { return v[0].value; }) + .map(entries); } - var data = _.clone(entity.tags); - draw(data); - update(); + draw(d3.entries(_.clone(entity.tags))); selection.select('input').node().focus(); selection.append('div') .attr('class', 'inspector-buttons').call(drawbuttons); + function apply(entity) { + event.changeTags(entity, unentries(grabtags())); + event.close(entity); + } + function drawbuttons(selection) { selection.append('button') .attr('class', 'apply wide action') .html("Apply") - .on('click', function(entity) { - event.changeTags(entity, clean(grabtags())); - event.close(entity); - }); + .on('click', apply); selection.append('button') .attr('class', 'delete wide action fr') .html("Delete") diff --git a/js/id/ui/layerswitcher.js b/js/id/ui/layerswitcher.js index e5f1a8b32..f79ab169c 100644 --- a/js/id/ui/layerswitcher.js +++ b/js/id/ui/layerswitcher.js @@ -48,36 +48,36 @@ iD.layerswitcher = function(map) { var opa = content .append('div') - .attr('class', 'opacity-options-wrapper fillL2') + .attr('class', 'opacity-options-wrapper fillL2'); - opa.append('h4').text('Layers') + opa.append('h4').text('Layers'); - opa.append('ul') - .attr('class', 'opacity-options') - .selectAll('div.opacity') - .data(opacities) - .enter() - .append('li') - .attr('data-original-title', function(d) { - return (d * 100) + "% opacity"; - }) - .on('click.set-opacity', function(d) { - d3.select('#tile-g') - .transition() - .style('opacity', d) - .attr('data-opacity', d); - d3.selectAll('.opacity-options li') - .classed('selected', false); - d3.select(this) - .classed('selected', true); - }) - .html("
") - .call(bootstrap.tooltip().placement('top')) - .append('div') - .attr('class', 'opacity') - .style('opacity', function(d) { - return d; - }); + opa.append('ul') + .attr('class', 'opacity-options') + .selectAll('div.opacity') + .data(opacities) + .enter() + .append('li') + .attr('data-original-title', function(d) { + return (d * 100) + "% opacity"; + }) + .on('click.set-opacity', function(d) { + d3.select('#tile-g') + .transition() + .style('opacity', d) + .attr('data-opacity', d); + d3.selectAll('.opacity-options li') + .classed('selected', false); + d3.select(this) + .classed('selected', true); + }) + .html("
") + .call(bootstrap.tooltip().placement('top')) + .append('div') + .attr('class', 'opacity') + .style('opacity', function(d) { + return d; + }); // Make sure there is an active selection by default d3.select('.opacity-options li').classed('selected', true); diff --git a/js/lib/d3.keybinding.js b/js/lib/d3.keybinding.js index 12f7a33fd..01256aa20 100644 --- a/js/lib/d3.keybinding.js +++ b/js/lib/d3.keybinding.js @@ -39,8 +39,8 @@ d3.keybinding = function() { '⇞': 36, home: 36, // Insert key, or ins ins: 45, insert: 45, - // Delete key, on Mac: ⌫ (Delete) - del: 46, 'delete': 46, + // Delete key, on Mac: ⌦ (Delete) + '⌦': 46, del: 46, 'delete': 46, // Left Arrow Key, or ← '←': 37, left: 37, 'arrow-left': 37, // Up Arrow Key, or ↑ @@ -93,42 +93,27 @@ d3.keybinding = function() { while(++i < 91) _keys.keys[String.fromCharCode(i).toLowerCase()] = i; var pairs = d3.entries(_keys.keys), - key_shortcuts = pairs.map(function(d) { - return d.key; - }), - mods = d3.entries(_keys.mods), - mod_shortcuts = mods.map(function(d) { - return d.key; - }), - event = d3.dispatch.apply(d3, key_shortcuts), - modifiers = []; - - function keydown() { - var tagName = d3.select(d3.event.target).node().tagName; - if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') { - return; - } - - modifiers = modifiers.concat(mods.filter(function(d) { - return d.value === d3.event.keyCode; - }).map(function(d) { return d.key; })); - - pairs.filter(function(d) { - return d.value === d3.event.keyCode; - }).forEach(function(d) { - event[d.key](d3.event, modifiers.slice().sort().join('')); - }); - } - - function keyup() { - modifiers = []; - } + event = d3.dispatch.apply(d3, d3.keys(_keys.keys)); function keys(selection) { - d3.select(window).on('focus', keyup); - selection - .on('keyup', keyup) - .on('keydown', keydown); + selection.on('keydown', function () { + var tagName = d3.select(d3.event.target).node().tagName; + if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') { + return; + } + + var modifiers = ''; + if (d3.event.shiftKey) modifiers += '⇧'; + if (d3.event.ctrlKey) modifiers += '⌃'; + if (d3.event.altKey) modifiers += '⌥'; + if (d3.event.metaKey) modifiers += '⌘'; + + pairs.filter(function(d) { + return d.value === d3.event.keyCode; + }).forEach(function(d) { + event[d.key](d3.event, modifiers); + }); + }); } return d3.rebind(keys, event, 'on'); diff --git a/package.json b/package.json new file mode 100644 index 000000000..5c991f29b --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "iD", + "version": "0.0.0", + "description": "a new editor for openstreetmap", + "main": "iD.js", + "directories": { + "doc": "docs", + "test": "test" + }, + "scripts": { + "test": "mocha-phantomjs test/index.html" + }, + "repository": { + "type": "git", + "url": "git://github.com/systemed/iD.git" + }, + "keywords": [ + "editor", + "openstreetmap" + ], + "license": "BSD", + "devDependencies": { + "uglify-js": "~2.2.2", + "mocha-phantomjs": "~1.1.1" + } +} diff --git a/test/data/map.xml b/test/data/map.xml deleted file mode 100644 index 4b0ccd4c4..000000000 --- a/test/data/map.xml +++ /dev/null @@ -1,5449 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/data/node.xml b/test/data/node.xml new file mode 100644 index 000000000..9e6640dc4 --- /dev/null +++ b/test/data/node.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/test/data/way.xml b/test/data/way.xml new file mode 100644 index 000000000..e8e2ed5eb --- /dev/null +++ b/test/data/way.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/index.html b/test/index.html index 7e5abcda2..c583acee9 100644 --- a/test/index.html +++ b/test/index.html @@ -20,6 +20,8 @@ + + @@ -52,7 +54,8 @@ - + + @@ -63,6 +66,7 @@ + @@ -91,7 +95,8 @@ - + + @@ -99,14 +104,19 @@ + + + + + diff --git a/test/index_packaged.html b/test/index_packaged.html index 8271e3886..2fdd8ab0f 100644 --- a/test/index_packaged.html +++ b/test/index_packaged.html @@ -31,7 +31,8 @@ - + + @@ -39,14 +40,19 @@ + + + + + diff --git a/test/spec/actions/move.js b/test/spec/actions/move.js deleted file mode 100644 index 5e4163af7..000000000 --- a/test/spec/actions/move.js +++ /dev/null @@ -1,8 +0,0 @@ -describe("iD.actions.Move", function () { - it("changes an entity's location", function () { - var entity = iD.Entity(), - loc = [2, 3], - graph = iD.actions.Move(entity.id, loc)(iD.Graph([entity])); - expect(graph.entity(entity.id).loc).to.eql(loc); - }); -}); diff --git a/test/spec/actions/move_node.js b/test/spec/actions/move_node.js new file mode 100644 index 000000000..5e7c8f663 --- /dev/null +++ b/test/spec/actions/move_node.js @@ -0,0 +1,8 @@ +describe("iD.actions.MoveNode", function () { + it("changes a node's location", function () { + var node = iD.Node(), + loc = [2, 3], + graph = iD.actions.MoveNode(node.id, loc)(iD.Graph([node])); + expect(graph.entity(node.id).loc).to.eql(loc); + }); +}); diff --git a/test/spec/actions/move_way.js b/test/spec/actions/move_way.js new file mode 100644 index 000000000..71c703e7f --- /dev/null +++ b/test/spec/actions/move_way.js @@ -0,0 +1,21 @@ +describe("iD.actions.MoveWay", function () { + it("moves all nodes in a way by the given amount", function () { + var node1 = iD.Node({loc: [0, 0]}), + node2 = iD.Node({loc: [5, 10]}), + way = iD.Way({nodes: [node1.id, node2.id]}), + dxdy = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.MoveWay(way.id, dxdy, projection)(iD.Graph([node1, node2, way])); + expect(graph.entity(node1.id).loc).to.eql([1.4400000000000002, -2.1594885414215783]); + expect(graph.entity(node2.id).loc).to.eql([6.440000000000008, 7.866329874099955]); + }); + + it("moves repeated nodes only once", function () { + var node = iD.Node({loc: [0, 0]}), + way = iD.Way({nodes: [node.id, node.id]}), + dxdy = [2, 3], + projection = d3.geo.mercator(), + graph = iD.actions.MoveWay(way.id, dxdy, projection)(iD.Graph([node, way])); + expect(graph.entity(node.id).loc).to.eql([1.4400000000000002, -2.1594885414215783]); + }); +}); diff --git a/test/spec/connection.js b/test/spec/connection.js index 7a0bbacbe..70f018322 100644 --- a/test/spec/connection.js +++ b/test/spec/connection.js @@ -24,11 +24,11 @@ describe('Connection', function() { describe('#loadFromURL', function() { it('loads test data', function(done) { - c.loadFromURL('data/map.xml', done); + c.loadFromURL('data/node.xml', done); }); it('returns a graph', function(done) { - c.loadFromURL('data/map.xml', function(err, graph) { + c.loadFromURL('data/node.xml', function(err, graph) { expect(err).to.not.be.ok; expect(graph).to.be.instanceOf(iD.Graph); done(); @@ -36,15 +36,15 @@ describe('Connection', function() { }); it('parses a node', function(done) { - c.loadFromURL('data/map.xml', function(err, graph) { - expect(graph.entity('n1193811')).to.be.instanceOf(iD.Entity); + c.loadFromURL('data/node.xml', function(err, graph) { + expect(graph.entity('n356552551')).to.be.instanceOf(iD.Entity); done(); }); }); it('parses a way', function(done) { - c.loadFromURL('data/map.xml', function(err, graph) { - expect(graph.entity('w53471')).to.be.instanceOf(iD.Entity); + c.loadFromURL('data/way.xml', function(err, graph) { + expect(graph.entity('w19698713')).to.be.instanceOf(iD.Entity); done(); }); }); diff --git a/test/spec/modes/add_place.js b/test/spec/modes/add_place.js new file mode 100644 index 000000000..71cbfccd6 --- /dev/null +++ b/test/spec/modes/add_place.js @@ -0,0 +1,39 @@ +describe("iD.modes.AddPlace", function () { + var container, map, history, controller, mode; + + beforeEach(function () { + container = d3.select('body').append('div'); + map = iD.Map(); + history = iD.History(); + controller = iD.Controller(map, history); + + container.call(map); + + mode = iD.modes.AddPlace(); + controller.enter(mode); + }); + + afterEach(function() { + container.remove(); + }); + + describe("clicking the map", function () { + it("adds a node", function () { + happen.click(map.surface.node(), {}); + expect(history.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.entity).to.equal(history.changes().created[0]); + }); + }); + + describe("pressing ⎋", function () { + it("exits to browse mode", function () { + happen.keydown(document, {keyCode: 27}); + expect(controller.mode.id).to.equal('browse'); + }); + }); +});