First working revision of undo redo

This commit is contained in:
Tom MacWright
2012-11-05 14:23:01 -05:00
parent 812aff870e
commit 9101aa8772
3 changed files with 88 additions and 30 deletions

View File

@@ -95,6 +95,9 @@
d3.select('.zoombuttons .zoom-in').on('click', map.zoomIn);
d3.select('.zoombuttons .zoom-out').on('click', map.zoomOut);
d3.select('#undo').on('click', map.undo);
d3.select('#redo').on('click', map.redo);
</script>
</body>
</html>

View File

@@ -4,6 +4,7 @@ iD.Graph.prototype = {
// a pointer to the top of the stack.
head: {},
annotation: null,
// stack of previous versions of this datastructure
prev: [],
@@ -29,6 +30,27 @@ iD.Graph.prototype = {
return pois;
},
undo: function() {
if (this.prev.length && this.prev[0] !== this.head) {
// skip changes without annotations
for (var idx = this.prev.indexOf(this.head) - 1; idx > 0; idx--) {
if (this.annotations[idx]) break;
}
this.head = this.prev[idx];
this.annotation = this.annotations[idx];
}
},
redo: function() {
if (this.prev.length && this.prev[this.prev.length - 1] !== this.head) {
for (var idx = this.prev.indexOf(this.head) + 1; idx < this.prev.length - 1; idx++) {
if (this.annotations[idx]) break;
}
this.head = this.prev[idx];
this.annotation = this.annotations[idx];
}
},
insert: function(a) {
for (var i = 0; i < a.length; i++) {
if (this.head[a[i].id]) return;
@@ -54,6 +76,7 @@ iD.Graph.prototype = {
// Make head the top of the previous stack
this.head = this.prev[this.prev.length - 1];
this.annotation = this.annotations[this.annotations.length - 1];
},
intersects: function(version, extent) {

View File

@@ -10,6 +10,16 @@ iD.Map = function(elem) {
var version = 0;
// lon/lat object to array
function ll2a(o) {
return [o.lon, o.lat];
}
// array to lon/lat object
function a2ll(o) {
return { lon: o[0], lat: o[1] };
}
var map = {},
width, height,
dispatch = d3.dispatch('move', 'update'),
@@ -30,8 +40,13 @@ iD.Map = function(elem) {
// this is used with handles
dragbehavior = d3.behavior.drag()
.origin(function(d) {
var n = graph.head[d];
var p = projection([n.lon, n.lat]);
var data = (typeof d === 'number') ? graph.head[d] : d;
graph.modify(function(o) {
var c = {};
c[data.id] = pdata.object(data).set({ modified: true }).get();
return o.set(c);
}, '');
p = projection(ll2a(data));
return { x: p[0], y: p[1] };
})
.on('drag', function(d) {
@@ -42,20 +57,28 @@ iD.Map = function(elem) {
graph.head[d].lon = ll[0];
graph.head[d].lat = ll[1];
drawVector();
})
.on('dragend', function(d) {
var data = (typeof d === 'number') ? graph.head[d] : d;
graph.modify(function(o) {
var c = {};
c[data.id] = pdata.object(c[data.id]).get();
o.set(c);
return o;
}, 'moved an element');
map.update();
}),
// geo
linegen = d3.svg.line()
.defined(function(d) {
return !!graph.head[d];
})
.x(function(d) {
var node = graph.head[d];
return projection([node.lon, node.lat])[0];
})
.y(function(d) {
var node = graph.head[d];
return projection([node.lon, node.lat])[1];
}),
.defined(function(d) {
return !!graph.head[d];
})
.x(function(d) {
return projection(ll2a(graph.head[d]))[0];
})
.y(function(d) {
return projection(ll2a(graph.head[d]))[1];
}),
// Abstract linegen so that it pulls from `.children`. This
// makes it possible to call simply `.attr('d', nodeline)`.
nodeline = function(d) {
@@ -63,13 +86,12 @@ iD.Map = function(elem) {
},
key = function(d) { return d.id; };
// Creating containers
// -------------------
// Containers
// ----------
// The map uses SVG groups in order to restrict
// visual and event ordering - fills below casings, casings below
// strokes, and so on.
var surface = parent.append('svg')
.call(zoombehavior);
var surface = parent.append('svg').call(zoombehavior);
surface.append('defs').append('clipPath')
.attr('id', 'clip')
@@ -91,10 +113,10 @@ iD.Map = function(elem) {
temp = r.append('g').attr('id', 'temp-g');
var class_stroke = iD.Style.styleClasses('stroke'),
class_fill = iD.Style.styleClasses('stroke'),
class_area = iD.Style.styleClasses('area'),
class_marker = iD.Style.styleClasses('marker'),
class_casing = iD.Style.styleClasses('casing');
class_fill = iD.Style.styleClasses('stroke'),
class_area = iD.Style.styleClasses('area'),
class_marker = iD.Style.styleClasses('marker'),
class_casing = iD.Style.styleClasses('casing');
var tileclient = iD.Tiles(tilegroup, projection);
@@ -172,8 +194,7 @@ iD.Map = function(elem) {
.attr('r', 5)
.call(dragbehavior);
handles.attr('transform', function(d) {
var node = graph.head[d];
return 'translate(' + projection([node.lon, node.lat]) + ')';
return 'translate(' + projection(ll2a(graph.head[d])) + ')';
});
}
@@ -227,9 +248,21 @@ iD.Map = function(elem) {
// -----------
var undolabel = d3.select('button#undo small');
dispatch.on('update', function() {
undolabel.text(graph.annotations[graph.annotations.length - 1]);
undolabel.text(graph.annotation);
redraw();
});
// Undo/redo
function undo() {
graph.undo();
map.update();
}
function redo() {
graph.redo();
map.update();
}
// Getters & setters for map state
// -------------------------------
// The map state can be expressed entirely as the combination
@@ -281,13 +314,9 @@ iD.Map = function(elem) {
function zoomOut() { return setZoom(Math.floor(getZoom() - 1)); }
function getCenter() {
var ll = projection.invert([
return a2ll(projection.invert([
width / 2,
height / 2]);
return {
lon: ll[0],
lat: ll[1]
};
height / 2]));
}
function setCenter(loc) {
@@ -322,6 +351,9 @@ iD.Map = function(elem) {
map.graph = graph;
map.surface = surface;
map.undo = undo;
map.redo = redo;
setSize(parent.node().offsetWidth, parent.node().offsetHeight);
redraw();