position: 'absolute',
display: 'block',
left: '0px'
+ })
+ .on('mousedown', function () {
+ // prevent moving focus out of the text field
+ d3.event.preventDefault();
});
d3.select(document.body)
// https://github.com/systemed/iD/issues/772
// http://mathiasbynens.be/notes/localstorage-pattern#comment-9
try { storage = localStorage; } catch (e) {}
- storage = storage || {};
+ storage = storage || (function() {
+ var s = {};
+ return {
+ getItem: function(k) { return s[k]; },
+ setItem: function(k, v) { s[k] = v; },
+ removeItem: function(k) { delete s[k] }
+ };
+ })();
context.storage = function(k, v) {
- if (arguments.length === 1) return storage[k];
- else if (v === null) delete storage[k];
- else storage[k] = v;
+ if (arguments.length === 1) return storage.getItem(k);
+ else if (v === null) storage.removeItem(k);
+ else storage.setItem(k, v);
};
var history = iD.History(context),
return d3.rebind(context, dispatch, 'on');
};
-iD.version = '1.1.0';
+iD.version = '1.1.4';
(function() {
var detected = {};
}
function popularValues() {
- return function(d) { return parseFloat(d.fraction) > 0.01; };
+ return function(d) { return parseFloat(d.fraction) > 0.01 || d.in_wiki; };
}
function valKey(d) { return { value: d.key }; }
function draw(selection) {
context.install(hover);
- context.install(tail);
context.install(edit);
+ if (!iD.behavior.Draw.usedTails[tail.text()]) {
+ context.install(tail);
+ }
+
keybinding
.on('⌫', backspace)
.on('⌦', del)
draw.off = function(selection) {
context.uninstall(hover);
- context.uninstall(tail);
context.uninstall(edit);
+ if (!iD.behavior.Draw.usedTails[tail.text()]) {
+ context.uninstall(tail);
+ iD.behavior.Draw.usedTails[tail.text()] = true;
+ }
+
selection
.on('mousedown.draw', null)
.on('mousemove.draw', null);
};
draw.tail = function(_) {
- if (!_ || iD.behavior.Draw.usedTails[_] === undefined) {
- tail.text(_);
- iD.behavior.Draw.usedTails[_] = true;
- }
+ tail.text(_);
return draw;
};
iD.modes.DragNode(context).behavior];
mode.enter = function() {
+ context.history().save();
+
behaviors.forEach(function(behavior) {
context.install(behavior);
});
// Get focus on the body.
- document.activeElement.blur();
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
if (sidebar) {
context.ui().sidebar.show(sidebar);
};
mode.enter = function() {
+ context.history().save();
+
behaviors.forEach(function(behavior) {
context.install(behavior);
});
// Create the appropriate subtype.
if (attrs && attrs.type) {
return iD.Entity[attrs.type].apply(this, arguments);
+ } else if (attrs && attrs.id) {
+ return iD.Entity[iD.Entity.id.type(attrs.id)].apply(this, arguments);
}
// Initialize a generic Entity (used only in tests).
// A function suitable for use as the second argument to d3.selection#data().
iD.Entity.key = function(entity) {
- return entity.id + ',' + entity.v;
+ return entity.id + 'v' + (entity.v || 0);
};
iD.Entity.areaPath = d3.geo.path()
// Obliterates any existing entities
load: function(entities) {
-
- var base = this.base(),
- i, entity, prefix;
+ var base = this.base();
this.entities = Object.create(base.entities);
- for (i in entities) {
- entity = entities[i];
- prefix = i[0];
-
- if (entity === 'undefined') {
- this.entities[i] = undefined;
- } else if (prefix == 'n') {
- this.entities[i] = new iD.Node(entity);
-
- } else if (prefix == 'w') {
- this.entities[i] = new iD.Way(entity);
-
- } else if (prefix == 'r') {
- this.entities[i] = new iD.Relation(entity);
- }
+ for (var i in entities) {
+ this.entities[i] = entities[i];
this._updateCalculated(base.entities[i], this.entities[i]);
}
+
return this;
}
};
toJSON: function() {
if (stack.length <= 1) return;
+ var allEntities = {};
+
var s = stack.map(function(i) {
- var x = { entities: i.graph.entities };
+ var modified = [], deleted = [];
+
+ _.forEach(i.graph.entities, function(entity, id) {
+ if (entity) {
+ var key = iD.Entity.key(entity);
+ allEntities[key] = entity;
+ modified.push(key);
+ } else {
+ deleted.push(id);
+ }
+ });
+
+ var x = {};
+
+ if (modified.length) x.modified = modified;
+ if (deleted.length) x.deleted = deleted;
if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
if (i.annotation) x.annotation = i.annotation;
+
return x;
});
return JSON.stringify({
+ version: 2,
+ entities: _.values(allEntities),
stack: s,
nextIDs: iD.Entity.id.next,
index: index
- }, function includeUndefined(key, value) {
- if (typeof value === 'undefined') return 'undefined';
- return value;
});
},
fromJSON: function(json) {
-
var h = JSON.parse(json);
iD.Entity.id.next = h.nextIDs;
index = h.index;
- stack = h.stack.map(function(d) {
- d.graph = iD.Graph(stack[0].graph).load(d.entities);
- return d;
- });
+
+ if (h.version === 2) {
+ var allEntities = {};
+
+ h.entities.forEach(function(entity) {
+ allEntities[iD.Entity.key(entity)] = iD.Entity(entity);
+ });
+
+ stack = h.stack.map(function(d) {
+ var entities = {}, entity;
+
+ d.modified && d.modified.forEach(function(key) {
+ entity = allEntities[key];
+ entities[entity.id] = entity;
+ });
+
+ d.deleted && d.deleted.forEach(function(id) {
+ entities[id] = undefined;
+ });
+
+ return {
+ graph: iD.Graph(stack[0].graph).load(entities),
+ annotation: d.annotation,
+ imageryUsed: d.imageryUsed
+ };
+ });
+ } else { // original version
+ stack = h.stack.map(function(d) {
+ var entities = {};
+
+ for (var i in d.entities) {
+ var entity = d.entities[i];
+ entities[i] = entity === 'undefined' ? undefined : iD.Entity(entity);
+ }
+
+ d.graph = iD.Graph(stack[0].graph).load(entities);
+ return d;
+ });
+ }
+
stack[0].graph.inherited = false;
dispatch.change();
map.dimensions(selection.dimensions());
labels.supersurface(supersurface);
- mouse = iD.util.fastMouse(supersurface.node());
}
function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
surface.dimensions(dimensions);
context.background().dimensions(dimensions);
projection.clipExtent([[0, 0], dimensions]);
+ mouse = iD.util.fastMouse(supersurface.node());
setCenter(center);
return redraw();
};
['top', [0, -1]],
['right', [-1, 0]],
['bottom', [0, 1]]],
- opacityDefault = (context.storage('background-opacity') !== undefined) ?
+ opacityDefault = (context.storage('background-opacity') != undefined) ?
(+context.storage('background-opacity')) : 0.5;
function background(selection) {
.entityID(id));
function historyChanged() {
+ if (state === 'hide') return;
var entity = context.hasEntity(id);
if (!entity) return;
entityEditor.preset(context.presets().match(entity, context.graph()));
.text(t('inspector.feature_list'));
function keypress() {
- var q = search.property('value');
- if (d3.event.keyCode === 13 && q.length) {
- click(list.selectAll('.feature-list-item:first-child').datum().entity);
+ var q = search.property('value'),
+ items = list.selectAll('.feature-list-item');
+ if (d3.event.keyCode === 13 && q.length && items.size()) {
+ click(items.datum().entity);
}
}
inspector.state = function(_) {
if (!arguments.length) return state;
state = _;
+ entityEditor.state(state);
return inspector;
};
var shown = fields.filter(function(field) { return field.shown(); }),
notShown = fields.filter(function(field) { return !field.shown(); });
- var $form = selection.selectAll('form')
+ var $form = selection.selectAll('.preset-form')
.data([0]);
- $form.enter().append('form')
+ $form.enter().append('div')
.attr('class', 'preset-form inspector-inner fillL3');
var $fields = $form.selectAll('.form-field')
var message = messagewrap.append('h3')
.text(t('inspector.choose'));
- if (currentPreset) {
+ if (context.entity(id).isUsed(context.graph())) {
messagewrap.append('button')
.attr('class', 'preset-choose')
.on('click', function() { event.choose(currentPreset); })
presetList.entityID = function(_) {
if (!arguments.length) return id;
id = _;
+ presetList.preset(context.presets().match(context.entity(id), context.graph()));
return presetList;
};
.attr('class', 'tooltip-inner radial-menu-tooltip');
function mouseover(d, i) {
- var rect = context.surface().node().getBoundingClientRect(),
+ // Avoid getBoundingClientRect on SVG element; browser implementations
+ // differ: http://stackoverflow.com/questions/18153989/
+ var rect = context.surface().node().parentNode.getBoundingClientRect(),
angle = a0 + i * a,
dx = rect.left - (angle < 0 ? 200 : 0),
dy = rect.top;
} else if (!current) {
featureListWrap.classed('inspector-hidden', false);
inspectorWrap.classed('inspector-hidden', true);
+ inspector.state('hide');
}
};
} else if (!current) {
featureListWrap.classed('inspector-hidden', false);
inspectorWrap.classed('inspector-hidden', true);
+ inspector.state('hide');
}
};
.on('click', function() { event.cancel(success) });
header.append('h3')
- .text(t('just_edited'));
+ .text(t('success.just_edited'));
var body = selection.append('div')
.attr('class', 'body save-success');
+ body.append('p')
+ .html(t('success.help_html'));
+
body.append('a')
- .attr('class', 'col12 osm')
+ .attr('class', 'button col12 osm')
.attr('target', '_blank')
.attr('href', function() {
return context.connection().changesetURL(changeset.id);
})
- .text(t('view_on_osm'));
+ .text(t('success.view_on_osm'));
body.append('a')
- .attr('class', 'col12 twitter')
+ .attr('class', 'button col12 twitter')
.attr('target', '_blank')
.attr('href', function() {
return 'https://twitter.com/intent/tweet?source=webclient&text=' +
.text(t('success.tweet'));
body.append('a')
- .attr('class', 'col12 facebook')
+ .attr('class', 'button col12 facebook')
.attr('target', '_blank')
.attr('href', function() {
return 'https://facebook.com/sharer/sharer.php?u=' +
.placement('bottom')
.html(true)
.title(function (d) {
- return iD.ui.tooltipHtml(d.annotation() || t('nothing_to_' + d.id), d.cmd);
+ return iD.ui.tooltipHtml(d.annotation() ?
+ t(d.id + '.tooltip', {action: d.annotation()}) :
+ t(d.id + '.nothing'), d.cmd);
});
var buttons = selection.selectAll('button')
input.enter().append('input')
.attr('type', 'text')
- .attr('id', 'preset-input-' + field.id)
+ .attr('id', 'preset-input-' + field.id);
+
+ input
+ .on('change', change)
+ .on('blur', change)
.each(function() {
if (field.options) {
options(field.options);
if (!err) options(_.pluck(data, 'value'));
});
}
- });
-
- input
- .on('change', change)
- .on('blur', change)
+ })
.call(combobox);
function options(opts) {
if (language) value = language[2];
- t[key(d.lang)] = '';
+ if (d.lang) {
+ t[key(d.lang)] = '';
+ }
if (d.value) {
t[key(value)] = d.value;
},
"area": {
"name": "Area",
- "tags": {},
+ "tags": {
+ "area": "yes"
+ },
"geometry": [
"area"
]
},
"name": "Ambulance Station"
},
+ "emergency/fire_hydrant": {
+ "fields": [
+ "fire_hydrant/type"
+ ],
+ "geometry": [
+ "point",
+ "vertex"
+ ],
+ "tags": {
+ "emergency": "fire_hydrant"
+ },
+ "name": "Fire Hydrant"
+ },
"emergency/phone": {
"icon": "emergency-telephone",
"fields": [
"icon": "swimming",
"name": "Swimming Pool"
},
+ "leisure/track": {
+ "icon": "pitch",
+ "fields": [
+ "surface"
+ ],
+ "geometry": [
+ "point",
+ "line",
+ "area"
+ ],
+ "tags": {
+ "leisure": "track"
+ },
+ "name": "Race Track"
+ },
"line": {
"name": "Line",
"tags": {},
"name": "Power"
},
"power/generator": {
+ "name": "Power Generator",
"geometry": [
"point",
"vertex",
"tags": {
"power": "generator"
},
- "name": "Power Plant"
+ "fields": [
+ "generator/source",
+ "generator/method",
+ "generator/type"
+ ]
},
"power/line": {
"geometry": [
"type": "check",
"label": "Fee"
},
+ "fire_hydrant/type": {
+ "key": "fire_hydrant:type",
+ "type": "combo",
+ "options": [
+ "pillar",
+ "pond",
+ "underground",
+ "wall"
+ ],
+ "label": "Type"
+ },
"fixme": {
"key": "fixme",
"type": "textarea",
"label": "Fix Me"
},
+ "generator/method": {
+ "key": "generator:method",
+ "type": "combo",
+ "label": "Method"
+ },
+ "generator/source": {
+ "key": "generator:source",
+ "type": "combo",
+ "label": "Source"
+ },
+ "generator/type": {
+ "key": "generator:type",
+ "type": "combo",
+ "label": "Type"
+ },
"highway": {
"key": "highway",
"type": "combo",
"fi",
"fr",
"de",
+ "el",
"hu",
"is",
"id",
"multiple_ways": "There are too many lines here to split."
}
},
- "nothing_to_undo": "Nothing to undo.",
- "nothing_to_redo": "Nothing to redo.",
+ "undo": {
+ "tooltip": "Undo: {action}",
+ "nothing": "Nothing to undo."
+ },
+ "redo": {
+ "tooltip": "Redo: {action}",
+ "nothing": "Nothing to redo."
+ },
"tooltip_keyhint": "Shortcut:",
- "just_edited": "You just edited OpenStreetMap!",
"browser_notice": "This editor is supported in Firefox, Chrome, Safari, Opera, and Internet Explorer 9 and above. Please upgrade your browser or use Potlatch 2 to edit the map.",
- "view_on_osm": "View on OSM",
"translate": {
"translate": "Translate",
"localized_translation_label": "Multilingual name",
},
"success": {
"edited_osm": "Edited OSM!",
+ "just_edited": "You just edited OpenStreetMap!",
+ "view_on_osm": "View on OSM",
"facebook": "Share on Facebook",
"tweet": "Tweet",
- "okay": "Okay"
+ "help_html": "Your changes should appear in the \"Standard\" layer in a few minutes. Other layers, and certain features, may take longer\n(<a href='https://help.openstreetmap.org/questions/4705/why-havent-my-changes-appeared-on-the-map'>details</a>).\n"
},
"confirm": {
"okay": "Okay"
"fee": {
"label": "Fee"
},
+ "fire_hydrant/type": {
+ "label": "Type"
+ },
"fixme": {
"label": "Fix Me"
},
+ "generator/method": {
+ "label": "Method"
+ },
+ "generator/source": {
+ "label": "Source"
+ },
+ "generator/type": {
+ "label": "Type"
+ },
"highway": {
"label": "Type"
},
"name": "Ambulance Station",
"terms": ""
},
+ "emergency/fire_hydrant": {
+ "name": "Fire Hydrant",
+ "terms": ""
+ },
"emergency/phone": {
"name": "Emergency Phone",
"terms": ""
"name": "Swimming Pool",
"terms": ""
},
+ "leisure/track": {
+ "name": "Race Track",
+ "terms": ""
+ },
"line": {
"name": "Line",
"terms": ""
"terms": ""
},
"power/generator": {
- "name": "Power Plant",
+ "name": "Power Generator",
"terms": ""
},
"power/line": {