Merge branch 'presets'

This commit is contained in:
Ansis Brammanis
2013-02-15 13:16:37 -05:00
25 changed files with 27174 additions and 366 deletions
+7 -9
View File
@@ -139,21 +139,19 @@
<body>
<div id='foo'><input /></div>
<div id='bar'><input /></div>
<div id="iD"></div><script>
<input />
<div id="iD"><script>
var options = d3.range(0, 50).map(function(i) {
return {
value: i * 10,
title: i * 10
value: '' + (i * 10),
title: '' + (i * 10)
};
});
d3.select('#foo').call(d3.combobox()
.data(function(selection, cb) {
cb(options);
}));
.data(options));
d3.select('#bar').call(d3.combobox()
.data(function(selection, cb) {
cb(options);
}));
.data(options));
</script></body>
<script type="text/javascript">
+106 -27
View File
@@ -95,7 +95,10 @@ a:hover {
}
textarea,
input[type=text] {
input[type=text],
input[type=url],
input[type=tel],
input[type=email] {
background-color: white;
border:1px solid #ccc;
padding:10px;
@@ -508,7 +511,6 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
.inspector-wrap {
opacity:0;
display:none;
padding-left: 10px;
max-width: 500px;
}
@@ -522,6 +524,26 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
padding: 10px;
}
.inspector-inner.message {
padding: 20px;
height: 60px;
}
.inspector-inner.type {
z-index:1;
position:relative;
}
.inspector-inner.type .preset-icon {
width: 50px;
height: 50px;
}
.inspector-inner.type .preset-name {
display: inline-block;
margin-left: 20px;
}
.inspector-inner.head {
z-index:1;
position:relative;
@@ -533,11 +555,6 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
display: inline-block
}
.inspector-inner.tag-wrap {
max-height: 200px;
overflow: auto;
}
.inspector-toggle {
color:#fff;
width: 100%;
@@ -548,6 +565,8 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
.inspector-body {
position:relative;
max-height: 400px;
overflow: auto;
}
.tag-row {
@@ -562,6 +581,10 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
margin-top: -1px;
}
.input-wrap-position {
position: relative;
}
.tag-row input {
width: 50%;
border-left: 0;
@@ -579,18 +602,6 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
border-left: 1px solid #ccc;
}
.input-wrap::after {
content: '';
position: absolute;
right: 9px;
top: 14px;
height: 0;
width: 0;
border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
.tag-row button {
position: absolute;
top: 5px;
@@ -632,6 +643,31 @@ a.selected:hover .toggle.icon { background-position: -40px -180px;}
display: none;
}
/* Preset grid */
/* temp */
.preset-grid {
max-height: 360px;
}
.preset-grid .grid-entry {
float: left;
width: 100px;
height: 100px;
background: red;
color: black;
margin: 10px;
}
.preset-grid-search-wrap {
padding: 20px;
}
.preset-grid-search {
width: 100%;
height: 20px;
}
/* Map Controls */
.map-control {
@@ -1419,6 +1455,41 @@ a.success-action {
.save .label, .apply .label, .cancel .label { display: block;}
}
.preset-section.cf,
.inspector-preset.cf,
.preset-section-input.cf {
width: 100%;
}
.preset-search-input {
width:100%;
}
.preset-search-input input {
width: 100%;
padding: 5px;
}
.preset-search-result {
padding: 0px 10px;
height:30px;
margin: 5px;
}
.preset-label {
padding: 5px;
}
.preset-fav button.fav {
height: 30px;
margin: 5px;
padding: 0 10px;
}
.preset-input input {
width: 100%;
}
div.combobox {
width:155px;
z-index: 9999;
@@ -1431,7 +1502,7 @@ div.combobox {
border: 1px solid #ccc;
}
div.combobox a {
.combobox a {
height: 25px;
line-height: 25px;
cursor: pointer;
@@ -1442,18 +1513,26 @@ div.combobox a {
white-space: nowrap;
}
div.combobox a:hover,
div.combobox a.selected {
.combobox a:hover,
.combobox a.selected {
background: #e1e8ff;
color: #154dff;
}
div.combobox a:first-child {
.combobox a:first-child {
border-top: 0;
}
div.combobox-carat {
cursor: pointer;
padding:0 5px;
vertical-align:middle;
.combobox-carat::after {
display:block;
content: '';
cursor:url(../img/cursor-pointer.png) 6 1, auto;
position: absolute;
right: 10px;
top: 5px;
height: 0;
width: 0;
border-top: 5px solid #ccc;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
+15 -4
View File
@@ -18,6 +18,7 @@
<script src='js/lib/ohauth.js'></script>
<script src='js/lib/jxon.js'></script>
<script src='js/lib/d3.typeahead.js'></script>
<script src='js/lib/d3.combobox.js'></script>
<script src='js/lib/d3.geo.tile.js'></script>
<script src='js/lib/d3.size.js'></script>
<script src='js/lib/d3.trigger.js'></script>
@@ -30,6 +31,7 @@
<script src='js/id/id.js'></script>
<script src='js/id/util.js'></script>
<script src='js/id/oauth.js'></script>
<script src='js/id/presetdata.js'></script>
<script src='js/id/services/taginfo.js'></script>
<script src='data/data.js'></script>
@@ -79,11 +81,16 @@
<script src='js/id/ui/restore.js'></script>
<script src='js/id/ui/tag_reference.js'></script>
<script src='js/id/ui/key_reference.js'></script>
<script src='js/id/ui/preset.js'></script>
<script src='js/id/ui/presetsearch.js'></script>
<script src='js/id/ui/presetfavs.js'></script>
<script src='js/id/ui/lasso.js'></script>
<script src='js/id/ui/source_switch.js'></script>
<script src='js/id/ui/toggle.js'></script>
<script src='js/id/ui/undo_redo.js'></script>
<script src='js/id/ui/zoom.js'></script>
<script src='js/id/ui/taglist.js'></script>
<script src='js/id/ui/presetgrid.js'></script>
<script src='js/id/actions.js'></script>
<script src="js/id/actions/add_midpoint.js"></script>
@@ -171,11 +178,15 @@
var id = iD();
d3.json('keys.json', function(err, keys) {
id.connection()
.keys(keys);
d3.json('presets/presets_josm.json', function(err, presets_data) {
d3.select("#iD")
.call(id.ui())
id.connection()
.keys(keys)
.presetData(iD.presetData().data(presets_data));
d3.select("#iD")
.call(id.ui())
});
});
</script>
+8
View File
@@ -4,6 +4,8 @@ iD.Connection = function(context) {
url = 'http://www.openstreetmap.org',
connection = {},
user = {},
version,
presetData = iD.presetData(),
keys,
inflight = {},
loadedTiles = {},
@@ -327,6 +329,12 @@ iD.Connection = function(context) {
return connection;
};
connection.presetData = function(_) {
if (!arguments.length) return presetData;
presetData = _;
return connection;
};
connection.authenticate = function(callback) {
function done(err, res) {
event.auth();
+3 -1
View File
@@ -83,7 +83,9 @@ iD.modes.Select = function(context, selection, initial) {
}), true));
if (entity) {
inspector.context(context);
inspector
.context(context)
.presetData(context.connection().presetData());
context.container()
.select('.inspector-wrap')
+42
View File
@@ -0,0 +1,42 @@
iD.presetData = function() {
var presets = {},
data = [];
presets.data = function(_) {
if (!arguments.length) return data;
data = _;
return presets;
};
presets.favs = function() {
return data.filter(function(d) {
return d.favorite;
});
};
presets.match = function(entity) {
var type = entity.type == 'node' ? 'node' : entity.geometry();
return data.filter(function(d) {
return _.contains(d.type, type);
});
};
presets.matchTags = function(entity) {
var tags, count, best,
maxcount = 0,
type = entity.type == 'node' ? 'node' : entity.geometry();
for (var i = 0; i < data.length; i++) {
count = 0;
tags = data[i].tags;
if (!_.contains(data[i].type, type)) continue;
for (var k in tags) {
if (entity.tags[k] == tags[k]) count++;
}
if (count > maxcount) best = data[i], maxcount = count;
}
return best;
};
return presets;
};
+113 -249
View File
@@ -1,53 +1,112 @@
iD.ui.Inspector = function() {
var event = d3.dispatch('changeTags', 'close'),
var event = d3.dispatch('changeTags', 'close', 'change'),
taginfo = iD.taginfo(),
presetData = iD.presetData(),
initial = false,
context,
tagList;
expert = false,
inspectorbody,
presetUI,
tagList,
context;
function inspector(selection) {
var entity = selection.datum();
presetMatch = presetData.matchTags(entity);
var inspector = selection.append('div')
.attr('class','inspector content hide');
var iwrap = selection.append('div')
.attr('class','inspector content hide'),
messagewrap = iwrap.append('div')
.attr('class', 'message inspector-inner fillL2'),
message = messagewrap.append('h4');
inspector.append('div')
.attr('class', 'head inspector-inner fillL')
.call(drawHead);
var inspectorbody = inspector.append('div')
.attr('class', 'inspector-body');
var inspectorwrap = inspectorbody.append('div')
.attr('class', 'inspector-inner tag-wrap fillL2');
inspectorwrap.append('h4')
.text(t('inspector.edit_tags'));
tagList = inspectorwrap.append('ul');
var newTag = inspectorwrap.append('button')
.attr('class', 'add-tag');
newTag.on('click', function () {
addTag();
focusNewKey();
});
newTag.append('span')
.attr('class', 'icon icon-pre-text plus');
newTag.append('span')
.attr('class', 'label')
.text(t('inspector.new_tag'));
drawTags(entity.tags);
inspectorbody.append('div')
inspectorbody = iwrap.append('div')
.attr('class', 'inspector-body'),
iwrap.append('div')
.attr('class', 'inspector-buttons pad1 fillD')
.call(drawButtons);
inspector.call(iD.ui.Toggle(true));
if (initial) {
inspectorbody.call(iD.ui.PresetGrid()
.presetData(presetData)
.entity(selection.datum())
.on('choose', function(preset) {
inspectorbody.call(drawEditor, entity, preset);
}));
} else {
inspectorbody.call(drawEditor, entity, presetMatch);
}
iwrap.call(iD.ui.Toggle(true));
}
function drawEditor(selection, entity, presetMatch) {
selection.html('');
var editorwrap = selection.append('div')
.attr('class', 'inspector-inner tag-wrap fillL2');
var typewrap = editorwrap.append('div')
.attr('class', 'type inspector-inner fillL');
typewrap.append('h4')
.text('Type');
typewrap.append('img')
.attr('class', 'preset-icon');
typewrap.append('h3')
.attr('class', 'preset-name')
.text(presetMatch ? presetMatch.name : '');
var namewrap = editorwrap.append('div')
.attr('class', 'head inspector-inner fillL'),
h2 = namewrap.append('h2');
h2.append('span')
.attr('class', 'icon big icon-pre-text big-' + entity.geometry(context.graph()));
var name = h2.append('input')
.attr('placeholder', 'name')
.property('value', function() {
return entity.tags.name || '';
})
.on('keyup', function() {
var tags = inspector.tags();
tags.name = this.value;
inspector.tags(tags);
event.change();
});
event.on('change.name', function() {
var tags = inspector.tags();
name.property('value', tags.name);
});
presetUI = iD.ui.preset()
.on('change', function(tags) {
event.change();
});
tagList = iD.ui.Taglist()
.context(context)
.on('change', function(tags) {
event.change();
});
var inspectorpreset = editorwrap.append('div')
.attr('class', 'inspector-preset cf');
if (presetMatch && !expert) {
inspectorpreset.call(presetUI
.preset(presetMatch));
}
var taglistwrap = editorwrap.append('div').call(tagList);
inspector.tags(entity.tags);
}
function drawHead(selection) {
@@ -80,210 +139,16 @@ iD.ui.Inspector = function() {
.attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId())
.attr('target', '_blank')
.text(t('inspector.view_on_osm'));
}
function drawTags(tags) {
var entity = tagList.datum();
tags = d3.entries(tags);
if (!tags.length) {
tags = [{key: '', value: ''}];
}
var li = tagList.html('')
.selectAll('li')
.data(tags, function(d) { return d.key; });
li.exit().remove();
var row = li.enter().append('li')
.attr('class', 'tag-row');
var inputs = row.append('div')
.attr('class', 'input-wrap');
inputs.append('input')
.property('type', 'text')
.attr('class', 'key')
.attr('maxlength', 255)
.property('value', function(d) { return d.key; })
.on('change', function(d) { d.key = this.value; });
inputs.append('input')
.property('type', 'text')
.attr('class', 'value')
.attr('maxlength', 255)
.property('value', function(d) { return d.value; })
.on('change', function(d) { d.value = this.value; })
.on('keydown.push-more', pushMore);
inputs.each(bindTypeahead);
var removeBtn = row.append('button')
.attr('tabindex', -1)
.attr('class','remove minor')
.on('click', removeTag);
removeBtn.append('span')
.attr('class', 'icon delete');
function findLocal(docs) {
var locale = iD.detect().locale.toLowerCase(),
localized;
localized = _.find(docs, function(d) {
return d.lang.toLowerCase() === locale;
});
if (localized) return localized;
// try the non-regional version of a language, like
// 'en' if the language is 'en-US'
if (locale.indexOf('-') !== -1) {
var first = locale.split('-')[0];
localized = _.find(docs, function(d) {
return d.lang.toLowerCase() === first;
});
if (localized) return localized;
}
// finally fall back to english
return _.find(docs, function(d) {
return d.lang.toLowerCase() === 'en';
});
}
function keyValueReference(err, docs) {
var local;
if (!err && docs) {
local = findLocal(docs);
}
if (local) {
var types = [];
if (local.on_area) types.push('area');
if (local.on_node) types.push('point');
if (local.on_way) types.push('line');
local.types = types;
iD.ui.modal(context.container())
.select('.content')
.datum(local)
.call(iD.ui.tagReference);
} else {
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_combination'));
}
}
function keyReference(err, values, params) {
if (!err && values.length) {
iD.ui.modal(context.container())
.select('.content')
.datum({
data: values,
title: 'Key:' + params.key,
geometry: params.geometry
})
.call(iD.ui.keyReference);
} else {
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_key'));
}
}
var helpBtn = row.append('button')
.attr('tabindex', -1)
.attr('class', 'tag-help minor')
.on('click', function(d) {
var params = _.extend({}, d, {
geometry: entity.geometry(context.graph())
});
if (d.key && d.value) {
taginfo.docs(params, keyValueReference);
} else if (d.key) {
taginfo.values(params, keyReference);
}
var expertButton = selection.append('button')
.attr('class', 'apply')
.text('Tag view')
.on('click', function() {
expert = !expert;
expertButton.text(expert ? 'Preset view' : 'Tag view');
inspectorbody.call(drawEditor);
});
helpBtn.append('span')
.attr('class', 'icon inspect');
if (initial && tags.length === 1 &&
tags[0].key === '' && tags[0].value === '') {
focusNewKey();
}
return li;
}
function pushMore() {
if (d3.event.keyCode === 9 &&
tagList.selectAll('li:last-child input.value').node() === this) {
addTag();
focusNewKey();
d3.event.preventDefault();
}
}
function bindTypeahead() {
var entity = tagList.datum(),
geometry = entity.geometry(context.graph()),
row = d3.select(this),
key = row.selectAll('.key'),
value = row.selectAll('.value');
function sort(value, data) {
var sameletter = [],
other = [];
for (var i = 0; i < data.length; i++) {
if (data[i].value.substring(0, value.length) === value) {
sameletter.push(data[i]);
} else {
other.push(data[i]);
}
}
return sameletter.concat(other);
}
key.call(d3.typeahead()
.data(_.debounce(function(_, callback) {
taginfo.keys({
geometry: geometry,
query: key.property('value')
}, function(err, data) {
if (!err) callback(sort(key.property('value'), data));
});
}, 500)));
value.call(d3.typeahead()
.data(_.debounce(function(_, callback) {
taginfo.values({
key: key.property('value'),
geometry: geometry,
query: value.property('value')
}, function(err, data) {
if (!err) callback(sort(value.property('value'), data));
});
}, 500)));
}
function focusNewKey() {
tagList.selectAll('li:last-child input.key').node().focus();
}
function addTag() {
var tags = inspector.tags();
tags[''] = '';
drawTags(tags);
}
function removeTag(d) {
var tags = inspector.tags();
delete tags[d.key];
drawTags(tags);
}
function apply(entity) {
@@ -293,16 +158,10 @@ iD.ui.Inspector = function() {
inspector.tags = function(tags) {
if (!arguments.length) {
tags = {};
tagList.selectAll('li').each(function() {
var row = d3.select(this),
key = row.selectAll('.key').property('value'),
value = row.selectAll('.value').property('value');
if (key !== '') tags[key] = value;
});
return tags;
return _.extend(presetUI.tags(), tagList.tags());
} else {
drawTags(tags);
presetUI.change(tags);
tagList.tags(_.omit(tags, _.keys(presetUI.tags() || {})));
}
};
@@ -311,6 +170,11 @@ iD.ui.Inspector = function() {
return inspector;
};
inspector.presetData = function(_) {
presetData = _;
return inspector;
};
inspector.context = function(_) {
context = _;
return inspector;
+132
View File
@@ -0,0 +1,132 @@
iD.ui.preset = function() {
var event = d3.dispatch('change'),
hidden,
sections,
exttags,
preset;
function getTags() {
var tags = _.clone(preset.tags);
sections.selectAll('input,select')
.each(function(d) {
tags[d.key] = this.value;
});
return tags;
}
function setTags(tags) {
if (!sections) return;
sections.selectAll('input,select')
.each(function(d) {
if (tags[d.key]) {
this.value = tags[d.key];
}
});
}
function clean(o) {
var out = {};
for (var k in o) {
if (o[k] !== '') out[k] = o[k];
}
return out;
}
function key() {
var tags = clean(getTags());
event.change(tags);
}
// generate form fields for a given field.
function input(d) {
var i;
switch (d.type) {
case 'text':
i = this.append('input')
.attr('type', 'text')
.attr('id', 'input-' + d.key)
.attr('placeholder', d['default'] || '');
break;
case 'tel':
i = this.append('input')
.attr('type', 'tel')
.attr('id', 'input-' + d.key)
.attr('placeholder', '1-555-555-5555');
break;
case 'email':
i = this.append('input')
.attr('type', 'email')
.attr('id', 'input-' + d.key)
.attr('placeholder', 'email@domain.com');
break;
case 'url':
i = this.append('input')
.attr('type', 'url')
.attr('id', 'input-' + d.key)
.attr('placeholder', 'http://example.com/');
break;
case 'check':
i = this.append('input')
.attr('type', 'checkbox')
.attr('id', 'input-' + d.key)
.each(function() {
if (d['default']) {
this.attr('checked', 'checked');
}
});
break;
case 'select':
var w = this.append('span').attr('class', 'input-wrap-position');
i = w.append('input');
w.call(d3.combobox()
.data([''].concat(d.values.slice()).map(function(o) {
return { value: o, title: o };
})));
break;
}
if (i) {
i.on('change', key);
}
}
function presets(selection) {
selection.html('');
sections = selection.selectAll('div.preset-section')
.data(preset.main)
.enter()
.append('div')
.attr('class', 'preset-section cf');
sections.each(function(d) {
var s = d3.select(this);
var wrap = s.append('div')
.attr('class', 'preset-section-input cf');
wrap.append('div')
.attr('class', 'col4 preset-label')
.append('label')
.attr('for', 'input-' + d.key)
.text(d.text);
input.call(wrap.append('div')
.attr('class', 'col8 preset-input'), d);
});
if (exttags) setTags(exttags);
}
presets.preset = function(_) {
if (!arguments.length) return preset;
preset = _;
return presets;
};
presets.change = function(_) {
exttags = _;
setTags(_);
return presets;
};
presets.tags = function() {
if (hidden || !preset || !sections) return {};
return clean(getTags());
};
return d3.rebind(presets, event, 'on');
};
+30
View File
@@ -0,0 +1,30 @@
iD.ui.presetfavs = function() {
var event = d3.dispatch('choose'),
presetData;
function favs(selection) {
var favData = presetData.favs();
selection.append('div')
.attr('class', 'preset-fav')
.selectAll('button.fav')
.data(favData)
.enter()
.append('button')
.attr('class', 'fav')
.text(function(d) {
return d.name;
})
.on('click', function(d) {
event.choose(d);
});
}
favs.presetData = function(_) {
if (!arguments.length) return presetData;
presetData = _;
return favs;
};
return d3.rebind(favs, event, 'on');
};
+71
View File
@@ -0,0 +1,71 @@
iD.ui.PresetGrid = function() {
var event = d3.dispatch('choose'),
entity,
presetData;
function presetgrid(selection) {
selection.html('');
var wrap = selection.append('div')
.attr('class', 'fillL');
var viable = presetData.match(entity);
var grid = wrap.append('div')
.attr('class', 'preset-grid')
.call(drawGrid, filter(''));
var searchwrap = wrap.append('div')
.attr('class', 'preset-grid-search-wrap');
var search = searchwrap.append('input')
.attr('class', 'preset-grid-search')
.on('keyup', function() {
grid.call(drawGrid, filter(search.property('value')));
});
function filter(value) {
value = value.toLowerCase();
return viable.filter(function(v) {
return v.name.toLowerCase().indexOf(value) !== -1;
});
}
}
function name(d) { return d.name; }
function drawGrid(selection, presets) {
var entries = selection
.selectAll('div.grid-entry')
.data(presets.slice(0, 12), name);
entries.enter()
.append('div')
.attr('class', 'grid-entry')
.text(name)
.on('click', function(d) {
event.choose(d);
});
entries.exit().remove();
}
presetgrid.presetData = function(_) {
if (!arguments.length) return presetData;
presetData = _;
return presetgrid;
};
presetgrid.entity = function(_) {
if (!arguments.length) return entity;
entity = _;
return presetgrid;
};
return d3.rebind(presetgrid, event, 'on');
};
+55
View File
@@ -0,0 +1,55 @@
iD.ui.presetsearch = function() {
var event = d3.dispatch('choose'),
entity,
presetData;
function search(selection) {
var viable = presetData.match(entity);
function filter(value) {
value = value.toLowerCase();
return viable.filter(function(v) {
return v.name.toLowerCase().indexOf(value) !== -1;
}).map(function(v) {
return {
title: v.name,
value: v.name
};
});
}
function find(value) {
return _.find(viable, function(v) {
return v.name == value;
});
}
var preset_search_input = selection.append('div')
.attr('class', 'preset-search-input')
.append('h3')
.append('input')
.attr('placeholder', 'preset search')
.call(d3.typeahead()
.autohighlight(true)
.data(function(_, callback) {
callback(filter(preset_search_input.property('value')));
})
.on('accept', function() {
event.choose(find(preset_search_input.property('value')));
}));
}
search.presetData = function(_) {
if (!arguments.length) return presetData;
presetData = _;
return search;
};
search.entity = function(_) {
if (!arguments.length) return entity;
entity = _;
return search;
};
return d3.rebind(search, event, 'on');
};
+263
View File
@@ -0,0 +1,263 @@
iD.ui.Taglist = function() {
var event = d3.dispatch('change'),
taginfo = iD.taginfo(),
initial = false,
list,
context;
function taglist(selection) {
//selection.append('h4')
//.text(t('inspector.edit_tags'));
list = selection.append('ul')
.attr('class', 'tag-list');
var newTag = selection.append('button')
.attr('class', 'add-tag');
newTag.on('click', function() {
addTag();
focusNewKey();
});
newTag.append('span')
.attr('class', 'icon icon-pre-text plus');
newTag.append('span')
.attr('class', 'label')
.text(t('inspector.new_tag'));
}
function drawTags(tags) {
var entity = list.datum();
tags = d3.entries(tags);
if (!tags.length) {
tags = [{key: '', value: ''}];
}
var li = list.html('')
.selectAll('li')
.data(tags, function(d) { return d.key; });
li.exit().remove();
var row = li.enter().append('li')
.attr('class', 'tag-row');
var inputs = row.append('div')
.attr('class', 'input-wrap');
inputs.append('span')
.attr('class', 'key-wrap')
.append('input')
.property('type', 'text')
.attr('class', 'key')
.attr('maxlength', 255)
.property('value', function(d) { return d.key; })
.on('change', function(d) { d.key = this.value; event.change(); });
inputs.append('span')
.attr('class', 'input-wrap-position')
.append('input')
.property('type', 'text')
.attr('class', 'value')
.attr('maxlength', 255)
.property('value', function(d) { return d.value; })
.on('change', function(d) { d.value = this.value; event.change(); })
.on('keydown.push-more', pushMore);
inputs.each(bindTypeahead);
var removeBtn = row.append('button')
.attr('tabindex', -1)
.attr('class','remove minor')
.on('click', removeTag);
removeBtn.append('span')
.attr('class', 'icon delete');
function findLocal(docs) {
var locale = iD.detect().locale.toLowerCase(),
localized;
localized = _.find(docs, function(d) {
return d.lang.toLowerCase() === locale;
});
if (localized) return localized;
// try the non-regional version of a language, like
// 'en' if the language is 'en-US'
if (locale.indexOf('-') !== -1) {
var first = locale.split('-')[0];
localized = _.find(docs, function(d) {
return d.lang.toLowerCase() === first;
});
if (localized) return localized;
}
// finally fall back to english
return _.find(docs, function(d) {
return d.lang.toLowerCase() === 'en';
});
}
function keyValueReference(err, docs) {
var local;
if (!err && docs) {
local = findLocal(docs);
}
if (local) {
var types = [];
if (local.on_area) types.push('area');
if (local.on_node) types.push('point');
if (local.on_way) types.push('line');
local.types = types;
iD.ui.modal(context.container())
.select('.content')
.datum(local)
.call(iD.ui.tagReference);
} else {
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_combination'));
}
}
function keyReference(err, values, params) {
if (!err && values.length) {
iD.ui.modal(context.container())
.select('.content')
.datum({
data: values,
title: 'Key:' + params.key,
geometry: params.geometry
})
.call(iD.ui.keyReference);
} else {
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_key'));
}
}
var helpBtn = row.append('button')
.attr('tabindex', -1)
.attr('class', 'tag-help minor')
.on('click', function(d) {
var params = _.extend({}, d, {
geometry: entity.geometry(context.graph())
});
if (d.key && d.value) {
taginfo.docs(params, keyValueReference);
} else if (d.key) {
taginfo.values(params, keyReference);
}
});
helpBtn.append('span')
.attr('class', 'icon inspect');
if (initial && tags.length === 1 &&
tags[0].key === '' && tags[0].value === '') {
focusNewKey();
}
return li;
}
function pushMore() {
if (d3.event.keyCode === 9 &&
list.selectAll('li:last-child input.value').node() === this) {
addTag();
focusNewKey();
d3.event.preventDefault();
}
}
function bindTypeahead() {
var entity = list.datum(),
geometry = entity.geometry(context.graph()),
row = d3.select(this),
key = row.selectAll('.key'),
value = row.selectAll('.input-wrap-position');
function sort(value, data) {
var sameletter = [],
other = [];
for (var i = 0; i < data.length; i++) {
if (data[i].value.substring(0, value.length) === value) {
sameletter.push(data[i]);
} else {
other.push(data[i]);
}
}
return sameletter.concat(other);
}
key.call(d3.typeahead()
.data(_.debounce(function(_, callback) {
taginfo.keys({
geometry: geometry,
query: key.property('value')
}, function(err, data) {
if (!err) callback(sort(key.property('value'), data));
});
}, 500)));
var valueinput = value.select('input');
value.call(d3.combobox()
.fetcher(_.debounce(function(_, __, callback) {
taginfo.values({
key: key.property('value'),
geometry: geometry,
query: valueinput.property('value')
}, function(err, data) {
if (!err) callback(sort(valueinput.property('value'), data));
});
}, 500)));
}
function focusNewKey() {
list.selectAll('li:last-child input.key').node().focus();
}
function addTag() {
var tags = taglist.tags();
tags[''] = '';
drawTags(tags);
}
function removeTag(d) {
var tags = taglist.tags();
delete tags[d.key];
drawTags(tags);
}
taglist.tags = function(tags) {
if (!arguments.length) {
tags = {};
list.selectAll('li').each(function() {
var row = d3.select(this),
key = row.selectAll('.key').property('value'),
value = row.selectAll('.value').property('value');
if (key !== '') tags[key] = value;
});
return tags;
} else {
drawTags(tags);
}
};
taglist.context = function(_) {
context = _;
return taglist;
};
return d3.rebind(taglist, event, 'on');
};
+20
View File
@@ -75,6 +75,26 @@ iD.util.getStyle = function(selector) {
}
};
iD.util.editDistance = function(a, b) {
if (a.length === 0) return b.length;
if (b.length === 0) return a.length;
var matrix = [];
for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
if (b.charAt(i-1) == a.charAt(j-1)) {
matrix[i][j] = matrix[i-1][j-1];
} else {
matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
Math.min(matrix[i][j-1] + 1, // insertion
matrix[i-1][j] + 1)); // deletion
}
}
}
return matrix[b.length][a.length];
};
// a d3.mouse-alike which
// 1. Only works on HTML elements, not SVG
// 2. Does not cause style recalculation
+131 -68
View File
@@ -1,16 +1,21 @@
d3.combobox = function() {
var event = d3.dispatch('accept'),
autohighlight = false,
autofilter = false,
input,
container,
data;
container, input, shown = false, data = [];
var fetcher = function(val, data, cb) {
cb(data.filter(function(d) {
return d.title
.toString()
.toLowerCase()
.indexOf(val.toLowerCase()) !== -1;
}));
};
var typeahead = function(selection) {
var hidden, idx = autohighlight ? 0 : -1;
var rect = selection.select('input').node().getBoundingClientRect();
var idx = -1,
rect = selection.select('input')
.node()
.getBoundingClientRect();
input = selection.select('input');
container = selection
@@ -25,104 +30,174 @@ d3.combobox = function() {
});
carat = selection
.insert('div', ':first-child')
.insert('a', ':first-child')
.attr('class', 'combobox-carat')
.text('+')
.style({
position: 'absolute',
left: (rect.width - 20) + 'px',
left: rect.width + 'px',
top: '0px'
})
.on('click', function() {
update();
show();
});
.on('mousedown', stop)
.on('click', click);
selection
.on('keyup.typeahead', key);
function stop() {
// prevent the form element from blurring. it blurs
// on mousedown
d3.event.stopPropagation();
d3.event.preventDefault();
}
hidden = false;
function click() {
d3.event.preventDefault();
d3.event.stopPropagation();
update();
show();
// focus the node so that a click outside of the
// combo box will hide it
input.node().focus();
}
function hide() {
idx = autohighlight ? 0 : -1;
hidden = true;
function blur() {
// hide the combobox whenever the input element
// loses focus
slowHide();
}
function show() {
container.style('display', 'block');
shown = true;
}
function hide() {
idx = -1;
container.style('display', 'none');
shown = false;
}
function slowHide() {
if (autohighlight && container.select('a.selected').node()) {
select(container.select('a.selected').datum());
event.accept();
}
window.setTimeout(hide, 150);
}
selection
.on('focus.typeahead', show)
.on('blur.typeahead', slowHide);
function key() {
var len = container.selectAll('a').data().length;
if (d3.event.keyCode === 40) {
idx = Math.min(idx + 1, len - 1);
return highlight();
} else if (d3.event.keyCode === 38) {
idx = Math.max(idx - 1, 0);
return highlight();
} else if (d3.event.keyCode === 13) {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
event.accept();
hide();
} else {
update();
function keydown() {
if (!shown) return;
switch (d3.event.keyCode) {
// down arrow
case 40:
next();
d3.event.preventDefault();
break;
// up arrow
case 38:
prev();
d3.event.preventDefault();
break;
// escape, tab
case 9:
case 13:
d3.event.preventDefault();
break;
}
d3.event.stopPropagation();
}
function keyup() {
switch (d3.event.keyCode) {
// escape
case 27:
hide();
break;
// escape, tab
case 9:
case 13:
if (!shown) return;
accept();
break;
default:
update();
d3.event.preventDefault();
}
d3.event.stopPropagation();
}
function accept() {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
hide();
}
function next() {
var len = container.selectAll('a').data().length;
idx = Math.min(idx + 1, len - 1);
highlight();
}
function prev() {
idx = Math.max(idx - 1, 0);
highlight();
}
function highlight() {
container
.selectAll('a')
.classed('selected', function(d, i) { return i == idx; });
var height = container.node().offsetHeight,
top = container.select('a.selected').node().offsetTop,
selectedHeight = container.select('a.selected').node().offsetHeight;
if ((top + selectedHeight) < height) {
container.node().scrollTop = 0;
} else {
container.node().scrollTop = top;
}
}
function update() {
function run(data) {
container.style('display', function() {
return data.length ? 'block' : 'none';
});
function render(data) {
if (data.length) show();
else hide();
var options = container
.selectAll('a')
.selectAll('a.combobox-option')
.data(data, function(d) { return d.value; });
options.enter()
.append('a')
.text(function(d) { return d.value; })
.attr('class', 'combobox-option')
.attr('title', function(d) { return d.title; })
.on('click', select);
options.exit().remove();
options
.classed('selected', function(d, i) { return i == idx; });
.classed('selected', function(d, i) { return i == idx; })
.order();
}
if (typeof data === 'function') data(selection, run);
else run(data);
fetcher.apply(selection, [
selection.select('input').property('value'),
data, render]);
}
// select the choice given as d
function select(d) {
input
.property('value', d.value)
.trigger('change');
container.style('display', 'none');
event.accept(d);
hide();
}
input
.on('blur.typeahead', blur)
.on('keydown.typeahead', keydown)
.on('keyup.typeahead', keyup);
};
typeahead.fetcher = function(_) {
if (!arguments.length) return fetcher;
fetcher = _;
return typeahead;
};
typeahead.data = function(_) {
@@ -131,17 +206,5 @@ d3.combobox = function() {
return typeahead;
};
typeahead.autofilter = function(_) {
if (!arguments.length) return autofilter;
autofilter = _;
return typeahead;
};
typeahead.autohighlight = function(_) {
if (!arguments.length) return autohighlight;
autohighlight = _;
return typeahead;
};
return d3.rebind(typeahead, event, 'on');
};
+21 -4
View File
@@ -1,8 +1,12 @@
d3.typeahead = function() {
var data;
var event = d3.dispatch('accept'),
autohighlight = false,
data;
var typeahead = function(selection) {
var container, hidden, idx = -1;
var container,
hidden,
idx = autohighlight ? 0 : -1;
function setup() {
var rect = selection.node().getBoundingClientRect();
@@ -20,11 +24,17 @@ d3.typeahead = function() {
function hide() {
container.remove();
idx = -1;
idx = autohighlight ? 0 : -1;
hidden = true;
}
function slowHide() {
if (autohighlight) {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
event.accept();
}
}
window.setTimeout(hide, 150);
}
@@ -44,6 +54,7 @@ d3.typeahead = function() {
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
event.accept();
hide();
} else {
update();
@@ -95,5 +106,11 @@ d3.typeahead = function() {
return typeahead;
};
return typeahead;
typeahead.autohighlight = function(_) {
if (!arguments.length) return autohighlight;
autohighlight = _;
return typeahead;
};
return d3.rebind(typeahead, event, 'on');
};
+83
View File
@@ -0,0 +1,83 @@
from xml.dom.minidom import parse
import json
import os
import re
dirr = os.path.dirname(__file__)
def relative(x):
return os.path.join(dirr, x)
prefs = json.load(open(relative('prefs.json')))
dom1 = parse(relative('./josm.xml'))
jsonOutput = []
def isemail(x):
return re.search('email', x, flags=re.IGNORECASE)
def iswebsite(x):
return re.search('web', x, flags=re.IGNORECASE)
def istel(x):
return re.search('phone|tel|fax', x, flags=re.IGNORECASE)
def isfav(x):
return x in prefs
for item in dom1.getElementsByTagName('item'):
tags = {}
for elem in item.getElementsByTagName('key'):
tags[elem.getAttribute('key')] = elem.getAttribute('value')
jitem = {
"name": item.getAttribute('name'),
"type": item.getAttribute('type').replace('closedway', 'area').replace('way', 'line').split(','),
"tags": tags,
"main": []
}
if isfav(jitem['name']):
jitem['favorite'] = True
for n in item.getElementsByTagName('text'):
txt = n.getAttribute('text')
type = 'text'
if isemail(txt):
type = 'email'
elif iswebsite(txt):
type = 'url'
elif istel(txt):
type = 'tel'
jitem['main'].append({
'type': type,
'key': n.getAttribute('key'),
'text': n.getAttribute('text')
})
for n in item.getElementsByTagName('combo'):
jitem['main'].append({
'type': 'select',
'key': n.getAttribute('key'),
'text': n.getAttribute('text'),
'values': n.getAttribute('values').split(',')
})
for n in item.getElementsByTagName('check'):
jitem['main'].append({
'type': 'check',
'key': n.getAttribute('key'),
'text': n.getAttribute('text'),
'default': (n.getAttribute('check') == 'true')
})
jsonOutput.append(jitem)
json.dump(jsonOutput, open(relative('presets_josm.json'), 'w'), indent=4)
+47
View File
@@ -0,0 +1,47 @@
from xml.dom.minidom import parse
import json
dom1 = parse('potlatch.xml')
inputSets = dom1.getElementsByTagName('inputSet')
jsonOutput = []
for inputSet in inputSets:
setId = inputSet.getAttribute('id')
inputs = inputSet.getElementsByTagName('input')
for i in inputs:
jsonInput = {}
inputType = i.getAttribute('type')
if inputType == 'choice':
choices = i.getElementsByTagName('choice')
jsonInput['type'] = 'choice'
jsonInput['description'] = i.getAttribute('description')
jsonInput['name'] = i.getAttribute('name')
jsonInput['key'] = i.getAttribute('key')
jsonInput['choices'] = []
for c in choices:
jsonInput['choices'].append({
"value": c.getAttribute('value'),
"text": c.getAttribute('text')
})
elif inputType == 'freetext':
jsonInput['type'] = 'freetext'
jsonInput['description'] = i.getAttribute('description')
jsonInput['name'] = i.getAttribute('name')
jsonInput['key'] = i.getAttribute('key')
elif inputType == 'checkbox':
jsonInput['type'] = 'checkbox'
jsonInput['description'] = i.getAttribute('description')
jsonInput['name'] = i.getAttribute('name')
jsonInput['key'] = i.getAttribute('key')
elif inputType == 'number':
jsonInput['type'] = 'number'
jsonInput['description'] = i.getAttribute('description')
jsonInput['name'] = i.getAttribute('name')
jsonInput['minimum'] = i.getAttribute('minimum')
jsonInput['maximum'] = i.getAttribute('maximum')
jsonInput['key'] = i.getAttribute('key')
jsonOutput.append(jsonInput)
json.dump(jsonOutput, open('presets_potlatch.json', 'w'), indent=4)
+5819
View File
File diff suppressed because it is too large Load Diff
+693
View File
@@ -0,0 +1,693 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
See http://wiki.openstreetmap.org/wiki/Potlatch_2/Developer_Documentation/Map_Features for documentation.
Files can be included like this: <include file="map_features/roads.xml">
-->
<mapFeatures>
<!-- Categories -->
<category name="Roads" id="roads"/>
<category name="Paths" id="paths"/>
<category name="Shopping" id="shopping"/>
<category name="Food and Drink" id="foodanddrink"/>
<category name="Amenity" id="amenity"/>
<category name="Tourism" id="tourism"/>
<category name="Accommodation" id="accommodation"/>
<category name="Transport" id="transport"/>
<category name="Water" id="water"/>
<category name="Natural" id="natural"/>
<category name="Barrier" id="barrier"/>
<category name="Power" id="power"/>
<category name="Admin" id="admin"/>
<category name="Buildings" id="buildings"/>
<category name="Landuse" id="landuse"/>
<category name="Agriculture" id="agriculture"/>
<category name="Advanced" id="advanced"/>
<category name="Places" id="places"/>
<category name="Sport and Leisure" id="sport"/>
<category name="Man-made" id="manmade"/>
<!-- Common input sets -->
<inputSet id="simpleName">
<input type="freetext" presence="always" name="Name" key="name" description="The name" priority="highest"/>
</inputSet>
<inputSet id="nameAndRef">
<inputSet ref="simpleName" />
<input type="freetext" presence="always" name="Reference number" key="ref" description="A reference number or code used to identify this thing." />
</inputSet>
<inputSet id="simpleBrand">
<input type="freetext" category="Naming" presence="always" name="Brand" key="brand" description="Brand, i.e. Acme"/>
</inputSet>
<inputSet id="simpleOperator">
<input type="freetext" category="Naming" presence="always" name="Operator" key="operator" description="Operator, i.e. Acme Springfield Ltd"/>
</inputSet>
<inputSet id="source">
<input type="freetext" category="Details" presence="onTagMatch" name="Source" key="source" description="The primary source of information for this object (GPS, survey, Bing, ...)" priority="lowest"/>
</inputSet>
<inputSet id="designation">
<input type="freetext" category="Details" presence="onTagMatch" description="The official designation or classification (if any). Only use this if the organisation that runs it has its own classification system." name="Official classification" key="designation" priority="lowest" />
</inputSet>
<inputSet id="common">
<inputSet ref="source"/>
<inputSet ref="designation"/>
</inputSet>
<inputSet id="names">
<input type="freetext" presence="always"
name="Name" category="Details" priority="highest"
key="name" description="The most common name"/>
<input type="freetext" presence="onTagMatch"
name="International Name" category="Details" subcategory="Additional names"
key="int_name" description="The internationally recognised name"/>
<input type="freetext" presence="onTagMatch"
name="Historical Name" category="Details" subcategory="Additional names" priority="low"
key="old_name" description="The historic or previous name"/>
<input type="freetext" presence="onTagMatch"
name="Alternative Name" category="Details" subcategory="Additional names" priority="low"
key="alt_name" description="An alternative, currently used, name"/>
</inputSet>
<inputSet id="wifi">
<input type="choice" presence="onTagMatch" category="Details" name="Wifi" key="wifi">
<choice value="free" text="Free"/>
<choice value="yes" text="Yes"/>
<choice value="no" text="No"/>
</input>
</inputSet>
<inputSet id="buildingAddress">
<input type="freetext" presence="onTagMatch" category="Address" description="The number of the house, e.g. 156 or 10-12" name="House Number" key="addr:housenumber"/>
<input type="freetext" presence="onTagMatch" category="Address" description="The name of the house, e.g. Riverbank Cottage" name="Building Name" key="addr:housename"/>
<input type="freetext" presence="onTagMatch" category="Address" description="The Street Name (optional)" name="Street Name" key="addr:street"/>
<input type="freetext" presence="onTagMatch" category="Address" description="The postcode" name="Postcode" key="addr:postcode"/>
</inputSet>
<inputSet id="web">
<input type="freetext" presence="onTagMatch" category="Address" description="The URL of the website" name="Website" key="website"/>
</inputSet>
<inputSet id="cuisine">
<input type="choice" presence="always" name="Cuisine" category="Details" description="The type of food that they serve" key="cuisine">
<!-- The 30 most popular values according to taginfo 23/12/2010 -->
<choice value="burger" text="Burger"/>
<choice value="chicken" text="Chicken"/>
<choice value="chinese" text="Chinese"/>
<choice value="coffee_shop" text="Coffee Shop"/>
<choice value="greek" text="Greek"/>
<choice value="pizza" text="Pizza"/>
<choice value="sandwich" text="Sandwich"/>
<choice value="seafood" text="Sea Food"/>
<choice value="regional" text="Regional"/>
<choice value="italian" text="Italian"/>
<choice value="german" text="German"/>
<choice value="kebab" text="Kebab/souvlaki/gyro"/>
<choice value="indian" text="Indian"/>
<choice value="asian" text="Asian"/>
<choice value="mexican" text="Mexican"/>
<choice value="thai" text="Thai"/>
<choice value="japanese" text="Japanese"/>
<choice value="ice_cream" text="Ice-cream"/>
<choice value="fish_and_chips" text="Fish &amp; Chips"/>
<choice value="turkish" text="Turkish"/>
<choice value="french" text="French"/>
<choice value="sushi" text="Sushi"/>
<choice value="american" text="American"/>
<choice value="steak_house" text="Steak House"/>
<choice value="international" text="International"/>
<choice value="spanish" text="Spanish"/>
<choice value="vietnamese" text="Vietnamese"/>
<choice value="fish" text="Fish"/>
<choice value="bavarian" text="Bavarian"/>
<choice value="vegetarian" text="Vegetarian"/>
<help>http://wiki.openstreetmap.org/wiki/Key:cuisine</help>
</input>
</inputSet>
<!-- Roads -->
<inputSet id="majorRoad">
<inputSet ref="names"/>
<inputSet ref="roadRefs"/>
<inputSet ref="roadRestrictions"/>
<inputSet ref="roadPhysical"/>
<inputSet ref="cycle"/>
<inputSet ref="bicycle-lane"/>
<inputSet ref="bus-route"/>
<inputSet ref="tram-route"/>
<inputSet ref="pedestrians"/>
<inputSet ref="roadLanes"/>
<inputSet ref="roadRoundabout"/>
<inputSet ref="permissions"/>
</inputSet>
<inputSet id="minorRoad">
<inputSet ref="names"/>
<inputSet ref="roadRestrictions"/>
<inputSet ref="roadPhysical"/>
<inputSet ref="cycle"/>
<inputSet ref="bicycle-lane"/>
<inputSet ref="bus-route"/>
<inputSet ref="tram-route"/>
<inputSet ref="pedestrians"/>
<inputSet ref="roadLanes"/>
<inputSet ref="roadRoundabout"/>
<inputSet ref="permissions"/>
</inputSet>
<inputSet id="path">
<inputSet ref="simpleName"/>
<inputSet ref="roadPhysical"/>
<inputSet ref="cycle"/>
<inputSet ref="pedestrians"/>
<inputSet ref="permissions"/>
</inputSet>
<inputSet id="junctionNode">
<inputSet ref="turnRestrictions"/>
</inputSet>
<inputSet id="roadRefs">
<input type="freetext" presence="always"
name="Reference" category="Details" priority="high"
key="ref" description="The official reference number"/>
<input type="freetext" presence="onTagMatch"
name="International Reference" category="Details" subcategory="Additional names"
key="int_ref" description="The official international reference number"/>
<input type="freetext" presence="onTagMatch"
name="Old Reference" category="Details" subcategory="Additional names" priority="low"
key="old_ref" description="The historic or previous reference number"/>
</inputSet>
<inputSet id="roadPhysical">
<input type="freetext" presence="onTagMatch"
name="Width" category="Details" subcategory="Physical"
key="width" description="Width of the road" layout="horizontal"/>
<input type="choice" presence="onTagMatch"
name="Surface" category="Details" description="Type of road surface"
key="surface" layout="horizontal">
<choice value="unpaved" text="Unpaved" description="Road surface is unsealed"/>
<choice value="paved" text="Paved" description="Road surface is sealed"/>
<choice value="asphalt" text="Asphalt"/>
<choice value="concrete" text="Concrete"/>
<choice value="paving_stones" text="Paving stones"/>
<choice value="cobblestone" text="Cobblestone"/>
<choice value="sand" text="Sand"/>
<choice value="gravel" text="Gravel"/>
<choice value="dirt" text="Dirt"/>
<choice value="grass" text="Grass"/>
</input>
<inputSet ref="bridge"/>
<inputSet ref="tunnel"/>
<inputSet ref="embankment-cutting"/>
<!-- not sure which category best suits put area=yes -->
<input type="checkbox" presence="onTagMatch" category="Details" subcategory="Physical" key="area" name="Open area" description="The way is a large open space, like at a dock, where vehicles can move anywhere within the space, rather than just along the edge." />
</inputSet>
<inputSet id="roadLanes">
<input presence="onTagMatch" type="number" name="Lanes" category="Details" description="Total number of lanes, counting both directions"
key="lanes" minimum="1" maximum="10" layout="horizontal"/>
</inputSet>
<inputSet id="bridge">
<input type="choice" presence="onTagMatch"
name="Bridge" category="Details" description="Road goes over a bridge"
key="bridge" layout="horizontal">
<choice value="yes" text="Generic Bridge" description="Generic bridge -- type unknown"/>
<choice value="viaduct" text="Viaduct" description="Viaduct"/>
<choice value="suspension" text="Suspension bridge"/>
</input>
<input type="slider" presence="onTagMatch"
name="Layer" category="Details" description="Relative vertical positions (-5 lowest, +5 highest)"
key="layer" minimum="-5" maximum="5" default="0" snapInterval="1" labels="Lowest,Ground,Highest"
defaultName="Ground"/>
</inputSet>
<inputSet id="tunnel">
<!-- Not ideal, used for non-roads too. -->
<input type="choice" presence="onTagMatch"
name="Tunnel" category="Details" subcategory="Physical" description="Road goes into a tunnel"
key="tunnel" layout="horizontal">
<choice value="yes" text="Tunnel" description="Generic tunnel"/>
</input>
</inputSet>
<inputSet id="embankment-cutting">
<input type="choice"
name="Embankment" category="Details" subcategory="Physical" description="Road supported on a raised bed of earth and rock."
key="embankment" layout="horizontal">
<choice value="yes" text="Embankment"/>
</input>
<input type="choice"
name="Cutting" category="Details" subcategory="Physical" description="Road carved out of hill on one or both sides."
key="cutting" layout="horizontal">
<choice value="yes" text="Cutting"/>
</input>
</inputSet>
<inputSet id="rail-electrification">
<input type="choice" name="Electrified" category="Details" subcategory="Electrification" description="Is the track electrified (whether by 3rd rail, overhead wires, etc)?"
key="electrified">
<choice value="yes" text="Yes"/>
<choice value="contact_line" text="Overhead line"/>
<choice value="rail" text="Third rail"/>
<choice value="no" text="No"/>
</input>
<input type="choice" name="Voltage" category="Details" subcategory="Electrification" description="Nominal voltage of electric wires"
key="voltage" presence="withCategory">
<choice value="600" text="600V"/>
<choice value="750" text="750V"/>
<choice value="1500" text="1500V"/>
<choice value="3000" text="3000V"/>
<choice value="12000" text="12kV"/>
<choice value="15000" text="15kV"/>
<choice value="25000" text="25kV"/>
</input>
<input type="choice" name="Frequency" category="Details" subcategory="Electrification" description="Frequency in Hertz of alternating current power supply"
key="frequency" presence="withCategory">
<choice value="0" text="DC"/>
<choice value="16.67" text="16.67 Hz"/>
<choice value="16.7" text="16.7 Hz"/>
<choice value="25" text="25 Hz"/>
<choice value="50" text="50 Hz"/>
<choice value="60" text="60 Hz"/>
</input>
</inputSet>
<inputSet id="fee">
<input type="freetext" presence="onTagMatch" category="Restrictions" description="The charge/cost of using this amenity" name="Fee" key="fee"/>
</inputSet>
<inputSet id="roadRestrictions">
<input type="choice" presence="always"
name="Oneway" category="Restrictions" description="Oneway roads"
key="oneway">
<choice value="yes" match="yes|true|1" text="One way"
description="Road can only be travelled in direction of way" icon="features/oneway__yes.png"/>
<choice value="no" match="no|false|0" text="Two way"
description="Road can be travelled in both directions" icon="features/oneway__no.png"/>
<choice value="-1" match="-1|reverse" text="One way reverse"
description="Road can be travelled in opposite direction to way" icon="features/oneway__-1.png"/>
</input>
<input type="speed" presence="onTagMatch"
name="Speed Limit" category="Restrictions" description="Maximum permitted speed on this road"
key="maxspeed"/>
</inputSet>
<inputSet id="roadRoundabout">
<!-- review the choice of category -->
<input type="choice" presence="onTagMatch" name="Roundabout" category="Restrictions" description="Whether this road is a roundabout. Make the way face the direction appropriate for the country."
key="junction">
<choice value="roundabout" text="Yes"/>
</input>
</inputSet>
<inputSet id="turnRestrictions">
<input type="turn" name="Turn restriction" description="Turn restriction" category="Restrictions" priority="normal" presence="onTagMatch">
<match k="type" v="restriction"/>
<role role="via"/>
</input>
</inputSet>
<inputSet id="trafficSignals">
<!-- can't add as standalone feature due to current limitations, would conflict with junction node feature -->
<input type="choice" key="highway" name="Traffic signals" description="Intersection controlled by traffic lights" category="Restrictions" presence="always">
<choice value="traffic_signals" text="Yes"/>
</input>
</inputSet>
<inputSet id="pedestrians">
<input type="choice" name="Pedestrians permitted" description="Can pedestrians use this road, including footpaths if any?" category="Walk" key="foot">
<choice value="yes" text="Allowed"/>
<choice value="no" text="Prohibited"/>
<choice value="designated" text="Designated"/>
</input>
<input type="choice" presence="onTagMatch"
name="Sidewalks" category="Walk" description="Whether there is a sidewalk at the side of the street"
key="sidewalk" layout="horizontal">
<choice value="both" text="Both" description="There is a sidewalk on both sides of the road." match="yes"/>
<choice value="left" text="Left" description="The sidewalk is on the left of the direction of the road."/>
<choice value="right" text="Right" description="The sidewalk is on the right of the direction of the road."/>
<choice value="separate" text="Separate" description="The sidewalk has been mapped as a separate line parallel."/>
<choice value="none" text="None" description="There is no sidewalk on this section of road." match="no"/>
</input>
<input type="route" name="National Walking Route" description="National walking route" category="Walk" priority="low">
<match k="type" v="route"/>
<match k="route" v="hiking|foot"/>
<match k="network" v="nwn"/>
<icon image="features/route__nwn.png" background="red" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
<input type="route" name="Regional Walking Route" description="Regional walking route" category="Walk" priority="low">
<match k="type" v="route"/>
<match k="route" v="hiking|foot"/>
<match k="network" v="rwn"/>
<icon image="features/route__rwn.png" background="cyan" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
<input type="route" name="Local Walking Route" description="Local walking route" category="Walk" priority="lowest">
<match k="type" v="route"/>
<match k="route" v="hiking|foot"/>
<match k="network" v="lwn"/>
<icon image="features/route__lwn.png" background="blue" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
</inputSet>
<inputSet id="naptan">
<input type="freetext" presence="onTagMatch" category="Transport" description="12 character internal Naptan ID" name="Atco Code" key="naptan:AtcoCode"/>
<input type="choice" presence="onTagMatch" category="Transport" description="The eight-point compass bearning" name="Naptan Bearing" key="naptan:Bearing" >
<choice value="N" match="N" text="N" description=""/>
<choice value="NE" match="NE" text="NE" description=""/>
<choice value="E" match="E" text="E" description=""/>
<choice value="SE" match="SE" text="SE" description=""/>
<choice value="S" match="S" text="S" description=""/>
<choice value="SW" match="SW" text="SW" description=""/>
<choice value="W" match="W" text="W" description=""/>
<choice value="NW" match="NW" text="NW" description=""/>
</input>
<input type="freetext" presence="onTagMatch" category="Transport" description="The naptan common name" name="Naptan Common Name (read-only)" key="naptan:CommonName"/>
<input type="freetext" presence="onTagMatch" category="Transport" description="" name="Naptan Indicator (read-only)" key="naptan:Indicator"/>
<input type="freetext" presence="onTagMatch" category="Transport" description="" name="Naptan Street (read-only)" key="naptan:Street"/>
<input type="freetext" presence="onTagMatch" category="Transport" description="Delete this when the details have been verified on-the-ground" name="Naptan Verified?" key="naptan:verified"/>
</inputSet>
<inputSet id="buses">
<input type="freetext" presence="always" category="Transport" name="Stop Name" key="name" description="The name of the bus stop"/>
<input type="freetext" presence="always" category="Transport" name="Local Ref" key="local_ref" description="The local reference of the stop, usually one or two letters above the main flag, used at bus interchanges, e.g. L, BX"/>
<inputSet ref="naptan"/>
</inputSet>
<inputSet id="bus-route">
<input type="route" name="Bus Route" description="Bus route" category="Transport" priority="low" presence="onTagMatch">
<match k="type" v="route"/>
<match k="route" v="bus"/>
<icon image="features/route__bus.png">
<font size="12pt">${operator} <b>${ref}</b></font>
</icon>
</input>
</inputSet>
<inputSet id="tram-route">
<input type="route" name="Tram Route" description="Tram route" category="Transport" priority="low" presence="onTagMatch">
<match k="type" v="route"/>
<match k="route" v="tram"/>
<icon image="features/transport__tram.png">
<font size="12pt">${operator} <b>${ref}</b></font>
</icon>
</input>
</inputSet>
<inputSet id="train-route">
<input type="route" name="Train Route" description="Train route" category="Transport" priority="low" presence="onTagMatch">
<match k="type" v="route"/>
<match k="route" v="train"/>
<icon image="features/transport__railway.png">
<font size="12pt">${name|operator} <b>(${ref})</b></font>
</icon>
</input>
</inputSet>
<inputSet id="cycle">
<inputSet ref="bicycle-permission"/>
<input type="route" name="National Cycle Routes" description="A signposted route in a National Cycle Network, or nearest equivalent." category="Cycle" priority="low">
<match k="type" v="route"/>
<match k="route" v="bicycle"/>
<match k="network" v="ncn"/>
<icon image="features/route__ncn.png" background="#ff6f7a" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
<input type="route" name="Regional Cycle Routes" description="A signposted route in a Regional Cycle Network, or nearest equivalent." category="Cycle" priority="low">
<match k="type" v="route"/>
<match k="route" v="bicycle"/>
<match k="network" v="rcn"/>
<icon image="features/route__rcn.png" background="#6ff7ff" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
<input type="route" name="Local Cycle Routes" description="A signposted route in a Local Cycle Network, or nearest equivalent." category="Cycle" priority="lowest">
<match k="type" v="route"/>
<match k="route" v="bicycle"/>
<match k="network" v="lcn"/>
<icon image="features/route__lcn.png" background="#7d6fff" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
<input type="route" name="Mountain bike route" description="A signposted route for mountain biking." category="Cycle" priority="lowest">
<match k="type" v="route"/>
<match k="route" v="mtb"/>
<icon image="features/route__mtb.png" background="#9f4404" foreground="white">
<font size="14pt"><b>${ref}</b></font><br/>
<font size="12pt">${name}</font>
</icon>
</input>
</inputSet>
<inputSet id="bicycle-permission">
<input type="choice"
name="Bicycles permitted" category="Cycle" description="Are bicyles allowed to use this road (regardless of physical suitability)?"
key="bicycle">
<choice value="yes" text="Allowed"/>
<choice value="no" text="Prohibited" desciption="Cycling is not allowed. Pushing your bicycle is not allowed either."/>
<choice value="private" text="Private"/>
<choice value="dismount" text="Cyclists dismount" description="Signage states that cyclists should dismount and push their bike for the selected path or road."/>
<choice value="designated" text="Designated"/>
<help>http://wiki.openstreetmap.org/wiki/Key:access</help>
</input>
</inputSet>
<inputSet id="bicycle-lane">
<input type="choice"
name="Bike lanes" category="Cycle" description="Road has bike lanes within the road surface"
key="cycleway" layout="horizontal">
<choice value="no" text="No bike lanes"/>
<choice value="lane" text="On-road bike lane" description="Separated by painted line on the road from cars"/>
<choice value="track" text="Parallel track" description="Separated by kerb or parked cars"/>
<choice value="opposite_lane" text="Contraflow lane" description="Separated by painted line, and allowing bicycles in both directions in an otherwise one-way street."/>
<choice value="opposite_track" text="Contraflow track" description="Separated by kerb or parked cars, and allowing bicycles in both directions in an otherwise one-way street."/>
<choice value="opposite" text="Contraflow unmarked" description="The route may be cycled in the direction opposite of other traffic, but does not have a dedicated lane."/>
<help>http://wiki.openstreetmap.org/wiki/Key:cycleway</help>
</input>
</inputSet>
<inputSet id="route">
<inputSet ref="names"/>
<inputSet ref="roadRefs"/>
</inputSet>
<inputSet id="waterways">
<inputSet ref="simpleName"/>
<input type="freetext" presence="always" name="Width" category="Details" key="width" description="The width in metres"/>
<inputSet ref="boatPermissions"/>
</inputSet>
<!-- Would be good to have a dedicated 'access' type of input -->
<!-- all the description fields are identical at the moment for ease of maintenance, should be tailored in future. -->
<inputSet id="boatPermissions">
<input type="choice" name="Boat permission" category="Restrictions" key="boat" description="Are boats allowed to use this waterway?">
<choice value="yes" text="Allowed" description="General right of way."/>
<choice value="no" text="Prohibited" description="No access to the public."/>
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
</input>
</inputSet>
<inputSet id="permissions">
<input type="choice" name="General access" category="Restrictions" key="access" description="Is there a general right of access, regardless of mode of transport?">
<choice value="yes" text="Allowed" description="General right of way."/>
<choice value="no" text="Prohibited" description="No access to the public."/>
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
</input>
<input type="choice" name="Motor vehicles" category="Restrictions" key="motor_vehicle" description="Are cars and other private vehicles allowed?">
<choice value="yes" text="Allowed" description="General right of way."/>
<choice value="no" text="Prohibited" description="No access to the public."/>
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
</input>
<inputSet ref="hores-permission" />
</inputSet>
<inputSet id="horse-permission">
<input type="choice" name="Horses" category="Restrictions" key="horse" description="Are horses allowed?">
<choice value="yes" text="Allowed" description="General right of way."/>
<choice value="no" text="Prohibited" description="No access to the public."/>
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
</input>
</inputSet>
<inputSet id="places">
<input type="choice" name="Type of Place" presence="always" key="place">
<choice value="locality" text="Locality"/>
<choice value="hamlet" text="Hamlet"/>
<choice value="village" text="Village"/>
<choice value="suburb" text="Suburb"/>
<choice value="town" text="Town"/>
<choice value="city" text="City"/>
<choice value="county" text="County"/>
<choice value="region" text="Region"/>
<choice value="state" text="State"/>
<choice value="country" text="Country"/>
<choice value="continent" text="Continent"/>
<choice value="island" text="Island"/>
<choice value="islet" text="Islet"/>
</input>
</inputSet>
<inputSet id="isBuilding">
<input type="choice" presence="withCategory" category="Details" name="Building type, if it is one" key="building">
<choice value="yes" text="Generic building"/>
<choice value="residential" text="Generic residential"/>
<choice value="apartments" text="Big apartments house"/>
<choice value="terrace" text="Terraced house"/>
<choice value="house" text="Family house"/>
<choice value="hut" text="Small hut"/>
<choice value="garage" text="A garage"/>
<choice value="garages" text="Block of garages"/>
<choice value="office" text="Office building"/>
<choice value="public" text="Public building"/>
<choice value="industrial" text="Generic industrial"/>
<choice value="manufacture" text="Manufacture"/>
<choice value="warehouse" text="Warehouse"/>
<choice value="hangar" text="Hangar"/>
<choice value="storage_tank" text="Fluids storage tank"/>
<choice value="retail" text="Retail"/>
<choice value="supermarket" text="Supermarket"/>
<choice value="train_station" text="Train station"/>
<choice value="church" text="Church"/>
<choice value="school" text="School"/>
<choice value="bunker" text="Military bunker"/>
<choice value="collapsed" text="Collapsed building"/>
<choice value="roof" text="Just a roof"/>
<help>http://wiki.openstreetmap.org/wiki/Key:building</help>
</input>
</inputSet>
<inputSet id="powerCables">
<input type="choice" name="Cables" key="cables" presence="always" category="Details">
<choice value="2" text="2"/>
<choice value="3" text="3"/>
<choice value="4" text="4"/>
<choice value="6" text="6"/>
<choice value="8" text="8"/>
<choice value="10" text="10"/>
<choice value="12" text="12"/>
</input>
<input type="choice" name="Voltage" key="voltage" presence="always" category="Details">
<!-- choices based on http://osmdoc.com/en/tag/voltage/#values-->
<choice value="400" text="400 V"/>
<choice value="600" text="600 V"/>
<choice value="750" text="750 V"/>
<choice value="1500" text="1500 V"/>
<choice value="3000" text="3000 V"/>
<choice value="15000" text="15 kV"/>
<choice value="20000" text="20 kV"/>
<choice value="35000" text="35 kV"/>
<choice value="110000" text="110 kV"/>
<choice value="132000" text="132 kV"/>
<choice value="138000" text="138 kV"/>
<choice value="220000" text="220 kV"/>
<choice value="380000" text="380 kV"/>
<choice value="500000" text="500 kV"/>
</input>
</inputSet>
<inputSet id="pitchSport">
<input name="Sport" presence="always" category="Details" key="sport" type="choice" description="The sport that is predominantly played here.">
<choice value="9pin" text="9 pin bowling"/>
<choice value="10pin" text="10 pin bowling"/>
<choice value="american_football" text="American football"/>
<choice value="archery" text="Archery"/>
<choice value="athletics" text="Athletics"/>
<choice value="australian_football" text="Australian Rules Football"/>
<choice value="baseball" text="Baseball"/>
<choice value="basketball" text="Basketball"/>
<choice value="beachvolleyball" text="Beach volleyball"/>
<choice value="boules" text="Boules/petanque/bocci"/>
<choice value="bowls" text="Lawn bowls"/>
<choice value="canadian_football" text="Canadian football"/>
<choice value="chess" text="Chess"/>
<choice value="cricket" text="Cricket"/>
<choice value="cricket_nets" text="Cricket nets"/>
<choice value="croquet" text="Croquet"/>
<choice value="equestrian" text="Equestrian"/>
<choice value="gaelic_football" text="Gaelic football"/>
<choice value="gymnastics" text="Gymnastics"/>
<choice value="team_handball" text="(Team) handball"/>
<choice value="hockey" text="(Field) hockey"/>
<choice value="korfball" text="Korball"/>
<choice value="pelota" text="Pelota"/>
<choice value="rugby_league" text="Rugby league"/>
<choice value="rugby_union" text="Rugby union"/>
<choice value="shooting" text="Shooting"/>
<choice value="skating" text="Ice skating"/>
<choice value="skateboard" text="Skateboarding"/>
<choice value="soccer" text="Soccer/football"/>
<choice value="swimming" text="Swimming"/>
<choice value="table_tennis" text="Table tennis"/>
<choice value="tennis" text="Tennis"/>
<choice value="volleyball" text="Volleyball"/>
</input>
</inputSet>
<inputSet id="wheelchair-basic">
<input type="choice" name="Wheelchair" key="wheelchair" presence="onTagMatch" category="Details">
<choice value="yes" text="Yes: ramps/elevators/etc" />
<choice value="no" text="No: inaccessible to wheelchairs" />
<choice value="limited" text="Limited accessibility" />
</input>
</inputSet>
<!-- Features -->
<include file="map_features/roads.xml" /> <!-- includes traffic calming, roundabouts, race track -->
<include file="map_features/paths.xml" /> <!-- includes steps, highway=pedestrian, bike/hike routes -->
<include file="map_features/water.xml" /> <!-- includes coastline -->
<include file="map_features/transport.xml" /> <!-- includes rail, trams, bus, airports, car rental, bike parking... -->
<include file="map_features/power.xml" /> <!-- includes power lines, pylons, stations -->
<include file="map_features/places.xml" /> <!-- includes boundaries (inc. NP) -->
<include file="map_features/tourism.xml" /> <!-- includes accommodation -->
<include file="map_features/barriers.xml" />
<include file="map_features/shopping.xml" /> <!-- no clear distinction between this, amenities and buildings -->
<include file="map_features/amenities.xml" /> <!-- includes cafe, bar... -->
<include file="map_features/landuse.xml" /> <!-- includes outdoor leisure/sporting stuff, agriculture, natural. -->
<include file="map_features/man_made.xml" />
<include file="map_features/buildings.xml" /> <!-- a bit of a dumping ground, includes sports centre, shopping centre, hospital... -->
<!-- Relations -->
<feature name="Multipolygon">
<category>advanced</category>
<relation/>
<tag k="type" v="multipolygon"/>
<inputSet ref="common"/>
</feature>
</mapFeatures>
+3
View File
@@ -0,0 +1,3 @@
[
"Cafe", "Restaurant", "Bus Station", "Hospital", "Bar", "Place of Worship"
]
+60
View File
@@ -0,0 +1,60 @@
[
{
"title": "Highway",
"name": "highway",
"match": {
"type": ["line"],
"tags": {
"highway": "*"
}
},
"form": [
{
"tag": "highway",
"title": "Highway Type",
"type": "select",
"option": ["primary", "secondary", "tertiary"]
}
]
},
{
"title": "Cafe",
"name": "cafe",
"match": {
"type": ["node", "area"],
"tags": {
"amenity": "cafe"
}
},
"form": [
{
"tag": "phone",
"type": "tel"
},
{
"tag": "fax",
"type": "tel"
},
{
"tag": "website",
"type": "url"
},
{
"tag": "email",
"type": "email"
},
{
"tag": "internet_access",
"title": "Internet Access",
"type": "select",
"option": ["yes", "wlan","wired","terminal","no"]
},
{
"tag": "internet_access:fee",
"title": "Internet Access Fee",
"type": "select",
"option": ["yes", "no"]
}
]
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -34,6 +34,7 @@
<script src='../js/id/util.js'></script>
<script src='../js/id/oauth.js'></script>
<script src='../js/id/services/taginfo.js'></script>
<script src='../js/id/presetdata.js'></script>
<script src='../data/data.js'></script>
<script src='../data/deprecated.js'></script>
@@ -70,6 +71,9 @@
<script src='../js/id/ui/lasso.js'></script>
<script src='../js/id/ui/flash.js'></script>
<script src='../js/id/ui/confirm.js'></script>
<script src='../js/id/ui/preset.js'></script>
<script src='../js/id/ui/presetfavs.js'></script>
<script src='../js/id/ui/presetsearch.js'></script>
<script src='../js/id/ui/splash.js'></script>
<script src='../js/id/ui/source_switch.js'></script>
<script src='../js/id/ui/toggle.js'></script>
+4 -4
View File
@@ -43,14 +43,14 @@ describe("iD.ui.Inspector", function () {
element.remove();
entity = entity.update({tags: {}});
render();
expect(element.selectAll("input.value").property('value')).to.be.empty;
expect(element.selectAll("input.key").property('value')).to.be.empty;
expect(element.select('.tag-list').selectAll("input.value").property('value')).to.be.empty;
expect(element.select('.tag-list').selectAll("input.key").property('value')).to.be.empty;
});
it("adds tags when clicking the add button", function () {
element.selectAll("button.add-tag").trigger('click');
expect(element.selectAll("input")[0][2].value).to.be.empty;
expect(element.selectAll("input")[0][3].value).to.be.empty;
expect(element.select('.tag-list').selectAll("input")[0][2].value).to.be.empty;
expect(element.select('.tag-list').selectAll("input")[0][3].value).to.be.empty;
});
it("removes tags when clicking the remove button", function () {