diff --git a/js/id/services/taginfo.js b/js/id/services/taginfo.js index 6aaf81e97..0af481616 100644 --- a/js/id/services/taginfo.js +++ b/js/id/services/taginfo.js @@ -34,14 +34,25 @@ iD.services.taginfo = function() { return _.omit(parameters, 'geometry', 'debounce'); } - function popularKeys(parameters) { + function filterKeys(parameters) { var pop_field = 'count_all'; if (parameters.filter) pop_field = 'count_' + parameters.filter; - return function(d) { return parseFloat(d[pop_field]) > 2500 || d.in_wiki; }; + return function(d) { + return parseFloat(d[pop_field]) > 2500 || d.in_wiki; + }; } - function popularValues() { - return function(d) { return parseFloat(d.fraction) > 0.0 || d.in_wiki; }; + function filterMultikeys() { + return function(d) { + return (d.key.match(/:/g) || []).length === 1; // exactly one ':' + }; + } + + function filterValues() { + return function(d) { + return d.value.match(/;/g) === null && // exclude values with ';' + (parseFloat(d.fraction) > 0.0 || d.in_wiki); + }; } function valKey(d) { @@ -95,7 +106,22 @@ iD.services.taginfo = function() { page: 1 }, parameters)), debounce, function(err, d) { if (err) return callback(err); - callback(null, d.data.filter(popularKeys(parameters)).sort(sortKeys).map(valKey)); + callback(null, d.data.filter(filterKeys(parameters)).sort(sortKeys).map(valKey)); + }); + }; + + taginfo.multikeys = function(parameters, callback) { + var debounce = parameters.debounce; + parameters = clean(setSort(parameters)); + request(endpoint + 'keys/all?' + + iD.util.qsString(_.extend({ + rp: 25, + sortname: 'count_all', + sortorder: 'desc', + page: 1 + }, parameters)), debounce, function(err, d) { + if (err) return callback(err); + callback(null, d.data.filter(filterMultikeys(parameters)).map(valKey)); }); }; @@ -110,7 +136,7 @@ iD.services.taginfo = function() { page: 1 }, parameters)), debounce, function(err, d) { if (err) return callback(err); - callback(null, d.data.filter(popularValues()).map(valKeyDescription), parameters); + callback(null, d.data.filter(filterValues()).map(valKeyDescription), parameters); }); }; diff --git a/js/id/ui/preset/multiselect.js b/js/id/ui/preset/multiselect.js index 400e5efa2..c0d5db19f 100644 --- a/js/id/ui/preset/multiselect.js +++ b/js/id/ui/preset/multiselect.js @@ -86,7 +86,7 @@ iD.ui.preset.multiselect = function(field, context) { dispatch.init(); isInitialized = true; } else if (context.taginfo()) { - context.taginfo().keys({query: field.key}, function(err, data) { + context.taginfo().multikeys({query: field.key}, function(err, data) { if (!err) { strings = data.map(function(k) { var d = k.value.replace(field.key, ''); diff --git a/test/spec/services/taginfo.js b/test/spec/services/taginfo.js index 76929aaee..b595e13c7 100644 --- a/test/spec/services/taginfo.js +++ b/test/spec/services/taginfo.js @@ -44,7 +44,6 @@ describe("iD.services.taginfo", function() { it("filters only popular keys with an entity type filter", function() { var callback = sinon.spy(); - taginfo.keys({query: "amen", filter: "nodes"}, callback); server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/keys/all"), @@ -58,7 +57,6 @@ describe("iD.services.taginfo", function() { it("sorts keys with ':' below keys without ':'", function() { var callback = sinon.spy(); - taginfo.keys({query: "ref"}, callback); server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/keys/all"), @@ -71,10 +69,38 @@ describe("iD.services.taginfo", function() { }); }); + describe("#multikeys", function() { + it("calls the given callback with the results of the multikeys query", function() { + var callback = sinon.spy(); + taginfo.multikeys({query: "recycling:"}, callback); + + server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/keys/all"), + [200, { "Content-Type": "application/json" }, + '{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0}]}']); + server.respond(); + + expect(query(server.requests[0].url)).to.eql( + {query: "recycling:", page: "1", rp: "25", sortname: "count_all", sortorder: "desc"}); + expect(callback).to.have.been.calledWith(null, [{"title":"recycling:glass", "value":"recycling:glass"}]); + }); + + it("excludes multikeys with extra colons", function() { + var callback = sinon.spy(); + taginfo.multikeys({query: "recycling:"}, callback); + + server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/keys/all"), + [200, { "Content-Type": "application/json" }, + '{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0},\ + {"count_all":22,"key":"recycling:glass:color","count_all_fraction":0.0}]}']); + server.respond(); + + expect(callback).to.have.been.calledWith(null, [{"title":"recycling:glass", "value":"recycling:glass"}]); + }); + }); + describe("#values", function() { it("calls the given callback with the results of the values query", function() { var callback = sinon.spy(); - taginfo.values({key: "amenity", query: "par"}, callback); server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/key/values"), @@ -89,13 +115,25 @@ describe("iD.services.taginfo", function() { it("filters popular values", function() { var callback = sinon.spy(); - taginfo.values({key: "amenity", query: "par"}, callback); server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/key/values"), [200, { "Content-Type": "application/json" }, '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},\ - {"value":"party","description":"A place for partying", "fraction":0.0}]}']); + {"value":"party","description":"A place for partying", "fraction":0.0}]}']); + server.respond(); + + expect(callback).to.have.been.calledWith(null, [{"value":"parking","title":"A place for parking cars"}]); + }); + + it("excludes values with semicolons", function() { + var callback = sinon.spy(); + taginfo.values({key: "amenity", query: "par"}, callback); + + server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/key/values"), + [200, { "Content-Type": "application/json" }, + '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},\ + {"value":"parking;partying","description":"A place for parking cars *and* partying", "fraction":1.0}]}']); server.respond(); expect(callback).to.have.been.calledWith(null, [{"value":"parking","title":"A place for parking cars"}]); @@ -105,7 +143,6 @@ describe("iD.services.taginfo", function() { describe("#docs", function() { it("calls the given callback with the results of the docs query", function() { var callback = sinon.spy(); - taginfo.docs({key: "amenity", value: "parking"}, callback); server.respondWith("GET", new RegExp("https://taginfo.openstreetmap.org/api/4/tag/wiki_page"),