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'); +};