diff --git a/css/app.css b/css/app.css
index 9317d70d3..8aaf4be6e 100644
--- a/css/app.css
+++ b/css/app.css
@@ -962,7 +962,7 @@ a:hover .icon.out-link { background-position: -500px -14px;}
height: 65px;
}
-.form-field-name input {
+.form-field-name input.localized-main {
height: 35px;
font-size: 18px;
font-weight: bold;
@@ -1194,6 +1194,50 @@ input[type=number] {
content: none;
}
+.form-field .localized-main {
+ width: 90%;
+ border-radius: 0 0 0 4px;
+}
+
+.form-field .localized-add {
+ width: 10%;
+ height: 35px;
+ border-radius: 0 0 4px 0;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ vertical-align: top;
+}
+
+.form-field .localized-wrap .entry .localized-lang {
+ border-top: none;
+ border-right: none;
+ border-radius: 0;
+ width: 30%;
+}
+
+.form-field .localized-wrap .entry .localized-value {
+ border-top: none;
+ border-radius: 0;
+ width: 60%;
+}
+
+.form-field .localized-wrap .entry .localized-remove {
+ height: 30px;
+ border-radius: 0;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ vertical-align: top;
+ width: 10%;
+}
+
+.form-field .localized-wrap .entry:last-child .localized-lang {
+ border-radius: 0 0 0 4px;
+}
+
+.form-field .localized-wrap .entry:last-child .localized-remove {
+ border-radius: 0 0 4px 0;
+}
+
.form-field .wiki-lang {
width: 30%;
border-right: none;
diff --git a/data/presets/fields.json b/data/presets/fields.json
index 42baad8cc..64032c33b 100644
--- a/data/presets/fields.json
+++ b/data/presets/fields.json
@@ -286,7 +286,7 @@
},
"name": {
"key": "name",
- "type": "text",
+ "type": "localized",
"label": "Name"
},
"natural": {
diff --git a/data/presets/fields/name.json b/data/presets/fields/name.json
index 7eee07a8e..5c5b7e9d0 100644
--- a/data/presets/fields/name.json
+++ b/data/presets/fields/name.json
@@ -1,5 +1,5 @@
{
"key": "name",
- "type": "text",
+ "type": "localized",
"label": "Name"
-}
\ No newline at end of file
+}
diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json
index 78a68da54..24b054d68 100644
--- a/data/presets/schema/field.json
+++ b/data/presets/schema/field.json
@@ -31,6 +31,7 @@
"url",
"radio",
"textarea",
+ "localized",
"wikipedia"
],
"required": true
diff --git a/index.html b/index.html
index 38cfb9fff..8f9c1444d 100644
--- a/index.html
+++ b/index.html
@@ -102,6 +102,7 @@
+
diff --git a/js/id/ui/preset.js b/js/id/ui/preset.js
index 934c8e875..01ff7501f 100644
--- a/js/id/ui/preset.js
+++ b/js/id/ui/preset.js
@@ -115,7 +115,10 @@ iD.ui.preset = function(context, entity, preset) {
.duration(200)
.style('padding-top', '20px')
.style('max-height', '240px')
- .style('opacity', '1');
+ .style('opacity', '1')
+ .each('end', function(d) {
+ d3.select(this).style('max-height', '');
+ });
var label = enter.append('label')
.attr('class', 'form-label')
diff --git a/js/id/ui/preset/localized.js b/js/id/ui/preset/localized.js
new file mode 100644
index 000000000..2c3aeaa5b
--- /dev/null
+++ b/js/id/ui/preset/localized.js
@@ -0,0 +1,147 @@
+iD.ui.preset.localized = function(field, context) {
+
+ var event = d3.dispatch('change', 'close'),
+ input, localizedInputs;
+
+ function i(selection) {
+
+ input = selection.append('input')
+ .attr('type', 'text')
+ .attr('id', 'preset-input-' + field.id)
+ .attr('class', 'localized-main')
+ .attr('placeholder', field.placeholder || '')
+ .on('blur', change)
+ .on('change', change)
+ .call(iD.behavior.accept().on('accept', event.close));
+
+ selection.append('button')
+ .attr('class', 'localized-add')
+ .on('click', addBlank)
+ .append('span')
+ .attr('class', 'icon');
+
+ localizedInputs = selection.append('div')
+ .attr('class', 'localized-wrap');
+ }
+
+ function addBlank() {
+ var data = localizedInputs.selectAll('div.entry').data();
+ data.push({ lang: '', value: '' });
+ localizedInputs.call(render, data);
+ }
+
+ function change() {
+ var t = {};
+ t[field.key] = d3.select(this).property('value'),
+ event.change(t);
+ }
+
+ function key(lang) { return field.key + ':' + lang; }
+
+ function changeLang(d) {
+ var value = d3.select(this).property('value'),
+ t = {},
+ language = _.find(iD.data.wikipedia, function(d) {
+ return d[0].toLowerCase() === value.toLowerCase() ||
+ d[1].toLowerCase() === value.toLowerCase();
+ });
+
+ if (language) value = language[2];
+
+ t[key(d.lang)] = '';
+ if (d.value) t[key(value)] = d.value;
+ event.change(t);
+
+ d.lang = value;
+ }
+
+ function changeValue(d) {
+ var t = {};
+ t[key(d.lang)] = d3.select(this).property('value') || '';
+ event.change(t);
+
+ }
+
+ function fetcher(value, __, cb) {
+ var v = value.toLowerCase();
+
+ cb(iD.data.wikipedia.filter(function(d) {
+ return d[0].toLowerCase().indexOf(v) >= 0 ||
+ d[1].toLowerCase().indexOf(v) >= 0 ||
+ d[2].toLowerCase().indexOf(v) >= 0;
+ }).map(function(d) {
+ return { value: d[1] };
+ }));
+ }
+
+ function render(selection, data) {
+ var wraps = selection.selectAll('div.entry').
+ data(data, function(d) { return d.lang; });
+
+ wraps.enter().append('div')
+ .attr('class', 'entry')
+ .each(function(d) {
+ var wrap = d3.select(this);
+ var langcombo = d3.combobox().fetcher(fetcher);
+
+ wrap.append('input')
+ .attr('class', 'localized-lang')
+ .attr('type', 'text')
+ .on('blur', changeLang)
+ .on('change', changeLang)
+ .call(langcombo);
+
+ wrap.append('input')
+ .on('blur', changeValue)
+ .on('change', changeValue)
+ .attr('type', 'text')
+ .attr('class', 'localized-value');
+
+ wrap.append('button')
+ .attr('class', 'localized-remove')
+ .on('click', function(d) {
+ var t = {};
+ t[key(d.lang)] = '';
+ event.change(t);
+ d3.select(this.parentNode).remove();
+ })
+ .append('span').attr('class', 'icon remove');
+
+ });
+
+ wraps.exit().remove();
+
+ selection.selectAll('.entry').select('.localized-lang').property('value', function(d) {
+ var lang = _.find(iD.data.wikipedia, function(lang) {
+ return lang[2] === d.lang;
+ });
+ return lang ? lang[1] : d.lang;
+ });
+
+ selection.selectAll('.entry').select('.localized-value').property('value', function(d) {
+ return d.value;
+ });
+
+
+ }
+
+ i.tags = function(tags) {
+ input.property('value', tags[field.key] || '');
+
+ var postfixed = [];
+ for (var i in tags) {
+ var m = i.match(new RegExp(field.key + ':([a-z]+)'));
+ if (m && m[1]) {
+ postfixed.push({ lang: m[1], value: tags[i]});
+ }
+ }
+
+ localizedInputs.call(render, postfixed);
+ };
+
+ i.focus = function() {
+ title.node().focus();
+ };
+
+ return d3.rebind(i, event, 'on');
+};