From aba961e130089b8892d3629af51005b7f7105282 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Thu, 11 May 2017 13:45:48 +0200 Subject: [PATCH] Reformat code using Prettier --- data/get_preset_translations.js | 37 +- js/GeoJsonNoVanish.js | 46 +- js/OSM4Leaflet.js | 42 +- js/autorepair.js | 216 ++- js/configs.js | 8 +- js/ffs.js | 677 +++---- js/ffs/free.js | 156 +- js/i18n.js | 126 +- js/ide.js | 2810 +++++++++++++++++++----------- js/index.js | 44 +- js/jsmapcss/Condition.js | 73 +- js/jsmapcss/Rule.js | 91 +- js/jsmapcss/RuleChain.js | 93 +- js/jsmapcss/RuleSet.js | 943 +++++----- js/jsmapcss/Style.js | 536 +++--- js/jsmapcss/StyleChooser.js | 165 +- js/jsmapcss/StyleList.js | 137 +- js/jsmapcss/index.js | 16 +- js/map.js | 244 ++- js/misc.js | 257 ++- js/nominatim.js | 75 +- js/overpass.js | 1500 ++++++++++------ js/promise-polyfill.js | 2 +- js/query.js | 39 +- js/settings.js | 248 +-- js/shortcuts.js | 176 +- js/urlParameters.js | 67 +- karma.conf.js | 21 +- locales/update_locales.js | 106 +- tests/test.autorepair.josm.js | 265 +-- tests/test.autorepair.recurse.js | 111 +- tests/test.ffs.js | 596 +++---- tests/test.mapcss.eval.js | 248 +-- tests/test.permalink.js | 51 +- tests/test.query.js | 143 +- tests/test.urlParameters.js | 38 +- webpack.config.js | 106 +- 37 files changed, 6088 insertions(+), 4421 deletions(-) diff --git a/data/get_preset_translations.js b/data/get_preset_translations.js index fad9cc98..6ea75ae1 100644 --- a/data/get_preset_translations.js +++ b/data/get_preset_translations.js @@ -1,22 +1,29 @@ /* Downloads preset translations from iD editor github repo */ -var request = require('request'), - fs = require('fs'); +var request = require("request"), fs = require("fs"); -var api = 'https://github.com/openstreetmap/iD/raw/master/dist/locales/'; -var outdir = './data/'; -var localesdir = './locales/'; +var api = "https://github.com/openstreetmap/iD/raw/master/dist/locales/"; +var outdir = "./data/"; +var localesdir = "./locales/"; -var locales = fs.readdirSync(localesdir).filter(function(filename) { return filename.match(/\.json$/); }); +var locales = fs.readdirSync(localesdir).filter(function(filename) { + return filename.match(/\.json$/); +}); locales.forEach(function(locale) { - locale = locale.replace('.json', ''); - request(api + locale + '.json', function(err, resp, body) { - console.log(locale); - if (err) return; - try { var data = JSON.parse(body); } catch (e) { return; } - data = data.presets.presets; - fs.writeFileSync(outdir + 'iD_presets_' + locale + '.json', JSON.stringify(data, null, 2)); - }); + locale = locale.replace(".json", ""); + request(api + locale + ".json", function(err, resp, body) { + console.log(locale); + if (err) return; + try { + var data = JSON.parse(body); + } catch (e) { + return; + } + data = data.presets.presets; + fs.writeFileSync( + outdir + "iD_presets_" + locale + ".json", + JSON.stringify(data, null, 2) + ); + }); }); - diff --git a/js/GeoJsonNoVanish.js b/js/GeoJsonNoVanish.js index 4fdd9a6a..40b99c29 100644 --- a/js/GeoJsonNoVanish.js +++ b/js/GeoJsonNoVanish.js @@ -1,11 +1,11 @@ -import L from 'leaflet'; +import L from "leaflet"; L.GeoJsonNoVanish = L.GeoJSON.extend({ - initialize: function (geojson, options) { + initialize: function(geojson, options) { this.options = { threshold: 10 }; - L.GeoJSON.prototype.initialize.call(this,geojson,options); + L.GeoJSON.prototype.initialize.call(this, geojson, options); }, onAdd: function(map) { this._map = map; @@ -20,20 +20,20 @@ L.GeoJsonNoVanish = L.GeoJSON.extend({ this.eachLayer(map.removeLayer, map); this._map = null; }, - _onZoomEnd: function() { // todo: name + _onZoomEnd: function() { + // todo: name // todo: possible optimizations: zoomOut = skip already compressed objects (and vice versa) var is_max_zoom = this._map.getZoom() == this._map.getMaxZoom(); this.eachLayer(function(o) { - if (!o.feature || !o.feature.geometry) - return; // skip invalid layers - if (o.feature.geometry.type == "Point" && !o.obj) - return; // skip node features + if (!o.feature || !o.feature.geometry) return; // skip invalid layers + if (o.feature.geometry.type == "Point" && !o.obj) return; // skip node features var crs = this._map.options.crs; - if (o.obj) { // already compressed feature + if (o.obj) { + // already compressed feature var bounds = o.obj.getBounds(); var p1 = crs.latLngToPoint(bounds.getSouthWest(), o._map.getZoom()); var p2 = crs.latLngToPoint(bounds.getNorthEast(), o._map.getZoom()); - var d = Math.sqrt(Math.pow(p1.x-p2.x,2)+Math.pow(p1.y-p2.y,2)); + var d = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); if (d > this.options.threshold || is_max_zoom) { delete o.obj.placeholder; this.addLayer(o.obj); @@ -41,26 +41,22 @@ L.GeoJsonNoVanish = L.GeoJSON.extend({ } return; } - if (is_max_zoom) - return; // do not compress objects at max zoom - if (this.options.compress && - !this.options.compress(o.feature)) - return; + if (is_max_zoom) return; // do not compress objects at max zoom + if (this.options.compress && !this.options.compress(o.feature)) return; var bounds = o.getBounds(); var p1 = crs.latLngToPoint(bounds.getSouthWest(), o._map.getZoom()); var p2 = crs.latLngToPoint(bounds.getNorthEast(), o._map.getZoom()); - var d = Math.sqrt(Math.pow(p1.x-p2.x,2)+Math.pow(p1.y-p2.y,2)); - if (d > this.options.threshold) - return; + var d = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + if (d > this.options.threshold) return; /*var c = this.options.pointToLayer ? this.options.pointToLayer(o.feature, bounds.getCenter()) : new L.Marker(bounds.getCenter());*/ var center = bounds.getCenter(); - var f = L.extend({},o.feature); + var f = L.extend({}, o.feature); f.is_placeholder = true; f.geometry = { - "type": "Point", - "coordinates": [center.lng, center.lat], + type: "Point", + coordinates: [center.lng, center.lat] }; var c = L.GeoJSON.geometryToLayer(f, this.options.pointToLayer); o.placeholder = c; @@ -68,15 +64,15 @@ L.GeoJsonNoVanish = L.GeoJSON.extend({ this.resetStyle(c); c.obj = o; //c.addEventListener("click dblclick mousedown mouseover mouseout contextmenu", function(e) { - c.on("click",function(e) { - this.obj.fireEvent(e.type,e); + c.on("click", function(e) { + this.obj.fireEvent(e.type, e); }); this.addLayer(c); this.removeLayer(o); - },this); + }, this); } }); -L.geoJsonNoVanish = function (geojson, options) { +L.geoJsonNoVanish = function(geojson, options) { return new L.GeoJsonNoVanish(geojson, options); }; diff --git a/js/OSM4Leaflet.js b/js/OSM4Leaflet.js index 9650d366..5671fc4e 100644 --- a/js/OSM4Leaflet.js +++ b/js/OSM4Leaflet.js @@ -1,36 +1,36 @@ -import L from 'leaflet'; -import osmtogeojson from 'osmtogeojson'; +import L from "leaflet"; +import osmtogeojson from "osmtogeojson"; L.OSM4Leaflet = L.Class.extend({ - initialize: function (data, options) { + initialize: function(data, options) { this.options = { data_mode: "xml", baseLayerClass: L.GeoJSON, baseLayerOptions: {} }; - L.Util.setOptions(this,options); + L.Util.setOptions(this, options); - this._baseLayer = new this.options.baseLayerClass(null, this.options.baseLayerOptions); + this._baseLayer = new this.options.baseLayerClass( + null, + this.options.baseLayerOptions + ); this._resultData = null; // if data - if (data) - this.addData(data); + if (data) this.addData(data); }, addData: function(data, onDone) { var obj = this; -setTimeout(function(){ - // 1. convert to GeoJSON - var geojson = osmtogeojson(data, {flatProperties: false}); - obj._resultData = geojson; - if (obj.options.afterParse) - obj.options.afterParse(); -setTimeout(function(){ - // 2. add to baseLayer - obj._baseLayer.addData(geojson); - if (onDone) - onDone(); -},1); //end setTimeout -},1); //end setTimeout + setTimeout(function() { + // 1. convert to GeoJSON + var geojson = osmtogeojson(data, {flatProperties: false}); + obj._resultData = geojson; + if (obj.options.afterParse) obj.options.afterParse(); + setTimeout(function() { + // 2. add to baseLayer + obj._baseLayer.addData(geojson); + if (onDone) onDone(); + }, 1); //end setTimeout + }, 1); //end setTimeout }, getGeoJSON: function() { return this._resultData; @@ -46,7 +46,7 @@ setTimeout(function(){ } }); -L.osm4Leaflet = function (data, options) { +L.osm4Leaflet = function(data, options) { return new L.OSM4Leaflet(data, options); }; diff --git a/js/autorepair.js b/js/autorepair.js index 16f5be39..803244e3 100644 --- a/js/autorepair.js +++ b/js/autorepair.js @@ -1,7 +1,7 @@ // query autorepair module -import $ from 'jquery'; +import $ from "jquery"; -import {Base64} from './misc'; +import {Base64} from "./misc"; export default function autorepair(q, lng) { var repair = {}; @@ -13,22 +13,23 @@ export default function autorepair(q, lng) { // (we do not want to autorepair stuff which is commented out.) if (lng == "xml") { var cs = q.match(//g) || []; - for (var i=0; i"; //todo: use some kind of checksum or hash maybe? - q = q.replace(cs[i],placeholder); + for (var i = 0; i < cs.length; i++) { + var placeholder = + ""; //todo: use some kind of checksum or hash maybe? + q = q.replace(cs[i], placeholder); comments[placeholder] = cs[i]; } } else { var cs = q.match(/\/\*[\s\S]*?\*\//g) || []; // multiline comments: /*...*/ - for (var i=0; i|<\/print>))/g) || []; - for (var i=0;i\n"+ws+"\n"+ws+" \n"+ws+" \n'+ws+"\n"+ws+""+i+""); + q = q.replace( + prints[i], + "\n" + + ws + + "\n" + + ws + + "\n" + + ws + + " \n" + + ws + + " \n' + + ws + + "\n" + + ws + + "" + + i + + "" + ); } - for (var i=0;i"+i+"", prints[i]); + for (var i = 0; i < prints.length; i++) + q = q.replace("" + i + "", prints[i]); } else { var outs = q.match(/(\n?[^\S\n]*(\.[^.;]+)?out[^:;"\]]*;)/g) || []; - for (var i=0;i;)->."+from[1]+";"; - else - add = "(._;>;);"; - q = q.replace(outs[i],ws+"/*added by auto repair*/"+ws+add+ws+"/*end of auto repair*/"+i+""); + add = "(." + from[1] + ";." + from[1] + " >;)->." + from[1] + ";"; + else add = "(._;>;);"; + q = q.replace( + outs[i], + ws + + "/*added by auto repair*/" + + ws + + add + + ws + + "/*end of auto repair*/" + + i + + "" + ); } - for (var i=0;i"+i+"", outs[i]); + for (var i = 0; i < outs.length; i++) + q = q.replace("" + i + "", outs[i]); } return true; - } + }; repair.editors = function() { if (lng == "xml") { // 1. fix ]*)>/); if (src) { - var output = $("osm-script",$.parseXML(src[0]+"")).attr("output"); + var output = $("osm-script", $.parseXML(src[0] + "")).attr( + "output" + ); if (output && output != "xml") { - var new_src = src[0].replace(output,"xml"); - q = q.replace(src[0],new_src+""); + var new_src = src[0].replace(output, "xml"); + q = q.replace(src[0], new_src + ""); } } // 2. fix |<\/print>))/g) || []; - for (var i=0;i'; + add = + ''; } repaired = true; } if (repaired) { - new_print = add+(new XMLSerializer()).serializeToString(print[0]); + new_print = add + new XMLSerializer().serializeToString(print[0]); new_print += ""; - q = q.replace(prints[i],new_print); + q = q.replace(prints[i], new_print); } } } else { // 1. fix [out:*] var out = q.match(/\[\s*out\s*:\s*([^\]\s]+)\s*\]\s*;?/); - ///^\s*\[\s*out\s*:\s*([^\]\s]+)/); + ///^\s*\[\s*out\s*:\s*([^\]\s]+)/); if (out && out[1] != "xml") - q = q.replace(/(\[\s*out\s*:\s*)([^\]\s]+)(\s*\]\s*;?)/,"$1xml$3/*fixed by auto repair*/"); + q = q.replace( + /(\[\s*out\s*:\s*)([^\]\s]+)(\s*\]\s*;?)/, + "$1xml$3/*fixed by auto repair*/" + ); // 2. fix print statements: non meta output, overpass geometries var prints = q.match(/(\.([^;.]+?)\s+)?(out[^:;"\]]*;)/g) || []; - for (var i=0;i;)->."+out_set+"; "+new_print; + new_print = + "(." + + out_set + + ";." + + out_set + + " >;)->." + + out_set + + "; " + + new_print; } else { - new_print = "(._;>;); "+new_print; + new_print = "(._;>;); " + new_print; } } if (new_print != print) - q = q.replace(print,new_print+"/*fixed by auto repair*/"); + q = q.replace(print, new_print + "/*fixed by auto repair*/"); } } return true; - } + }; return repair; -}; - +} autorepair.detect = {}; autorepair.detect.editors = function(q, lng) { // todo: test this // todo: move into autorepair "module" /// todo. done? - q = q.replace(/{{.*?}}/g,""); + q = q.replace(/{{.*?}}/g, ""); var err = {}; if (lng == "xml") { try { - var xml = $.parseXML(""+q+""); - var out = $("osm-script",xml).attr("output"); - if (out !== undefined && out !== "xml") - err.output = true; - $("print",xml).each(function(i,p) { if($(p).attr("mode")!=="meta") err.meta=true; }); - $("print",xml).each(function(i,p) { if($(p).attr("geometry").match(/(center|bounds|full)/)) err.geometry=true; }); - } catch(e) {} // ignore xml syntax errors ?! + var xml = $.parseXML("" + q + ""); + var out = $("osm-script", xml).attr("output"); + if (out !== undefined && out !== "xml") err.output = true; + $("print", xml).each(function(i, p) { + if ($(p).attr("mode") !== "meta") err.meta = true; + }); + $("print", xml).each(function(i, p) { + if ($(p).attr("geometry").match(/(center|bounds|full)/)) + err.geometry = true; + }); + } catch (e) {} // ignore xml syntax errors ?! } else { // ignore comments - q=q.replace(/\/\*[\s\S]*?\*\//g,""); - q=q.replace(/\/\/[^\n]*/g,""); + q = q.replace(/\/\*[\s\S]*?\*\//g, ""); + q = q.replace(/\/\/[^\n]*/g, ""); var out = q.match(/\[\s*out\s*:\s*([^\]\s]+)\s*\]/); - if (out && out[1] != "xml") - err.output = true; + if (out && out[1] != "xml") err.output = true; var prints = q.match(/out([^:;]*);/g); - $(prints).each(function(i,p) {if (p.match(/\s(body|skel|ids|tags)/) || !p.match(/meta/)) err.meta=true;}); - $(prints).each(function(i,p) {if (p.match(/\s(center|bb|geom)/)) err.geometry=true;}); + $(prints).each(function(i, p) { + if (p.match(/\s(body|skel|ids|tags)/) || !p.match(/meta/)) + err.meta = true; + }); + $(prints).each(function(i, p) { + if (p.match(/\s(center|bb|geom)/)) err.geometry = true; + }); } return $.isEmptyObject(err); -} +}; diff --git a/js/configs.js b/js/configs.js index a01986b9..9998faa8 100644 --- a/js/configs.js +++ b/js/configs.js @@ -4,12 +4,12 @@ export default { suggestedServers: [ "//overpass-api.de/api/", "http://overpass.osm.rambler.ru/cgi/", - "http://api.openstreetmap.fr/oapi/", + "http://api.openstreetmap.fr/oapi/" ], defaultTiles: "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", tileServerAttribution: "© OpenStreetMap.org contributors Data:ODbL, Map:cc-by-sa", suggestedTiles: [ - "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" //"http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png", //"http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png", //"http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png", @@ -24,5 +24,5 @@ export default { short_url_service: "", html2canvas_use_proxy: false, // api key for osmnames geocoder, go to http://osmnames.org/api/ to get one if you run your own overpass instance - osmnamesApiKey: "gtXyh2mBSaN5zWqqqQRh", -} + osmnamesApiKey: "gtXyh2mBSaN5zWqqqQRh" +}; diff --git a/js/ffs.js b/js/ffs.js index 53eacba9..83416474 100644 --- a/js/ffs.js +++ b/js/ffs.js @@ -1,367 +1,410 @@ // ffs/wizard module -import ffs_free from './ffs/free'; -import ffs_parser from './ffs/ffs.pegjs'; +import ffs_free from "./ffs/free"; +import ffs_parser from "./ffs/ffs.pegjs"; - var ffs = {}; - var freeFormQuery; +var ffs = {}; +var freeFormQuery; - /* this converts a random boolean expression into a normalized form: +/* this converts a random boolean expression into a normalized form: * A∧B∧… ∨ C∧D∧… ∨ … * for example: A∧(B∨C) ⇔ (A∧B)∨(A∧C) */ - function normalize(query) { - var normalized_query = { - logical:"or", - queries:[] - }; - function normalize_recursive(rem_query) { - if (!rem_query.logical) { - return [{ +function normalize(query) { + var normalized_query = { + logical: "or", + queries: [] + }; + function normalize_recursive(rem_query) { + if (!rem_query.logical) { + return [ + { logical: "and", queries: [rem_query] - }]; - } else if (rem_query.logical === "and") { - var c1 = normalize_recursive( rem_query.queries[0] ); - var c2 = normalize_recursive( rem_query.queries[1] ); - // return cross product of c1 and c2 - var c = []; - for (var i=0; i.searchArea;'); - bounds_part = '(area.searchArea)'; + switch (ffs.bounds) { + case "area": + query_parts.push("// fetch area “" + ffs.area + "” to search in"); + query_parts.push("{{geocodeArea:" + ffs.area + "}}->.searchArea;"); + bounds_part = "(area.searchArea)"; break; - case "around": - query_parts.push('// adjust the search radius (in meters) here'); - query_parts.push('{{radius=1000}}'); - bounds_part = '(around:{{radius}},{{geocodeCoords:'+ffs.area+'}})'; + case "around": + query_parts.push("// adjust the search radius (in meters) here"); + query_parts.push("{{radius=1000}}"); + bounds_part = "(around:{{radius}},{{geocodeCoords:" + ffs.area + "}})"; break; - case "bbox": - bounds_part = '({{bbox}})'; + case "bbox": + bounds_part = "({{bbox}})"; break; - case "global": - bounds_part = undefined; + case "global": + bounds_part = undefined; break; - default: - alert("unknown bounds condition: "+ffs.bounds); - return false; + default: + alert("unknown bounds condition: " + ffs.bounds); + return false; break; - } + } - function get_query_clause(condition) { - function esc(str) { - if (typeof str !== "string") return; - // see http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Escaping - return str.replace(/\\/g,"\\\\").replace(/"/g,"\\\"") // need to escape those - .replace(/\t/g,"\\t").replace(/\n/g,"\\n"); // also escape newlines an tabs for better readability of the query - } - var key = esc(condition.key); - var val = esc(condition.val); - // convert substring searches into matching regexp ones - if (condition.query === "substr") { + function get_query_clause(condition) { + function esc(str) { + if (typeof str !== "string") return; + // see http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Escaping + return str + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') // need to escape those + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n"); // also escape newlines an tabs for better readability of the query + } + var key = esc(condition.key); + var val = esc(condition.val); + // convert substring searches into matching regexp ones + if (condition.query === "substr") { + condition.query = "like"; + condition.val = {regex: escRegexp(condition.val)}; + } + // special case for empty values + // see https://github.com/drolbr/Overpass-API/issues/53 + if (val === "") { + if (condition.query === "eq") { condition.query = "like"; - condition.val={regex:escRegexp(condition.val)}; + condition.val = {regex: "^$"}; + } else if (condition.query === "neq") { + condition.query = "notlike"; + condition.val = {regex: "^$"}; } - // special case for empty values - // see https://github.com/drolbr/Overpass-API/issues/53 - if (val === '') { - if (condition.query === "eq") { - condition.query = "like"; - condition.val={regex:'^$'}; - } else if (condition.query === "neq") { - condition.query = "notlike"; - condition.val={regex:'^$'}; - } + } + // special case for empty keys + // see https://github.com/drolbr/Overpass-API/issues/53#issuecomment-26325122 + if (key === "") { + if (condition.query === "key") { + condition.query = "likelike"; + key = "^$"; + condition.val = {regex: ".*"}; + } else if (condition.query === "eq") { + condition.query = "likelike"; + key = "^$"; + condition.val = {regex: "^" + escRegexp(condition.val) + "$"}; + } else if (condition.query === "like") { + condition.query = "likelike"; + key = "^$"; } - // special case for empty keys - // see https://github.com/drolbr/Overpass-API/issues/53#issuecomment-26325122 - if (key === '') { - if (condition.query === "key") { - condition.query = "likelike"; - key='^$'; - condition.val={regex: '.*'}; - } else if (condition.query === "eq") { - condition.query = "likelike"; - key='^$'; - condition.val={regex: '^'+escRegexp(condition.val)+'$'}; - } else if (condition.query === "like") { - condition.query = "likelike"; - key='^$'; + } + // construct the query clause + switch (condition.query) { + case "key": + return '["' + key + '"]'; + case "nokey": + return '["' + key + '"!~".*"]'; + case "eq": + return '["' + key + '"="' + val + '"]'; + case "neq": + return '["' + key + '"!="' + val + '"]'; + case "like": + return ( + '["' + + key + + '"~"' + + esc(condition.val.regex) + + '"' + + (condition.val.modifier === "i" ? ",i" : "") + + "]" + ); + case "likelike": + return ( + '[~"' + + key + + '"~"' + + esc(condition.val.regex) + + '"' + + (condition.val.modifier === "i" ? ",i" : "") + + "]" + ); + case "notlike": + return ( + '["' + + key + + '"!~"' + + esc(condition.val.regex) + + '"' + + (condition.val.modifier === "i" ? ",i" : "") + + "]" + ); + case "meta": + switch (condition.meta) { + case "id": + return "(" + val + ")"; + case "newer": + if ( + condition.val.match( + /^-?\d+ ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?$/ + ) + ) + return '(newer:"{{date:' + val + '}}")'; + return '(newer:"' + val + '")'; + case "user": + return '(user:"' + val + '")'; + case "uid": + return "(uid:" + val + ")"; + default: + console.log("unknown query type: meta/" + condition.meta); + return false; } - } - // construct the query clause - switch(condition.query) { - case "key": - return '["'+key+'"]'; - case "nokey": - return '["'+key+'"!~".*"]'; - case "eq": - return '["'+key+'"="'+val+'"]'; - case "neq": - return '["'+key+'"!="'+val+'"]'; - case "like": - return '["'+key+'"~"'+esc(condition.val.regex)+'"' - +(condition.val.modifier==="i"?',i':'') - +']'; - case "likelike": - return '[~"'+key+'"~"'+esc(condition.val.regex)+'"' - +(condition.val.modifier==="i"?',i':'') - +']'; - case "notlike": - return '["'+key+'"!~"'+esc(condition.val.regex)+'"' - +(condition.val.modifier==="i"?',i':'') - +']'; - case "meta": - switch(condition.meta) { - case "id": - return '('+val+')'; - case "newer": - if (condition.val.match(/^-?\d+ ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?$/)) - return '(newer:"{{date:'+val+'}}")'; - return '(newer:"'+val+'")'; - case "user": - return '(user:"'+val+'")'; - case "uid": - return '(uid:'+val+')'; - default: - console.log("unknown query type: meta/"+condition.meta); - return false; - } - case "free form": - // own module, special cased below - default: - console.log("unknown query type: "+condition.query); - return false; - } + case "free form": + // own module, special cased below + default: + console.log("unknown query type: " + condition.query); + return false; } - function get_query_clause_str(condition) { - function quotes(s) { - if (s.match(/^[a-zA-Z0-9_]+$/) === null) - return '"'+s.replace(/"/g,'\\"')+'"'; - return s; - } - function quoteRegex(s) { - if (s.regex.match(/^[a-zA-Z0-9_]+$/) === null || s.modifier) - return '/'+s.regex.replace(/\//g,'\\/')+'/'+(s.modifier||''); - return s.regex; - } - switch(condition.query) { - case "key": - return quote_comment_str(quotes(condition.key)+'=*'); - case "nokey": - return quote_comment_str(quotes(condition.key)+'!=*'); - case "eq": - return quote_comment_str(quotes(condition.key)+'='+quotes(condition.val)); - case "neq": - return quote_comment_str(quotes(condition.key)+'!='+quotes(condition.val)); - case "like": - return quote_comment_str(quotes(condition.key)+'~'+quoteRegex(condition.val)); - case "likelike": - return quote_comment_str('~'+quotes(condition.key)+'~'+quoteRegex(condition.val)); - case "notlike": - return quote_comment_str(quotes(condition.key)+'!~'+quoteRegex(condition.val)); - case "substr": - return quote_comment_str(quotes(condition.key)+':'+quotes(condition.val)); - case "meta": - switch(condition.meta) { - case "id": - return quote_comment_str('id:'+quotes(condition.val)); - case "newer": - return quote_comment_str('newer:'+quotes(condition.val)); - case "user": - return quote_comment_str('user:'+quotes(condition.val)); - case "uid": - return quote_comment_str('uid:'+quotes(condition.val)); - default: - return ''; - } - case "free form": - return quote_comment_str(quotes(condition.free)); - default: - return ''; - } + } + function get_query_clause_str(condition) { + function quotes(s) { + if (s.match(/^[a-zA-Z0-9_]+$/) === null) + return '"' + s.replace(/"/g, '\\"') + '"'; + return s; } + function quoteRegex(s) { + if (s.regex.match(/^[a-zA-Z0-9_]+$/) === null || s.modifier) + return "/" + s.regex.replace(/\//g, "\\/") + "/" + (s.modifier || ""); + return s.regex; + } + switch (condition.query) { + case "key": + return quote_comment_str(quotes(condition.key) + "=*"); + case "nokey": + return quote_comment_str(quotes(condition.key) + "!=*"); + case "eq": + return quote_comment_str( + quotes(condition.key) + "=" + quotes(condition.val) + ); + case "neq": + return quote_comment_str( + quotes(condition.key) + "!=" + quotes(condition.val) + ); + case "like": + return quote_comment_str( + quotes(condition.key) + "~" + quoteRegex(condition.val) + ); + case "likelike": + return quote_comment_str( + "~" + quotes(condition.key) + "~" + quoteRegex(condition.val) + ); + case "notlike": + return quote_comment_str( + quotes(condition.key) + "!~" + quoteRegex(condition.val) + ); + case "substr": + return quote_comment_str( + quotes(condition.key) + ":" + quotes(condition.val) + ); + case "meta": + switch (condition.meta) { + case "id": + return quote_comment_str("id:" + quotes(condition.val)); + case "newer": + return quote_comment_str("newer:" + quotes(condition.val)); + case "user": + return quote_comment_str("user:" + quotes(condition.val)); + case "uid": + return quote_comment_str("uid:" + quotes(condition.val)); + default: + return ""; + } + case "free form": + return quote_comment_str(quotes(condition.free)); + default: + return ""; + } + } - ffs.query = normalize(ffs.query); + ffs.query = normalize(ffs.query); - var freeForm = false; - for (var i=0; i;'); - query_parts.push('out skel qt;'); + query_parts.push("// print results"); + query_parts.push("out body;"); + query_parts.push(">;"); + query_parts.push("out skel qt;"); - callback(null, query_parts.join('\n')); - }); - } + callback(null, query_parts.join("\n")); + }); +}; - // this is a "did you mean …" mechanism against typos in preset names - ffs.repair_search = function(search, callback) { - try { - ffs = ffs_parser.parse(search); - } catch(e) { - return callback(false); - } +// this is a "did you mean …" mechanism against typos in preset names +ffs.repair_search = function(search, callback) { + try { + ffs = ffs_parser.parse(search); + } catch (e) { + return callback(false); + } - function quotes(s) { - if (s.match(/^[a-zA-Z0-9_]+$/) === null) - return '"'+s.replace(/"/g,'\\"')+'"'; - return s; - } + function quotes(s) { + if (s.match(/^[a-zA-Z0-9_]+$/) === null) + return '"' + s.replace(/"/g, '\\"') + '"'; + return s; + } - var search_parts = []; - var repaired = false; + var search_parts = []; + var repaired = false; - ffs_free(function(freeFormQuery) { - ffs.query = normalize(ffs.query); - ffs.query.queries.forEach(function (q) { - q.queries.forEach(validateQuery); - }); - function validateQuery(cond_query) { - if (cond_query.query === "free form") { - var ffs_clause = freeFormQuery.get_query_clause(cond_query); - if (ffs_clause === false) { - // try to find suggestions for occasional typos - var fuzzy = freeFormQuery.fuzzy_search(cond_query); - var free_regex = null; - try { free_regex = new RegExp("['\"]?"+escRegexp(cond_query.free)+"['\"]?"); } catch(e) {} - if (fuzzy && search.match(free_regex)) { - search_parts = search_parts.concat(search.split(free_regex)); - search = search_parts.pop(); - var replacement = quotes(fuzzy); - search_parts.push(replacement); - repaired = true; - } + ffs_free(function(freeFormQuery) { + ffs.query = normalize(ffs.query); + ffs.query.queries.forEach(function(q) { + q.queries.forEach(validateQuery); + }); + function validateQuery(cond_query) { + if (cond_query.query === "free form") { + var ffs_clause = freeFormQuery.get_query_clause(cond_query); + if (ffs_clause === false) { + // try to find suggestions for occasional typos + var fuzzy = freeFormQuery.fuzzy_search(cond_query); + var free_regex = null; + try { + free_regex = new RegExp( + "['\"]?" + escRegexp(cond_query.free) + "['\"]?" + ); + } catch (e) {} + if (fuzzy && search.match(free_regex)) { + search_parts = search_parts.concat(search.split(free_regex)); + search = search_parts.pop(); + var replacement = quotes(fuzzy); + search_parts.push(replacement); + repaired = true; } } } - search_parts.push(search); + } + search_parts.push(search); - if (!repaired) - callback(false); - else - callback(search_parts); - }); - } + if (!repaired) callback(false); + else callback(search_parts); + }); +}; - ffs.invalidateCache = function() { - freeFormQuery = undefined; - } +ffs.invalidateCache = function() { + freeFormQuery = undefined; +}; - export default ffs; +export default ffs; diff --git a/js/ffs/free.js b/js/ffs/free.js index e7456100..b4a8856f 100644 --- a/js/ffs/free.js +++ b/js/ffs/free.js @@ -1,9 +1,9 @@ // ffs/wizard module -import $ from 'jquery'; +import $ from "jquery"; -import '../promise-polyfill'; -import i18n from '../i18n'; -import {levenshteinDistance} from '../misc'; +import "../promise-polyfill"; +import i18n from "../i18n"; +import {levenshteinDistance} from "../misc"; var freeFormQuery = {}; var presets = {}; @@ -13,74 +13,84 @@ export default function ffs_free(callback) { if (loaded) { callback(freeFormQuery); } else { - loadPresets() - .then(loadPresetTranslations) - .then(function() { + loadPresets().then(loadPresetTranslations).then(function() { callback(freeFormQuery); }); } // load presets function loadPresets() { - return import("../../data/iD_presets.json").then(function(data) { - presets = data; - Object.keys(presets).forEach(function(key) { - var preset = data[key]; - preset.nameCased = preset.name; - preset.name = preset.name.toLowerCase(); - preset.terms = !preset.terms ? [] : preset.terms.map(function(term) {return term.toLowerCase();}); + return import("../../data/iD_presets.json") + .then(function(data) { + presets = data; + Object.keys(presets).forEach(function(key) { + var preset = data[key]; + preset.nameCased = preset.name; + preset.name = preset.name.toLowerCase(); + preset.terms = !preset.terms + ? [] + : preset.terms.map(function(term) { + return term.toLowerCase(); + }); + }); + }) + .catch(function() { + throw new Error("failed to load presets file"); }); - }).catch(function() { - throw new Error("failed to load presets file"); - }); } // load preset translations function loadPresetTranslations() { var language = i18n.getLanguage(); if (language == "en") return; - import("../../data/iD_presets_"+language+".json").then(function(data){ - // load translated names and terms into presets object - Object.keys(data).forEach(function(preset) { - var translation = data[preset]; - preset = presets[preset]; - preset.translated = true; - // save original preset name under alternative terms - var oriPresetName = preset.name; - // save translated preset name - preset.nameCased = translation.name; - preset.name = translation.name.toLowerCase(); - // add new terms - if (translation.terms) - preset.terms = translation.terms.split(",") - .map(function(term) { return term.trim().toLowerCase(); }) - .concat(preset.terms); - // add this to the front to allow exact (english) preset names to match before terms - preset.terms.unshift(oriPresetName); + import("../../data/iD_presets_" + language + ".json") + .then(function(data) { + // load translated names and terms into presets object + Object.keys(data).forEach(function(preset) { + var translation = data[preset]; + preset = presets[preset]; + preset.translated = true; + // save original preset name under alternative terms + var oriPresetName = preset.name; + // save translated preset name + preset.nameCased = translation.name; + preset.name = translation.name.toLowerCase(); + // add new terms + if (translation.terms) + preset.terms = translation.terms + .split(",") + .map(function(term) { + return term.trim().toLowerCase(); + }) + .concat(preset.terms); + // add this to the front to allow exact (english) preset names to match before terms + preset.terms.unshift(oriPresetName); + }); + }) + .catch(function() { + throw new Error("failed to load preset translations file: " + language); }); - }).catch(function(){ - throw new Error("failed to load preset translations file: " + language); - }); } } freeFormQuery.get_query_clause = function(condition) { // search presets for ffs term var search = condition.free.toLowerCase(); - var candidates = Object.keys(presets).map(function(key) { - return presets[key]; - }).filter(function(preset) { - if (preset.searchable===false) return false; - if (preset.name === search) return true; - preset._termsIndex = preset.terms.indexOf(search); - return preset._termsIndex != -1; - }); - if (candidates.length === 0) - return false; + var candidates = Object.keys(presets) + .map(function(key) { + return presets[key]; + }) + .filter(function(preset) { + if (preset.searchable === false) return false; + if (preset.name === search) return true; + preset._termsIndex = preset.terms.indexOf(search); + return preset._termsIndex != -1; + }); + if (candidates.length === 0) return false; // sort candidates - candidates.sort(function(a,b) { + candidates.sort(function(a, b) { // prefer exact name matches if (a.name === search) return -1; - if (b.name === search) return 1; + if (b.name === search) return 1; return a._termsIndex - b._termsIndex; }); var preset = candidates[0]; @@ -102,7 +112,7 @@ freeFormQuery.get_query_clause = function(condition) { types.push("relation"); break; default: - console.log("unknown geometry type "+g+" of preset "+preset.name); + console.log("unknown geometry type " + g + " of preset " + preset.name); } }); function onlyUnique(value, index, self) { @@ -113,42 +123,46 @@ freeFormQuery.get_query_clause = function(condition) { conditions: Object.keys(preset.tags).map(function(k) { var v = preset.tags[k]; return { - query: v==="*" ? "key" : "eq", + query: v === "*" ? "key" : "eq", key: k, val: v }; }) }; -} +}; freeFormQuery.fuzzy_search = function(condition) { // search presets for ffs term var search = condition.free.toLowerCase(); // fuzzyness: max lev.dist allowed to still match - var fuzzyness = 2+Math.floor(search.length/7); + var fuzzyness = 2 + Math.floor(search.length / 7); function fuzzyMatch(term) { return levenshteinDistance(term, search) <= fuzzyness; } - var candidates = Object.keys(presets).map(function(key) { - return presets[key]; - }).filter(function(preset) { - if (preset.searchable===false) return false; - if (fuzzyMatch(preset.name)) return true; - return preset.terms.some(fuzzyMatch); - }); - if (candidates.length === 0) - return false; + var candidates = Object.keys(presets) + .map(function(key) { + return presets[key]; + }) + .filter(function(preset) { + if (preset.searchable === false) return false; + if (fuzzyMatch(preset.name)) return true; + return preset.terms.some(fuzzyMatch); + }); + if (candidates.length === 0) return false; // sort candidates function preset_weight(preset) { - return [preset.name].concat(preset.terms).map(function(term, index) { - return levenshteinDistance(term,search); - }).reduce(function min(a, b) { - return a <= b ? a : b; - }); - }; - candidates.sort(function(a,b) { + return [preset.name] + .concat(preset.terms) + .map(function(term, index) { + return levenshteinDistance(term, search); + }) + .reduce(function min(a, b) { + return a <= b ? a : b; + }); + } + candidates.sort(function(a, b) { return preset_weight(a) - preset_weight(b); }); var preset = candidates[0]; return preset.nameCased; -} +}; diff --git a/js/i18n.js b/js/i18n.js index ef1afba7..0fab4f8a 100644 --- a/js/i18n.js +++ b/js/i18n.js @@ -1,83 +1,82 @@ // global i18n object -import $ from 'jquery'; -import _ from 'lodash'; +import $ from "jquery"; +import _ from "lodash"; -import './promise-polyfill'; -import settings from './settings'; +import "./promise-polyfill"; +import settings from "./settings"; -var i18n = new(function() { +var i18n = new function() { function browser_locale() { /* taken from https://github.com/maxogden/browser-locale by Max Ogden, BSD licensed */ - var lang - + var lang; + if (navigator.languages) { // chrome does not currently set navigator.language correctly https://code.google.com/p/chromium/issues/detail?id=101138 // but it does set the first element of navigator.languages correctly - lang = navigator.languages[0] + lang = navigator.languages[0]; } else if (navigator.userLanguage) { // IE only - lang = navigator.userLanguage + lang = navigator.userLanguage; } else { // as of this writing the latest version of firefox + safari set this correctly - lang = navigator.language + lang = navigator.language; } - - return lang + + return lang; } var default_lng = "en"; var languages = { // translations found in locale/*.json - "en": "English", - "ca": "Catalan", - "da": "Danish", - "eo": "Esperanto", - "de": "German", - "el": "Greek", - "es": "Spanish", - "et": "Estonian", - "fr": "French", - "hr": "Croatian", - "hu": "Hungarian", - "it": "Italian", - "ja": "Japanese", - "nl": "Dutch", - "no": "Norwegian", - "pl": "Polish", - "pt": "Portuguese", + en: "English", + ca: "Catalan", + da: "Danish", + eo: "Esperanto", + de: "German", + el: "Greek", + es: "Spanish", + et: "Estonian", + fr: "French", + hr: "Croatian", + hu: "Hungarian", + it: "Italian", + ja: "Japanese", + nl: "Dutch", + no: "Norwegian", + pl: "Polish", + pt: "Portuguese", "pt-BR": "Portuguese (Brazil)", - "ru": "Russian", - "sl": "Slovenian", - "uk": "Ukrainian", - "vi": "Vietnamese", + ru: "Russian", + sl: "Slovenian", + uk: "Ukrainian", + vi: "Vietnamese", "zh-TW": "Chinese (Taiwan)" }; var supported_lngs = _.keys(languages); this.getSupportedLanguages = function() { return supported_lngs; - } + }; this.getSupportedLanguagesDescriptions = function() { return languages; - } + }; this.getLanguage = function(lng) { lng = lng || settings.ui_language; if (lng == "auto") { // get user agent's language try { lng = browser_locale().toLowerCase(); - } catch(e) {} + } catch (e) {} // hardcode some language fallbacks if (lng === "nb") lng = "no"; // Norwegian Bokmål // sanitize inconsistent use of lower and upper case spelling var parts; - if (parts = lng.match(/(.*)-(.*)/)) - lng = parts[1]+'-'+parts[2].toUpperCase(); + if ((parts = lng.match(/(.*)-(.*)/))) + lng = parts[1] + "-" + parts[2].toUpperCase(); // fall back to generic language file if no country-specific i18n is found - if ($.inArray(lng,supported_lngs) == -1) - lng = lng.replace(/-.*/,""); + if ($.inArray(lng, supported_lngs) == -1) lng = lng.replace(/-.*/, ""); } return lng; - } + }; /** * Determines the language, fetches the language pack and translates the UI @@ -86,54 +85,59 @@ var i18n = new(function() { this.translate = function(lng) { lng = i18n.getLanguage(lng); - if ($.inArray(lng,supported_lngs) == -1) { - console.log("unsupported language: "+lng+" switching back to: "+default_lng); + if ($.inArray(lng, supported_lngs) == -1) { + console.log( + "unsupported language: " + lng + " switching back to: " + default_lng + ); lng = default_lng; } // load language pack try { - return import("../locales/"+lng+".json").then(function(data){ - td = data; - i18n.translate_ui(); - // todo: nicer implementation - return data; - }, function(e){ - console.log("failed to load language file "+lng, e); - }); - } catch(e) { - console.log("failed to load language file "+lng, e); + return import("../locales/" + lng + ".json").then( + function(data) { + td = data; + i18n.translate_ui(); + // todo: nicer implementation + return data; + }, + function(e) { + console.log("failed to load language file " + lng, e); + } + ); + } catch (e) { + console.log("failed to load language file " + lng, e); } - } + }; this.translate_ui = function(element) { // if a DOM object is provided, only translate that one, otherwise // look for all object with the class "t" - $(element || ".t").each(function(nr,element) { + $(element || ".t").each(function(nr, element) { // get translation term(s) var terms = $(element).attr("data-t"); terms = terms.split(";"); - for (var i=0; i").parent().addClass("ui-combobox"); - input.autocomplete({ - source: options, - minLength: 0, - }).addClass("ui-widget ui-widget-content ui-corner-left ui-state-default"); - $( "" ).attr("tabIndex", -1).attr("title","show all items").appendTo(wrapper).button({ - icons: {primary: "ui-icon-triangle-1-s"}, text:false - }).removeClass( "ui-corner-all" ).addClass( "ui-corner-right ui-combobox-toggle" ).click(function() { - // close if already visible - if ( input.autocomplete( "widget" ).is( ":visible" ) ) { - input.autocomplete( "close" ); - return; - } - // pass empty string as value to search for, displaying all results - input.autocomplete( "search", "" ); - input.focus(); - }); + input + .autocomplete({ + source: options, + minLength: 0 + }) + .addClass("ui-widget ui-widget-content ui-corner-left ui-state-default"); + $("") + .attr("tabIndex", -1) + .attr("title", "show all items") + .appendTo(wrapper) + .button({ + icons: {primary: "ui-icon-triangle-1-s"}, + text: false + }) + .removeClass("ui-corner-all") + .addClass("ui-corner-right ui-combobox-toggle") + .click(function() { + // close if already visible + if (input.autocomplete("widget").is(":visible")) { + input.autocomplete("close"); + return; + } + // pass empty string as value to search for, displaying all results + input.autocomplete("search", ""); + input.focus(); + }); input[0].is_combobox = true; - } // make_combobox() + }; // make_combobox() // == public sub objects == @@ -77,7 +87,10 @@ var ide = new(function() { document.title = ide.waiter.frames[0] + " " + ide.waiter._initialTitle; var f = 0; ide.waiter.interval = setInterval(function() { - document.title = ide.waiter.frames[++f % ide.waiter.frames.length] + " " + ide.waiter._initialTitle; + document.title = + ide.waiter.frames[++f % ide.waiter.frames.length] + + " " + + ide.waiter._initialTitle; }, 250); ide.waiter.opened = true; }, @@ -91,17 +104,22 @@ var ide = new(function() { }, addInfo: function(txt, abortCallback) { $("#aborter").remove(); // remove previously added abort button, which cannot be used anymore. - $(".wait-info ul li:nth-child(n+1)").css("opacity",0.5); + $(".wait-info ul li:nth-child(n+1)").css("opacity", 0.5); $(".wait-info ul li span.ui-icon").addClass("ui-icon-check"); $(".wait-info ul li:nth-child(n+4)").hide(); - var li = $('
  • '+txt+"
  • "); + var li = $( + '
  • ' + + txt + + "
  • " + ); if (typeof abortCallback == "function") { ide.waiter.onAbort = abortCallback; - var aborter = $(' (
    abort)') - .on("click", function() { - ide.waiter.abort(); - return false; - }); + var aborter = $( + ' (abort)' + ).on("click", function() { + ide.waiter.abort(); + return false; + }); li.append(aborter); } $(".wait-info ul").prepend(li); @@ -111,7 +129,7 @@ var ide = new(function() { ide.waiter.addInfo("aborting"); ide.waiter.onAbort(ide.waiter.close); } - }, + } }; this.waiter._initialTitle = document.title; @@ -120,13 +138,23 @@ var ide = new(function() { this.init = function() { ide.waiter.addInfo("ide starting up"); // (very raw) compatibility check <- TODO: put this into its own function - if (jQuery.support.cors != true || - //typeof localStorage != "object" || - typeof (function() {var ls=undefined; try{localStorage.setItem("startup_localstorage_quota_test",123);localStorage.removeItem("startup_localstorage_quota_test");ls=localStorage;}catch(e){}; return ls;})() != "object" || - false) { + if ( + jQuery.support.cors != true || + //typeof localStorage != "object" || + typeof (function() { + var ls = undefined; + try { + localStorage.setItem("startup_localstorage_quota_test", 123); + localStorage.removeItem("startup_localstorage_quota_test"); + ls = localStorage; + } catch (e) {} + return ls; + })() != "object" || + false + ) { // the currently used browser is not capable of running the IDE. :( ide.not_supported = true; - $('#warning-unsupported-browser').dialog({modal:true}); + $("#warning-unsupported-browser").dialog({modal: true}); } // load settings ide.waiter.addInfo("load settings"); @@ -146,14 +174,17 @@ var ide = new(function() { ide.waiter.addInfo("parse url parameters"); var args = urlParameters(location.search); // set appropriate settings - if (args.has_coords) { // map center coords set via url + if (args.has_coords) { + // map center coords set via url settings.coords_lat = args.coords.lat; settings.coords_lon = args.coords.lng; } - if (args.has_zoom) { // map zoom set via url + if (args.has_zoom) { + // map zoom set via url settings.coords_zoom = args.zoom; } - if (args.run_query) { // query autorun activated via url + if (args.run_query) { + // query autorun activated via url ide.run_query_on_startup = true; } settings.save(); @@ -164,11 +195,11 @@ var ide = new(function() { // init page layout var isInitialAspectPortrait = $(window).width() / $(window).height() < 0.8; if (settings.editor_width != "" && !isInitialAspectPortrait) { - $("#editor").css("width",settings.editor_width); - $("#dataviewer").css("left",settings.editor_width); + $("#editor").css("width", settings.editor_width); + $("#dataviewer").css("left", settings.editor_width); } if (isInitialAspectPortrait) { - $("#editor, #dataviewer").addClass('portrait'); + $("#editor, #dataviewer").addClass("portrait"); } // make panels resizable $("#editor").resizable({ @@ -176,64 +207,79 @@ var ide = new(function() { minWidth: isInitialAspectPortrait ? undefined : "200", resize: function(ev) { if (!isInitialAspectPortrait) { - $(this).next().css('left', $(this).outerWidth() + 'px'); + $(this).next().css("left", $(this).outerWidth() + "px"); } else { var top = $(this).offset().top + $(this).outerHeight(); - $(this).next().css('top', top + 'px'); + $(this).next().css("top", top + "px"); } ide.map.invalidateSize(false); }, - stop:function() { + stop: function() { if (isInitialAspectPortrait) return; settings.editor_width = $("#editor").css("width"); settings.save(); } }); - $("#editor").prepend(""); + $("#editor").prepend( + "" + ); // init codemirror $("#editor textarea")[0].value = settings.code["overpass"]; if (settings.use_rich_editor) { - var pending=0; + var pending = 0; CodeMirror.defineMIME("text/x-overpassQL", { name: "clike", - keywords: (function(str){var r={}; var a=str.split(" "); for(var i=0; i'": function(cm) {cm.closeTag(cm, '>');}, - "'/'": function(cm) {cm.closeTag(cm, '/');}, - }, + "'>'": function(cm) { + cm.closeTag(cm, ">"); + }, + "'/'": function(cm) { + cm.closeTag(cm, "/"); + } + } }); // fire onChange after initialization ide.codeEditor.getOption("onChange")(ide.codeEditor); - } else { // use non-rich editor + } else { + // use non-rich editor ide.codeEditor = $("#editor textarea")[0]; ide.codeEditor.getValue = function() { return this.value; @@ -308,45 +368,47 @@ var ide = new(function() { }); } // set query if provided as url parameter or template: - if (args.has_query) { // query set via url + if (args.has_query) { + // query set via url ide.codeEditor.setValue(args.query); } // init dataviewer ide.dataViewer = CodeMirror($("#data")[0], { - value:'no data loaded yet', + value: "no data loaded yet", lineNumbers: true, readOnly: true, - mode: "javascript", + mode: "javascript" }); // init leaflet ide.map = new L.Map("map", { - attributionControl:false, - minZoom:0, - maxZoom:configs.maxMapZoom, - worldCopyJump:false, + attributionControl: false, + minZoom: 0, + maxZoom: configs.maxMapZoom, + worldCopyJump: false }); var tilesUrl = settings.tile_server; var tilesAttrib = configs.tileServerAttribution; - var tiles = new L.TileLayer(tilesUrl,{ - attribution:tilesAttrib, - noWrap:true, - maxNativeZoom:19, - maxZoom:ide.map.options.maxZoom, + var tiles = new L.TileLayer(tilesUrl, { + attribution: tilesAttrib, + noWrap: true, + maxNativeZoom: 19, + maxZoom: ide.map.options.maxZoom }); - attribControl = new L.Control.Attribution({prefix:""}); + attribControl = new L.Control.Attribution({prefix: ""}); attribControl.addAttribution(tilesAttrib); - var pos = new L.LatLng(settings.coords_lat,settings.coords_lon); - ide.map.setView(pos,settings.coords_zoom).addLayer(tiles); + var pos = new L.LatLng(settings.coords_lat, settings.coords_lon); + ide.map.setView(pos, settings.coords_zoom).addLayer(tiles); ide.map.tile_layer = tiles; // inverse opacity layer - ide.map.inv_opacity_layer = L.tileLayer("data:image/gif;base64,R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==") - .setOpacity(1-settings.background_opacity) + ide.map.inv_opacity_layer = L.tileLayer( + "data:image/gif;base64,R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" + ).setOpacity(1 - settings.background_opacity); if (settings.background_opacity != 1) ide.map.inv_opacity_layer.addTo(ide.map); - scaleControl = new L.Control.Scale({metric:true,imperial:false,}); + scaleControl = new L.Control.Scale({metric: true, imperial: false}); scaleControl.addTo(ide.map); - ide.map.on('moveend', function() { + ide.map.on("moveend", function() { settings.coords_lat = ide.map.getCenter().lat; settings.coords_lon = ide.map.getCenter().lng; settings.coords_zoom = ide.map.getZoom(); @@ -355,11 +417,12 @@ var ide = new(function() { // tabs $("#dataviewer > div#data")[0].style.zIndex = -1001; - $(".tabs a.button").bind("click",function(e) { + $(".tabs a.button").bind("click", function(e) { if ($(e.target).hasClass("active")) { return; } else { - $("#dataviewer > div#data")[0].style.zIndex = -1*$("#dataviewer > div#data")[0].style.zIndex; + $("#dataviewer > div#data")[0].style.zIndex = + -1 * $("#dataviewer > div#data")[0].style.zIndex; $(".tabs a.button").toggleClass("active"); } }); @@ -386,93 +449,154 @@ var ide = new(function() { // leaflet extension: more map controls var MapButtons = L.Control.extend({ options: { - position:'topleft', + position: "topleft" }, onAdd: function(map) { // create the control container with a particular class name - var container = L.DomUtil.create('div', 'leaflet-control-buttons leaflet-bar'); - var link = L.DomUtil.create('a', "leaflet-control-buttons-fitdata leaflet-bar-part leaflet-bar-part-top", container); + var container = L.DomUtil.create( + "div", + "leaflet-control-buttons leaflet-bar" + ); + var link = L.DomUtil.create( + "a", + "leaflet-control-buttons-fitdata leaflet-bar-part leaflet-bar-part-top", + container + ); $('').appendTo($(link)); - link.href = '#'; + link.href = "#"; link.className += " t"; link.setAttribute("data-t", "[title]map_controlls.zoom_to_data"); i18n.translate_ui(link); - L.DomEvent.addListener(link, 'click', function() { - // hardcoded maxZoom of 18, should be ok for most real-world use-cases - try {ide.map.fitBounds(overpass.osmLayer.getBaseLayer().getBounds(), {maxZoom: 18}); } catch (e) {} - return false; - }, ide.map); - link = L.DomUtil.create('a', "leaflet-control-buttons-myloc leaflet-bar-part", container); + L.DomEvent.addListener( + link, + "click", + function() { + // hardcoded maxZoom of 18, should be ok for most real-world use-cases + try { + ide.map.fitBounds(overpass.osmLayer.getBaseLayer().getBounds(), { + maxZoom: 18 + }); + } catch (e) {} + return false; + }, + ide.map + ); + link = L.DomUtil.create( + "a", + "leaflet-control-buttons-myloc leaflet-bar-part", + container + ); $('').appendTo($(link)); - link.href = '#'; + link.href = "#"; link.className += " t"; link.setAttribute("data-t", "[title]map_controlls.localize_user"); i18n.translate_ui(link); - L.DomEvent.addListener(link, 'click', function() { - // One-shot position request. - try { - navigator.geolocation.getCurrentPosition(function (position){ - var pos = new L.LatLng(position.coords.latitude,position.coords.longitude); - ide.map.setView(pos,settings.coords_zoom); - }); - } catch(e) {} - return false; - }, ide.map); - link = L.DomUtil.create('a', "leaflet-control-buttons-bboxfilter leaflet-bar-part", container); + L.DomEvent.addListener( + link, + "click", + function() { + // One-shot position request. + try { + navigator.geolocation.getCurrentPosition(function(position) { + var pos = new L.LatLng( + position.coords.latitude, + position.coords.longitude + ); + ide.map.setView(pos, settings.coords_zoom); + }); + } catch (e) {} + return false; + }, + ide.map + ); + link = L.DomUtil.create( + "a", + "leaflet-control-buttons-bboxfilter leaflet-bar-part", + container + ); $('').appendTo($(link)); - link.href = '#'; + link.href = "#"; link.className += " t"; link.setAttribute("data-t", "[title]map_controlls.select_bbox"); i18n.translate_ui(link); - L.DomEvent.addListener(link, 'click', function(e) { - if ($(e.target).parent().hasClass("disabled")) // check if this button is enabled + L.DomEvent.addListener( + link, + "click", + function(e) { + if ( + $(e.target).parent().hasClass("disabled") // check if this button is enabled + ) + return false; + if (!ide.map.bboxfilter.isEnabled()) { + ide.map.bboxfilter.setBounds(ide.map.getBounds().pad(-0.2)); + ide.map.bboxfilter.enable(); + } else { + ide.map.bboxfilter.disable(); + } + $(e.target) + .toggleClass("ui-icon-circlesmall-close") + .toggleClass("ui-icon-image"); return false; - if (!ide.map.bboxfilter.isEnabled()) { - ide.map.bboxfilter.setBounds(ide.map.getBounds().pad(-0.2)); - ide.map.bboxfilter.enable(); - } else { - ide.map.bboxfilter.disable(); - } - $(e.target).toggleClass("ui-icon-circlesmall-close").toggleClass("ui-icon-image"); - return false; - }, ide.map); - link = L.DomUtil.create('a', "leaflet-control-buttons-fullscreen leaflet-bar-part", container); - $('').appendTo($(link)); - link.href = '#'; + }, + ide.map + ); + link = L.DomUtil.create( + "a", + "leaflet-control-buttons-fullscreen leaflet-bar-part", + container + ); + $('').appendTo( + $(link) + ); + link.href = "#"; link.className += " t"; link.setAttribute("data-t", "[title]map_controlls.toggle_wide_map"); i18n.translate_ui(link); - L.DomEvent.addListener(link, 'click', function(e) { - $("#dataviewer").toggleClass("fullscreen"); - ide.map.invalidateSize(); - $(e.target).toggleClass("ui-icon-arrowthickstop-1-e").toggleClass("ui-icon-arrowthickstop-1-w"); - $("#editor").toggleClass("hidden"); - if ($("#editor").resizable("option","disabled")) - $("#editor").resizable("enable"); - else - $("#editor").resizable("disable"); - return false; - }, ide.map); - link = L.DomUtil.create('a', "leaflet-control-buttons-clearoverlay leaflet-bar-part leaflet-bar-part-bottom", container); + L.DomEvent.addListener( + link, + "click", + function(e) { + $("#dataviewer").toggleClass("fullscreen"); + ide.map.invalidateSize(); + $(e.target) + .toggleClass("ui-icon-arrowthickstop-1-e") + .toggleClass("ui-icon-arrowthickstop-1-w"); + $("#editor").toggleClass("hidden"); + if ($("#editor").resizable("option", "disabled")) + $("#editor").resizable("enable"); + else $("#editor").resizable("disable"); + return false; + }, + ide.map + ); + link = L.DomUtil.create( + "a", + "leaflet-control-buttons-clearoverlay leaflet-bar-part leaflet-bar-part-bottom", + container + ); $('').appendTo($(link)); - link.href = '#'; + link.href = "#"; link.className += " t"; link.setAttribute("data-t", "[title]map_controlls.toggle_data"); i18n.translate_ui(link); - L.DomEvent.addListener(link, 'click', function(e) { - e.preventDefault(); - if (ide.map.hasLayer(overpass.osmLayer)) - ide.map.removeLayer(overpass.osmLayer); - else - ide.map.addLayer(overpass.osmLayer); - return false; - }, ide.map); + L.DomEvent.addListener( + link, + "click", + function(e) { + e.preventDefault(); + if (ide.map.hasLayer(overpass.osmLayer)) + ide.map.removeLayer(overpass.osmLayer); + else ide.map.addLayer(overpass.osmLayer); + return false; + }, + ide.map + ); return container; - }, + } }); ide.map.addControl(new MapButtons()); // prevent propagation of doubleclicks on map controls - $(".leaflet-control-buttons > a").bind('dblclick', function(e) { + $(".leaflet-control-buttons > a").bind("dblclick", function(e) { e.stopPropagation(); }); // add tooltips to map controls @@ -490,51 +614,84 @@ var ide = new(function() { // leaflet extension: search box var SearchBox = L.Control.extend({ options: { - position:'topleft', + position: "topleft" }, onAdd: function(map) { - var container = L.DomUtil.create('div', 'leaflet-control-search ui-widget'); + var container = L.DomUtil.create( + "div", + "leaflet-control-search ui-widget" + ); container.style.position = "absolute"; container.style.left = "40px"; - var inp = L.DomUtil.create('input', '', container); - $('').click(function(e) {$(this).prev().autocomplete("search");}).insertAfter(inp); + var inp = L.DomUtil.create("input", "", container); + $( + '' + ) + .click(function(e) { + $(this).prev().autocomplete("search"); + }) + .insertAfter(inp); inp.id = "search"; // hack against focus stealing leaflet :/ - inp.onclick = function() {this.focus();} + inp.onclick = function() { + this.focus(); + }; // prevent propagation of doubleclicks to map container container.ondblclick = function(e) { e.stopPropagation(); }; // autocomplete functionality $(inp).autocomplete({ - source: function(request,response) { + source: function(request, response) { // ajax (GET) request to nominatim - $.ajax("https://search.osmnames.org/q/"+encodeURIComponent(request.term)+".js?key="+configs.osmnamesApiKey, { - success: function(data) { - // hacky firefox hack :( (it is not properly detecting json from the content-type header) - if (typeof data == "string") { // if the data is a string, but looks more like a json object - try { - data = $.parseJSON(data); - } catch (e) {} + $.ajax( + "https://search.osmnames.org/q/" + + encodeURIComponent(request.term) + + ".js?key=" + + configs.osmnamesApiKey, + { + success: function(data) { + // hacky firefox hack :( (it is not properly detecting json from the content-type header) + if (typeof data == "string") { + // if the data is a string, but looks more like a json object + try { + data = $.parseJSON(data); + } catch (e) {} + } + response( + $.map(data.results.slice(0, 10), function(item) { + return { + label: item.display_name, + value: item.display_name, + lat: item.lat, + lon: item.lon, + boundingbox: item.boundingbox + }; + }) + ); + }, + error: function() { + // todo: better error handling + console.error( + "An error occured while contacting the search server osmnames.org :(" + ); } - response($.map(data.results.slice(0,10),function(item) { - return {label:item.display_name, value:item.display_name,lat:item.lat,lon:item.lon,boundingbox:item.boundingbox} - })); - }, - error: function() { - // todo: better error handling - console.error("An error occured while contacting the search server osmnames.org :("); - }, - }); + } + ); }, minLength: 2, autoFocus: true, - select: function(event,ui) { + select: function(event, ui) { if (ui.item.boundingbox && ui.item.boundingbox instanceof Array) - ide.map.fitBounds(L.latLngBounds([[ui.item.boundingbox[1],ui.item.boundingbox[0]],[ui.item.boundingbox[3],ui.item.boundingbox[2]]]), {maxZoom: 18}); - else - ide.map.panTo(new L.LatLng(ui.item.lat,ui.item.lon)); - this.value=""; + ide.map.fitBounds( + L.latLngBounds([ + [ui.item.boundingbox[1], ui.item.boundingbox[0]], + [ui.item.boundingbox[3], ui.item.boundingbox[2]] + ]), + {maxZoom: 18} + ); + else ide.map.panTo(new L.LatLng(ui.item.lat, ui.item.lon)); + this.value = ""; return false; }, open: function() { @@ -542,12 +699,12 @@ var ide = new(function() { }, close: function() { $(this).addClass("ui-corner-all").removeClass("ui-corner-top"); - }, + } }); - $(inp).autocomplete("option","delay",20); + $(inp).autocomplete("option", "delay", 20); //$(inp).autocomplete().keypress(function(e) {if (e.which==13 || e.which==10) $(this).autocomplete("search");}); return container; - }, + } }); ide.map.addControl(new SearchBox()); // add cross hairs to map @@ -555,23 +712,27 @@ var ide = new(function() { .addClass("crosshairs") .hide() .appendTo("#map"); - if (settings.enable_crosshairs) - $(".crosshairs").show(); + if (settings.enable_crosshairs) $(".crosshairs").show(); - ide.map.bboxfilter = new L.LocationFilter({enable:!true,adjustButton:false,enableButton:false,}).addTo(ide.map); + ide.map.bboxfilter = new L.LocationFilter({ + enable: !true, + adjustButton: false, + enableButton: false + }).addTo(ide.map); - ide.map.on("popupopen popupclose",function(e) { + ide.map.on("popupopen popupclose", function(e) { if (typeof e.popup.layer != "undefined") { var layer = e.popup.layer.placeholder || e.popup.layer; // re-call style handler to eventually modify the style of the clicked feature - var stl = overpass.osmLayer._baseLayer.options.style(layer.feature, e.type=="popupopen"); + var stl = overpass.osmLayer._baseLayer.options.style( + layer.feature, + e.type == "popupopen" + ); if (typeof layer.eachLayer != "function") { - if (typeof layer.setStyle == "function") - layer.setStyle(stl); // other objects (pois, ways) + if (typeof layer.setStyle == "function") layer.setStyle(stl); // other objects (pois, ways) } else layer.eachLayer(function(layer) { - if (typeof layer.setStyle == "function") - layer.setStyle(stl); + if (typeof layer.setStyle == "function") layer.setStyle(stl); }); // for multipolygons! } }); @@ -580,65 +741,103 @@ var ide = new(function() { overpass.init(); // event handlers for overpass object - overpass.handlers["onProgress"] = function(msg,abortcallback) { - ide.waiter.addInfo(msg,abortcallback); - } + overpass.handlers["onProgress"] = function(msg, abortcallback) { + ide.waiter.addInfo(msg, abortcallback); + }; overpass.handlers["onDone"] = function() { ide.waiter.close(); - var map_bounds = ide.map.getBounds(); + var map_bounds = ide.map.getBounds(); var data_bounds = overpass.osmLayer.getBaseLayer().getBounds(); if (data_bounds.isValid() && !map_bounds.intersects(data_bounds)) { // show tooltip for button "zoom to data" - var prev_content = $(".leaflet-control-buttons-fitdata").tooltip("option","content"); - $(".leaflet-control-buttons-fitdata").tooltip("option","content", "← "+i18n.t("map_controlls.suggest_zoom_to_data")); + var prev_content = $(".leaflet-control-buttons-fitdata").tooltip( + "option", + "content" + ); + $(".leaflet-control-buttons-fitdata").tooltip( + "option", + "content", + "← " + i18n.t("map_controlls.suggest_zoom_to_data") + ); $(".leaflet-control-buttons-fitdata").tooltip("open"); - $(".leaflet-control-buttons-fitdata").tooltip("option", "hide", { effect: "fadeOut", duration: 1000 }); - setTimeout(function(){ - $(".leaflet-control-buttons-fitdata").tooltip("option","content", prev_content); + $(".leaflet-control-buttons-fitdata").tooltip("option", "hide", { + effect: "fadeOut", + duration: 1000 + }); + setTimeout(function() { + $(".leaflet-control-buttons-fitdata").tooltip( + "option", + "content", + prev_content + ); $(".leaflet-control-buttons-fitdata").tooltip("close"); - $(".leaflet-control-buttons-fitdata").tooltip("option", "hide", { effect: "fadeOut", duration: 100 }); - },2600); + $(".leaflet-control-buttons-fitdata").tooltip("option", "hide", { + effect: "fadeOut", + duration: 100 + }); + }, 2600); } - } + }; overpass.handlers["onEmptyMap"] = function(empty_msg, data_mode) { // show warning/info if only invisible data is returned if (empty_msg == "no visible data") { if (!settings.no_autorepair) { - var dialog_buttons= {}; + var dialog_buttons = {}; dialog_buttons[i18n.t("dialog.repair_query")] = function() { ide.repairQuery("no visible data"); $(this).dialog("close"); }; dialog_buttons[i18n.t("dialog.show_data")] = function() { - if ($("input[name=hide_incomplete_data_warning]",this)[0].checked) { + if ( + $("input[name=hide_incomplete_data_warning]", this)[0].checked + ) { settings.no_autorepair = true; settings.save(); } ide.switchTab("Data"); $(this).dialog("close"); }; - $('

    '+i18n.t("warning.incomplete.expl.1")+'

    '+i18n.t("warning.incomplete.expl.2")+'

     '+i18n.t("warning.incomplete.not_again")+'

    ').dialog({ - modal:true, - buttons: dialog_buttons, + $( + '

    ' + + i18n.t("warning.incomplete.expl.1") + + "

    " + + i18n.t("warning.incomplete.expl.2") + + '

     ' + + i18n.t("warning.incomplete.not_again") + + "

    " + ).dialog({ + modal: true, + buttons: dialog_buttons }); } } // auto tab switching (if only areas are returned) - if (empty_msg == "only areas returned") - ide.switchTab("Data"); + if (empty_msg == "only areas returned") ide.switchTab("Data"); // auto tab switching (if nodes without coordinates are returned) - if (empty_msg == "no coordinates returned") - ide.switchTab("Data"); + if (empty_msg == "no coordinates returned") ide.switchTab("Data"); // auto tab switching (if unstructured data is returned) - if (data_mode == "unknown") - ide.switchTab("Data"); + if (data_mode == "unknown") ide.switchTab("Data"); // display empty map badge - $('
    '+i18n.t("map.intentionally_blank")+' ('+empty_msg+')
    ').appendTo("#map"); - } - overpass.handlers["onDataRecieved"] = function(amount, amount_txt, abortCB, continueCB) { - if (amount > 1000000) { // more than ~1MB of data + $( + '
    ' + + i18n.t("map.intentionally_blank") + + " (" + + empty_msg + + ")
    " + ).appendTo("#map"); + }; + overpass.handlers["onDataRecieved"] = function( + amount, + amount_txt, + abortCB, + continueCB + ) { + if (amount > 1000000) { + // more than ~1MB of data // show warning dialog - var dialog_buttons= {}; + var dialog_buttons = {}; dialog_buttons[i18n.t("dialog.abort")] = function() { $(this).dialog("close"); abortCB(); @@ -647,53 +846,91 @@ var ide = new(function() { $(this).dialog("close"); continueCB(); }; - $('

    '+i18n.t("warning.huge_data.expl.1").replace("{{amount_txt}}",amount_txt)+'

    '+i18n.t("warning.huge_data.expl.2")+'

    ').dialog({ - modal:true, + $( + '

    ' + + i18n + .t("warning.huge_data.expl.1") + .replace("{{amount_txt}}", amount_txt) + + "

    " + + i18n.t("warning.huge_data.expl.2") + + "

    " + ).dialog({ + modal: true, buttons: dialog_buttons, dialogClass: "huge_data" }); - } else - continueCB(); - } + } else continueCB(); + }; overpass.handlers["onAbort"] = function() { ide.waiter.close(); - } + }; overpass.handlers["onAjaxError"] = function(errmsg) { // show error dialog - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - $('

    '+i18n.t("error.ajax.expl")+'

    '+errmsg+'
    ').dialog({ - modal:true, - buttons: dialog_buttons, + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.ajax.expl") + + "

    " + + errmsg + + "
    " + ).dialog({ + modal: true, + buttons: dialog_buttons }); // dialog // print error text, if present - if (overpass.resultText) - ide.dataViewer.setValue(overpass.resultText); - } + if (overpass.resultText) ide.dataViewer.setValue(overpass.resultText); + }; overpass.handlers["onQueryError"] = function(errmsg) { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - $('

    '+i18n.t("error.query.expl")+'

    '+errmsg+"
    ").dialog({ - modal:true, - maxHeight:600, - buttons: dialog_buttons, + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.query.expl") + + "

    " + + errmsg + + "
    " + ).dialog({ + modal: true, + maxHeight: 600, + buttons: dialog_buttons }); - } + }; overpass.handlers["onStyleError"] = function(errmsg) { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - $('

    '+i18n.t("error.mapcss.expl")+'

    '+errmsg+"
    ").dialog({ - modal:true, - buttons: dialog_buttons, + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.mapcss.expl") + + "

    " + + errmsg + + "
    " + ).dialog({ + modal: true, + buttons: dialog_buttons }); - } + }; overpass.handlers["onQueryErrorLine"] = function(linenumber) { ide.highlightError(linenumber); - } + }; overpass.handlers["onRawDataPresent"] = function() { - ide.dataViewer.setOption("mode",overpass.resultType); + ide.dataViewer.setOption("mode", overpass.resultType); ide.dataViewer.setValue(overpass.resultText); - } + }; overpass.handlers["onGeoJsonReady"] = function() { // show layer ide.map.addLayer(overpass.osmLayer); @@ -704,57 +941,88 @@ var ide = new(function() { // display stats if (settings.show_data_stats) { var stats = overpass.stats; - var stats_txt = ( - ""+i18n.t("data_stats.loaded")+" – "+ - ""+i18n.t("data_stats.nodes")+": "+stats.data.nodes+ - ", "+i18n.t("data_stats.ways")+": "+stats.data.ways+ - ", "+i18n.t("data_stats.relations")+": "+stats.data.relations+ - (stats.data.areas>0 ? ", "+i18n.t("data_stats.areas")+": "+stats.data.areas : "") + - "
    "+ - ""+i18n.t("data_stats.displayed")+" – "+ - ""+i18n.t("data_stats.pois")+": "+stats.geojson.pois+ - ", "+i18n.t("data_stats.lines")+": "+stats.geojson.lines+ - ", "+i18n.t("data_stats.polygons")+": "+stats.geojson.polys+ - "" - ); + var stats_txt = + "" + + i18n.t("data_stats.loaded") + + " – " + + "" + + i18n.t("data_stats.nodes") + + ": " + + stats.data.nodes + + ", " + + i18n.t("data_stats.ways") + + ": " + + stats.data.ways + + ", " + + i18n.t("data_stats.relations") + + ": " + + stats.data.relations + + (stats.data.areas > 0 + ? ", " + i18n.t("data_stats.areas") + ": " + stats.data.areas + : "") + + "
    " + + "" + + i18n.t("data_stats.displayed") + + " – " + + "" + + i18n.t("data_stats.pois") + + ": " + + stats.geojson.pois + + ", " + + i18n.t("data_stats.lines") + + ": " + + stats.geojson.lines + + ", " + + i18n.t("data_stats.polygons") + + ": " + + stats.geojson.polys + + ""; $( - '
    ' - +stats_txt - +'
    ' + '
    ' + stats_txt + "
    " ).insertAfter("#map"); // show more stats as a tooltip - var backlogOverpass = function () { + var backlogOverpass = function() { return moment(overpass.timestamp, moment.ISO_8601).fromNow(true); //return Math.round((new Date() - new Date(overpass.timestamp))/1000/60*10)/10; }; - var backlogOverpassAreas = function () { + var backlogOverpassAreas = function() { return moment(overpass.timestampAreas, moment.ISO_8601).fromNow(true); }; var backlogOverpassExceedsLimit = function() { var now = moment(); var ts = moment(overpass.timestamp, moment.ISO_8601); - return (now.diff(ts, 'hours', true) >= 24); + return now.diff(ts, "hours", true) >= 24; }; var backlogOverpassAreasExceedsLimit = function() { var now = moment(); var ts = moment(overpass.timestampAreas, moment.ISO_8601); - return (now.diff(ts, 'hours', true) >= 96); + return now.diff(ts, "hours", true) >= 96; }; $("#data_stats").tooltip({ items: "div", tooltipClass: "stats", - content: function () { + content: function() { var str = "
    "; if (overpass.timestamp) { - str += i18n.t("data_stats.lag")+": " - + backlogOverpass()+" "+i18n.t("data_stats.lag.expl")+"" + str += + i18n.t("data_stats.lag") + + ": " + + backlogOverpass() + + " " + + i18n.t("data_stats.lag.expl") + + ""; } if (overpass.timestampAreas) { - str += "
    " - + i18n.t("data_stats.lag_areas")+": " - + backlogOverpassAreas()+" "+i18n.t("data_stats.lag.expl")+"" + str += + "
    " + + i18n.t("data_stats.lag_areas") + + ": " + + backlogOverpassAreas() + + " " + + i18n.t("data_stats.lag.expl") + + ""; } - str+="
    "; + str += ""; return str; }, hide: { @@ -766,15 +1034,17 @@ var ide = new(function() { at: "right top" } }); - if ((overpass.timestamp && backlogOverpassExceedsLimit()) || - (overpass.timestampAreas && backlogOverpassAreasExceedsLimit())) { - $("#data_stats").css("background-color","yellow"); + if ( + (overpass.timestamp && backlogOverpassExceedsLimit()) || + (overpass.timestampAreas && backlogOverpassAreasExceedsLimit()) + ) { + $("#data_stats").css("background-color", "yellow"); } } - } + }; overpass.handlers["onPopupReady"] = function(p) { p.openOn(ide.map); - } + }; // close startup waiter ide.waiter.close(); @@ -783,15 +1053,21 @@ var ide = new(function() { if (ide.run_query_on_startup === true) { ide.update_map(); // automatically zoom to data. - if (!args.has_coords && - args.has_query && - args.query.match(/\{\{(bbox|center)\}\}/) === null) { + if ( + !args.has_coords && + args.has_query && + args.query.match(/\{\{(bbox|center)\}\}/) === null + ) { ide.run_query_on_startup = function() { ide.run_query_on_startup = null; // hardcoded maxZoom of 18, should be ok for most real-world use-cases - try {ide.map.fitBounds(overpass.osmLayer.getBaseLayer().getBounds(), {maxZoom: 18}); } catch (e) {} + try { + ide.map.fitBounds(overpass.osmLayer.getBaseLayer().getBounds(), { + maxZoom: 18 + }); + } catch (e) {} // todo: zoom only to specific zoomlevel if args.has_zoom is given - } + }; } } } // init() @@ -799,33 +1075,36 @@ var ide = new(function() { // returns the current visible bbox as a bbox-query this.map2bbox = function(lang) { var bbox; - if (!ide.map.bboxfilter.isEnabled()) - bbox = this.map.getBounds(); - else - bbox = ide.map.bboxfilter.getBounds(); - var lat1 = Math.min(Math.max(bbox.getSouthWest().lat,-90),90); - var lat2 = Math.min(Math.max(bbox.getNorthEast().lat,-90),90); - var lng1 = Math.min(Math.max(bbox.getSouthWest().lng,-180),180); - var lng2 = Math.min(Math.max(bbox.getNorthEast().lng,-180),180); - if (lang=="OverpassQL") - return lat1+','+lng1+','+lat2+','+lng2; - else if (lang=="xml") - return 's="'+lat1+'" w="'+lng1+'" n="'+lat2+'" e="'+lng2+'"'; - } + if (!ide.map.bboxfilter.isEnabled()) bbox = this.map.getBounds(); + else bbox = ide.map.bboxfilter.getBounds(); + var lat1 = Math.min(Math.max(bbox.getSouthWest().lat, -90), 90); + var lat2 = Math.min(Math.max(bbox.getNorthEast().lat, -90), 90); + var lng1 = Math.min(Math.max(bbox.getSouthWest().lng, -180), 180); + var lng2 = Math.min(Math.max(bbox.getNorthEast().lng, -180), 180); + if (lang == "OverpassQL") + return lat1 + "," + lng1 + "," + lat2 + "," + lng2; + else if (lang == "xml") + return ( + 's="' + lat1 + '" w="' + lng1 + '" n="' + lat2 + '" e="' + lng2 + '"' + ); + }; // returns the current visible map center as a coord-query this.map2coord = function(lang) { var center = this.map.getCenter(); - if (lang=="OverpassQL") - return center.lat+','+center.lng; - else if (lang=="xml") - return 'lat="'+center.lat+'" lon="'+center.lng+'"'; - } + if (lang == "OverpassQL") return center.lat + "," + center.lng; + else if (lang == "xml") + return 'lat="' + center.lat + '" lon="' + center.lng + '"'; + }; this.relativeTime = function(instr, callback) { var now = Date.now(); // very basic differential date - instr = instr.toLowerCase().match(/(-?[0-9]+) ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?/); + instr = instr + .toLowerCase() + .match( + /(-?[0-9]+) ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?/ + ); if (instr === null) { - callback(''); // todo: throw an error. do not silently fail + callback(""); // todo: throw an error. do not silently fail return; } var count = parseInt(instr[1]); @@ -833,46 +1112,63 @@ var ide = new(function() { switch (instr[2]) { case "second": case "seconds": - interval=1; break; + interval = 1; + break; case "minute": case "minutes": - interval=60; break; + interval = 60; + break; case "hour": case "hours": - interval=3600; break; + interval = 3600; + break; case "day": case "days": default: - interval=86400; break; + interval = 86400; + break; case "week": case "weeks": - interval=604800; break; + interval = 604800; + break; case "month": case "months": - interval=2628000; break; + interval = 2628000; + break; case "year": case "years": - interval=31536000; break; + interval = 31536000; + break; } - var date = now - count*interval*1000; - callback((new Date(date)).toISOString()); - } - function onNominatimError(search,type) { + var date = now - count * interval * 1000; + callback(new Date(date).toISOString()); + }; + function onNominatimError(search, type) { // close waiter ide.waiter.close(); // highlight error lines var query = ide.getRawQuery(); query = query.split("\n"); - query.forEach(function(line,i) { - if (line.indexOf("{{geocode"+type+":"+search+"}}") !== -1) - ide.highlightError(i+1); + query.forEach(function(line, i) { + if (line.indexOf("{{geocode" + type + ":" + search + "}}") !== -1) + ide.highlightError(i + 1); }); // show error message dialog - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - $('

    '+i18n.t("error.nominatim.expl")+'

    '+htmlentities(search)+'

    ').dialog({ - modal:true, - buttons: dialog_buttons, + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.nominatim.expl") + + "

    " + + htmlentities(search) + + "

    " + ).dialog({ + modal: true, + buttons: dialog_buttons }); // dialog } this.geocodeId = function(instr, callback) { @@ -880,65 +1176,60 @@ var ide = new(function() { function filter(n) { return n.osm_type && n.osm_id; } - nominatim.getBest(instr,filter, function(err, res) { - if (err) return onNominatimError(instr,"Id"); - if (lang=="OverpassQL") - res = res.osm_type+"("+res.osm_id+")"; - else if (lang=="xml") - res = 'type="'+res.osm_type+'" ref="'+res.osm_id+'"'; + nominatim.getBest(instr, filter, function(err, res) { + if (err) return onNominatimError(instr, "Id"); + if (lang == "OverpassQL") res = res.osm_type + "(" + res.osm_id + ")"; + else if (lang == "xml") + res = 'type="' + res.osm_type + '" ref="' + res.osm_id + '"'; callback(res); }); - } + }; this.geocodeArea = function(instr, callback) { var lang = ide.getQueryLang(); function filter(n) { - return n.osm_type && n.osm_id && n.osm_type!=="node"; + return n.osm_type && n.osm_id && n.osm_type !== "node"; } - nominatim.getBest(instr,filter, function(err, res) { - if (err) return onNominatimError(instr,"Area"); - var area_ref = 1*res.osm_id; - if (res.osm_type == "way") - area_ref += 2400000000; - if (res.osm_type == "relation") - area_ref += 3600000000; - if (lang=="OverpassQL") - res = "area("+area_ref+")"; - else if (lang=="xml") - res = 'type="area" ref="'+area_ref+'"'; + nominatim.getBest(instr, filter, function(err, res) { + if (err) return onNominatimError(instr, "Area"); + var area_ref = 1 * res.osm_id; + if (res.osm_type == "way") area_ref += 2400000000; + if (res.osm_type == "relation") area_ref += 3600000000; + if (lang == "OverpassQL") res = "area(" + area_ref + ")"; + else if (lang == "xml") res = 'type="area" ref="' + area_ref + '"'; callback(res); }); - } + }; this.geocodeBbox = function(instr, callback) { var lang = ide.getQueryLang(); nominatim.getBest(instr, function(err, res) { - if (err) return onNominatimError(instr,"Bbox"); - var lat1 = Math.min(Math.max(res.boundingbox[0],-90),90); - var lat2 = Math.min(Math.max(res.boundingbox[1],-90),90); - var lng1 = Math.min(Math.max(res.boundingbox[2],-180),180); - var lng2 = Math.min(Math.max(res.boundingbox[3],-180),180); - if (lang=="OverpassQL") - res = lat1+','+lng1+','+lat2+','+lng2; - else if (lang=="xml") - res = 's="'+lat1+'" w="'+lng1+'" n="'+lat2+'" e="'+lng2+'"'; + if (err) return onNominatimError(instr, "Bbox"); + var lat1 = Math.min(Math.max(res.boundingbox[0], -90), 90); + var lat2 = Math.min(Math.max(res.boundingbox[1], -90), 90); + var lng1 = Math.min(Math.max(res.boundingbox[2], -180), 180); + var lng2 = Math.min(Math.max(res.boundingbox[3], -180), 180); + if (lang == "OverpassQL") + res = lat1 + "," + lng1 + "," + lat2 + "," + lng2; + else if (lang == "xml") + res = + 's="' + lat1 + '" w="' + lng1 + '" n="' + lat2 + '" e="' + lng2 + '"'; callback(res); }); - } + }; this.geocodeCoords = function(instr, callback) { var lang = ide.getQueryLang(); nominatim.getBest(instr, function(err, res) { - if (err) return onNominatimError(instr,"Coords"); - if (lang=="OverpassQL") - res = res.lat+','+res.lon; - else if (lang=="xml") - res = 'lat="'+res.lat+'" lon="'+res.lon+'"'; + if (err) return onNominatimError(instr, "Coords"); + if (lang == "OverpassQL") res = res.lat + "," + res.lon; + else if (lang == "xml") + res = 'lat="' + res.lat + '" lon="' + res.lon + '"'; callback(res); }); - } + }; /* this returns the current raw query in the editor. * shortcuts are not expanded. */ this.getRawQuery = function() { return ide.codeEditor.getValue(); - } + }; /* this returns the current query in the editor. * shortcuts are expanded. */ this.getQuery = function(callback) { @@ -946,25 +1237,36 @@ var ide = new(function() { var queryLang = ide.getQueryLang(); // parse query and process shortcuts // special handling for global bbox in xml queries (which uses an OverpassQL-like notation instead of n/s/e/w parameters): - query = query.replace(/(\]+bbox[^=]*=[^"'']*["'])({{bbox}})(["'])/,"$1{{__bbox__global_bbox_xml__ezs4K8__}}$3"); + query = query.replace( + /(\]+bbox[^=]*=[^"'']*["'])({{bbox}})(["'])/, + "$1{{__bbox__global_bbox_xml__ezs4K8__}}$3" + ); var shortcuts = { - "bbox": ide.map2bbox(queryLang), - "center": ide.map2coord(queryLang), - "__bbox__global_bbox_xml__ezs4K8__": ide.map2bbox("OverpassQL"), - "date": ide.relativeTime, - "geocodeId": ide.geocodeId, - "geocodeArea": ide.geocodeArea, - "geocodeBbox": ide.geocodeBbox, - "geocodeCoords": ide.geocodeCoords, + bbox: ide.map2bbox(queryLang), + center: ide.map2coord(queryLang), + __bbox__global_bbox_xml__ezs4K8__: ide.map2bbox("OverpassQL"), + date: ide.relativeTime, + geocodeId: ide.geocodeId, + geocodeArea: ide.geocodeArea, + geocodeBbox: ide.geocodeBbox, + geocodeCoords: ide.geocodeCoords, // legacy - "nominatimId": queryLang=="xml" ? ide.geocodeId : function(instr,callback) { - ide.geocodeId(instr, function(result) { callback(result+';'); }); - }, - "nominatimArea": queryLang=="xml" ? ide.geocodeArea : function(instr,callback) { - ide.geocodeArea(instr, function(result) { callback(result+';'); }); - }, - "nominatimBbox": ide.geocodeBbox, - "nominatimCoords": ide.geocodeCoords, + nominatimId: queryLang == "xml" + ? ide.geocodeId + : function(instr, callback) { + ide.geocodeId(instr, function(result) { + callback(result + ";"); + }); + }, + nominatimArea: queryLang == "xml" + ? ide.geocodeArea + : function(instr, callback) { + ide.geocodeArea(instr, function(result) { + callback(result + ";"); + }); + }, + nominatimBbox: ide.geocodeBbox, + nominatimCoords: ide.geocodeCoords }; queryParser.parse(query, shortcuts, function(query) { // parse mapcss declarations @@ -976,12 +1278,12 @@ var ide = new(function() { var data_source = null; if (queryParser.hasStatement("data")) { data_source = queryParser.getStatement("data"); - data_source = data_source.split(','); + data_source = data_source.split(","); var data_mode = data_source[0].toLowerCase(); data_source = data_source.slice(1); var options = {}; - for (var i=0; i

    '+i18n.t("dialog.delete_query.expl")+': "'+ex+'"?

    ').dialog({ + $( + '

    ' + + i18n.t("dialog.delete_query.expl") + + ": "" + + ex + + ""?

    " + ).dialog({ modal: true, - buttons: dialog_buttons, + buttons: dialog_buttons }); - } + }; // Event handlers this.onLoadClick = function() { $("#load-dialog ul").html(""); // reset example lists // load example list var has_saved_query = false; - for(var example in settings.saves) { + for (var example in settings.saves) { var type = settings.saves[example].type; - if (type == 'template') continue; - $('
  • ') - .append( - $(''+example+'').on("click", function(example) { - return function() {ide.loadExample(example); $(this).parents('.ui-dialog-content').dialog('close'); return false; } - }(example)), - $('').on("click", function(example) { - return function() { ide.removeExample(example, this); return false; } - }(example)) - ).appendTo("#load-dialog ul."+type); - if (type == "saved_query") - has_saved_query = true; + if (type == "template") continue; + $("
  • ") + .append( + $('' + example + "").on( + "click", + (function(example) { + return function() { + ide.loadExample(example); + $(this).parents(".ui-dialog-content").dialog("close"); + return false; + }; + })(example) + ), + $( + '' + ).on( + "click", + (function(example) { + return function() { + ide.removeExample(example, this); + return false; + }; + })(example) + ) + ) + .appendTo("#load-dialog ul." + type); + if (type == "saved_query") has_saved_query = true; } if (!has_saved_query) - $('
  • '+i18n.t("load.no_saved_query")+'
  • ').appendTo("#load-dialog ul.saved_query"); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.cancel")] = function() {$(this).dialog("close");}; + $("
  • " + i18n.t("load.no_saved_query") + "
  • ").appendTo( + "#load-dialog ul.saved_query" + ); + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.cancel")] = function() { + $(this).dialog("close"); + }; $("#load-dialog").dialog({ - modal:true, - buttons: dialog_buttons, + modal: true, + buttons: dialog_buttons }); $("#load-dialog").accordion({active: has_saved_query ? 0 : 1}); - } + }; this.onSaveClick = function() { // combobox for existing saves. var saves_names = new Array(); for (var key in settings.saves) - if (settings.saves[key].type != "template") - saves_names.push(key); + if (settings.saves[key].type != "template") saves_names.push(key); make_combobox($("#save-dialog input[name=save]"), saves_names); - var dialog_buttons= {}; + var dialog_buttons = {}; dialog_buttons[i18n.t("dialog.save")] = function() { - var name = $("input[name=save]",this)[0].value; + var name = $("input[name=save]", this)[0].value; settings.saves[htmlentities(name)] = { - "overpass": ide.getRawQuery(), - "type": "saved_query" + overpass: ide.getRawQuery(), + type: "saved_query" }; settings.save(); $(this).dialog("close"); }; - dialog_buttons[i18n.t("dialog.cancel")] = function() {$(this).dialog("close");}; + dialog_buttons[i18n.t("dialog.cancel")] = function() { + $(this).dialog("close"); + }; $("#save-dialog").dialog({ - modal:true, - buttons: dialog_buttons, + modal: true, + buttons: dialog_buttons }); - } + }; this.onRunClick = function() { ide.update_map(); - } - this.compose_share_link = function(query,compression,coords,run) { + }; + this.compose_share_link = function(query, compression, coords, run) { var share_link = ""; - if (!compression) { // compose uncompressed share link - share_link += "?Q="+encodeURIComponent(query); + if (!compression) { + // compose uncompressed share link + share_link += "?Q=" + encodeURIComponent(query); if (coords) - share_link += "&C="+L.Util.formatNum(ide.map.getCenter().lat)+";"+L.Util.formatNum(ide.map.getCenter().lng)+";"+ide.map.getZoom(); - if (run) - share_link += "&R"; - } else { // compose compressed share link - share_link += "?q="+encodeURIComponent(Base64.encode(lzw_encode(query))); + share_link += + "&C=" + + L.Util.formatNum(ide.map.getCenter().lat) + + ";" + + L.Util.formatNum(ide.map.getCenter().lng) + + ";" + + ide.map.getZoom(); + if (run) share_link += "&R"; + } else { + // compose compressed share link + share_link += + "?q=" + encodeURIComponent(Base64.encode(lzw_encode(query))); if (coords) { - var encode_coords = function(lat,lng) { - var coords_cpr = Base64.encodeNum( Math.round((lat+90)*100000) + Math.round((lng+180)*100000)*180*100000 ); - return "AAAAAAAA".substring(0,9-coords_cpr.length)+coords_cpr; - } - share_link += "&c="+encode_coords(ide.map.getCenter().lat, ide.map.getCenter().lng)+Base64.encodeNum(ide.map.getZoom()); + var encode_coords = function(lat, lng) { + var coords_cpr = Base64.encodeNum( + Math.round((lat + 90) * 100000) + + Math.round((lng + 180) * 100000) * 180 * 100000 + ); + return "AAAAAAAA".substring(0, 9 - coords_cpr.length) + coords_cpr; + }; + share_link += + "&c=" + + encode_coords(ide.map.getCenter().lat, ide.map.getCenter().lng) + + Base64.encodeNum(ide.map.getZoom()); } - if (run) - share_link += "&R"; + if (run) share_link += "&R"; } return share_link; - } + }; this.updateShareLink = function() { - var baseurl=location.protocol+"//"+location.host+location.pathname; + var baseurl = location.protocol + "//" + location.host + location.pathname; var query = ide.getRawQuery(); - var compress = ((settings.share_compression == "auto" && query.length > 300) || - (settings.share_compression == "on")) - var inc_coords = $("div#share-dialog input[name=include_coords]")[0].checked; - var run_immediately = $("div#share-dialog input[name=run_immediately]")[0].checked; + var compress = + (settings.share_compression == "auto" && query.length > 300) || + settings.share_compression == "on"; + var inc_coords = $("div#share-dialog input[name=include_coords]")[0] + .checked; + var run_immediately = $("div#share-dialog input[name=run_immediately]")[0] + .checked; - var shared_query = ide.compose_share_link(query,compress,inc_coords,run_immediately); - var share_link = baseurl+shared_query; + var shared_query = ide.compose_share_link( + query, + compress, + inc_coords, + run_immediately + ); + var share_link = baseurl + shared_query; - var warning = ''; + var warning = ""; if (share_link.length >= 2000) - warning = '

    '+i18n.t("warning.share.long")+'

    '; + warning = '

    ' + i18n.t("warning.share.long") + "

    "; if (share_link.length >= 4000) - warning = '

    '+i18n.t("warning.share.very_long")+'

    '; + warning = + '

    ' + + i18n.t("warning.share.very_long") + + "

    "; $("div#share-dialog #share_link_warning").html(warning); - $("div#share-dialog #share_link_a")[0].href=share_link; - $("div#share-dialog #share_link_textarea")[0].value=share_link; + $("div#share-dialog #share_link_a")[0].href = share_link; + $("div#share-dialog #share_link_textarea")[0].value = share_link; // automatically minify urls if enabled if (configs.short_url_service != "") { - $.get(configs.short_url_service+encodeURIComponent(share_link), function(data) { - $("div#share-dialog #share_link_a")[0].href=data; - $("div#share-dialog #share_link_textarea")[0].value=data; - }); + $.get( + configs.short_url_service + encodeURIComponent(share_link), + function(data) { + $("div#share-dialog #share_link_a")[0].href = data; + $("div#share-dialog #share_link_textarea")[0].value = data; + } + ); } - } + }; this.onShareClick = function() { - $("div#share-dialog input[name=include_coords]")[0].checked = settings.share_include_pos; + $("div#share-dialog input[name=include_coords]")[0].checked = + settings.share_include_pos; ide.updateShareLink(); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.done")] = function() {$(this).dialog("close");}; + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.done")] = function() { + $(this).dialog("close"); + }; $("div#share-dialog").dialog({ - modal:true, - buttons: dialog_buttons, + modal: true, + buttons: dialog_buttons }); - } + }; this.onExportClick = function() { - // prepare export dialog - ide.getQuery(function(query) { - var baseurl=location.protocol+"//"+location.host+location.pathname.match(/.*\//)[0]; - var server = (ide.data_source && - ide.data_source.mode == "overpass" && - ide.data_source.options.server) ? - ide.data_source.options.server : settings.server; - var queryWithMapCSS = query; - if (queryParser.hasStatement("style")) - queryWithMapCSS += "{{style: "+queryParser.getStatement("style")+" }}"; - if (queryParser.hasStatement("data")) - queryWithMapCSS += "{{data:"+queryParser.getStatement("data")+"}}"; - else if (settings.server !== configs.defaultServer) - queryWithMapCSS += "{{data:overpass,server="+settings.server+"}}"; - $("#export-dialog a#export-interactive-map")[0].href = baseurl+"map.html?Q="+encodeURIComponent(queryWithMapCSS); - // encoding exclamation marks for better command line usability (bash) - $("#export-dialog a#export-overpass-api")[0].href = server+"interpreter?data="+encodeURIComponent(query).replace(/!/g,"%21").replace(/\(/g,"%28").replace(/\)/g,"%29"); - $("#export-dialog a#export-text")[0].href = "data:text/plain;charset="+(document.characterSet||document.charset)+";base64,"+Base64.encode(query,true); - var query_raw = ide.getRawQuery(); - $("#export-dialog a#export-text-raw")[0].href = "data:text/plain;charset="+(document.characterSet||document.charset)+";base64,"+Base64.encode(query_raw,true); - var query_wiki = "{{OverpassTurboExample|loc="+L.Util.formatNum(ide.map.getCenter().lat)+';'+L.Util.formatNum(ide.map.getCenter().lng)+';'+ide.map.getZoom()+"|query=\n"; - query_wiki += query_raw - .replace(/{{/g, "mSAvmrw81O8NgWlX").replace(/{/g, "Z9P563g6zQYzjiLE") - .replace(/}}/g, "AtUhvGGxAlM1mP5i").replace(/}/g, "Yfxw6RTW5lewTqtg") - .replace(/mSAvmrw81O8NgWlX/g, "{{((}}").replace(/Z9P563g6zQYzjiLE/g, "{{(}}") - .replace(/AtUhvGGxAlM1mP5i/g, "{{))}}").replace(/Yfxw6RTW5lewTqtg/g, "{{)}}") - .replace(/\|/g, "{{!}}").replace(/{{!}}{{!}}/g, "{{!!}}"); - query_wiki += "\n}}"; - $("#export-dialog a#export-text-wiki")[0].href = "data:text/plain;charset="+(document.characterSet||document.charset)+";base64,"+Base64.encode(query_wiki,true); + // prepare export dialog + ide.getQuery(function(query) { + var baseurl = + location.protocol + + "//" + + location.host + + location.pathname.match(/.*\//)[0]; + var server = ide.data_source && + ide.data_source.mode == "overpass" && + ide.data_source.options.server + ? ide.data_source.options.server + : settings.server; + var queryWithMapCSS = query; + if (queryParser.hasStatement("style")) + queryWithMapCSS += + "{{style: " + queryParser.getStatement("style") + " }}"; + if (queryParser.hasStatement("data")) + queryWithMapCSS += "{{data:" + queryParser.getStatement("data") + "}}"; + else if (settings.server !== configs.defaultServer) + queryWithMapCSS += "{{data:overpass,server=" + settings.server + "}}"; + $("#export-dialog a#export-interactive-map")[0].href = + baseurl + "map.html?Q=" + encodeURIComponent(queryWithMapCSS); + // encoding exclamation marks for better command line usability (bash) + $("#export-dialog a#export-overpass-api")[0].href = + server + + "interpreter?data=" + + encodeURIComponent(query) + .replace(/!/g, "%21") + .replace(/\(/g, "%28") + .replace(/\)/g, "%29"); + $("#export-dialog a#export-text")[0].href = + "data:text/plain;charset=" + + (document.characterSet || document.charset) + + ";base64," + + Base64.encode(query, true); + var query_raw = ide.getRawQuery(); + $("#export-dialog a#export-text-raw")[0].href = + "data:text/plain;charset=" + + (document.characterSet || document.charset) + + ";base64," + + Base64.encode(query_raw, true); + var query_wiki = + "{{OverpassTurboExample|loc=" + + L.Util.formatNum(ide.map.getCenter().lat) + + ";" + + L.Util.formatNum(ide.map.getCenter().lng) + + ";" + + ide.map.getZoom() + + "|query=\n"; + query_wiki += query_raw + .replace(/{{/g, "mSAvmrw81O8NgWlX") + .replace(/{/g, "Z9P563g6zQYzjiLE") + .replace(/}}/g, "AtUhvGGxAlM1mP5i") + .replace(/}/g, "Yfxw6RTW5lewTqtg") + .replace(/mSAvmrw81O8NgWlX/g, "{{((}}") + .replace(/Z9P563g6zQYzjiLE/g, "{{(}}") + .replace(/AtUhvGGxAlM1mP5i/g, "{{))}}") + .replace(/Yfxw6RTW5lewTqtg/g, "{{)}}") + .replace(/\|/g, "{{!}}") + .replace(/{{!}}{{!}}/g, "{{!!}}"); + query_wiki += "\n}}"; + $("#export-dialog a#export-text-wiki")[0].href = + "data:text/plain;charset=" + + (document.characterSet || document.charset) + + ";base64," + + Base64.encode(query_wiki, true); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.done")] = function() {$(this).dialog("close");}; - $("#export-dialog a#export-map-state").unbind("click").bind("click",function() { - $('
    '+ - '

    '+i18n.t("export.map_view.permalink")+'

    '+'

    '+i18n.t("export.map_view.permalink_osm")+'

    '+ - '

    '+i18n.t("export.map_view.center")+'

    '+L.Util.formatNum(ide.map.getCenter().lat)+', '+L.Util.formatNum(ide.map.getCenter().lng)+' ('+i18n.t("export.map_view.center_expl")+')

    '+ - '

    '+i18n.t("export.map_view.bounds")+'

    '+L.Util.formatNum(ide.map.getBounds().getSouthWest().lat)+', '+L.Util.formatNum(ide.map.getBounds().getSouthWest().lng)+', '+L.Util.formatNum(ide.map.getBounds().getNorthEast().lat)+', '+L.Util.formatNum(ide.map.getBounds().getNorthEast().lng)+'
    ('+i18n.t("export.map_view.bounds_expl")+')

    '+ - (ide.map.bboxfilter.isEnabled() ? - '

    '+i18n.t("export.map_view.bounds_selection")+'

    '+L.Util.formatNum(ide.map.bboxfilter.getBounds().getSouthWest().lat)+', '+L.Util.formatNum(ide.map.bboxfilter.getBounds().getSouthWest().lng)+', '+L.Util.formatNum(ide.map.bboxfilter.getBounds().getNorthEast().lat)+', '+L.Util.formatNum(ide.map.bboxfilter.getBounds().getNorthEast().lng)+'
    ('+i18n.t("export.map_view.bounds_expl")+')

    ': - '' - ) + - '

    '+i18n.t("export.map_view.zoom")+'

    '+ide.map.getZoom()+'

    '+ - '
    ').dialog({ - modal:true, - buttons: dialog_buttons, - }); - return false; - }); - $("#export-dialog a#export-image").unbind("click").on("click", function() { - ide.onExportImageClick(); - $(this).parents('.ui-dialog-content').dialog('close'); - return false; - }); - function constructGeojsonString(geojson) { - var geoJSON_str; - if (!geojson) - geoJSON_str = i18n.t("export.geoJSON.no_data"); - else { - console.log(new Date()); - var gJ = { - type: "FeatureCollection", - generator: configs.appname, - copyright: overpass.copyright, - timestamp: overpass.timestamp, - features: geojson.features.map(function(feature) { - return { - type: "Feature", - properties: feature.properties, - geometry: feature.geometry + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.done")] = function() { + $(this).dialog("close"); + }; + $("#export-dialog a#export-map-state") + .unbind("click") + .bind("click", function() { + $( + '
    ' + + "

    " + + i18n.t("export.map_view.permalink") + + "

    " + + '

    ' + + i18n.t("export.map_view.permalink_osm") + + "

    " + + "

    " + + i18n.t("export.map_view.center") + + "

    " + + L.Util.formatNum(ide.map.getCenter().lat) + + ", " + + L.Util.formatNum(ide.map.getCenter().lng) + + " (" + + i18n.t("export.map_view.center_expl") + + ")

    " + + "

    " + + i18n.t("export.map_view.bounds") + + "

    " + + L.Util.formatNum(ide.map.getBounds().getSouthWest().lat) + + ", " + + L.Util.formatNum(ide.map.getBounds().getSouthWest().lng) + + ", " + + L.Util.formatNum(ide.map.getBounds().getNorthEast().lat) + + ", " + + L.Util.formatNum(ide.map.getBounds().getNorthEast().lng) + + "
    (" + + i18n.t("export.map_view.bounds_expl") + + ")

    " + + (ide.map.bboxfilter.isEnabled() + ? "

    " + + i18n.t("export.map_view.bounds_selection") + + "

    " + + L.Util.formatNum( + ide.map.bboxfilter.getBounds().getSouthWest().lat + ) + + ", " + + L.Util.formatNum( + ide.map.bboxfilter.getBounds().getSouthWest().lng + ) + + ", " + + L.Util.formatNum( + ide.map.bboxfilter.getBounds().getNorthEast().lat + ) + + ", " + + L.Util.formatNum( + ide.map.bboxfilter.getBounds().getNorthEast().lng + ) + + "
    (" + + i18n.t("export.map_view.bounds_expl") + + ")

    " + : "") + + "

    " + + i18n.t("export.map_view.zoom") + + "

    " + + ide.map.getZoom() + + "

    " + + "
    " + ).dialog({ + modal: true, + buttons: dialog_buttons + }); + return false; + }); + $("#export-dialog a#export-image") + .unbind("click") + .on("click", function() { + ide.onExportImageClick(); + $(this).parents(".ui-dialog-content").dialog("close"); + return false; + }); + function constructGeojsonString(geojson) { + var geoJSON_str; + if (!geojson) geoJSON_str = i18n.t("export.geoJSON.no_data"); + else { + console.log(new Date()); + var gJ = { + type: "FeatureCollection", + generator: configs.appname, + copyright: overpass.copyright, + timestamp: overpass.timestamp, + features: geojson.features.map(function(feature) { + return { + type: "Feature", + properties: feature.properties, + geometry: feature.geometry + }; + }) // makes deep copy + }; + gJ.features.forEach(function(f) { + var p = f.properties; + f.id = p.type + "/" + p.id; + f.properties = { + "@id": f.id }; - }), // makes deep copy + // escapes tags beginning with an @ with another @ + for (var m in p.tags || {}) f.properties[m.replace(/^@/, "@@")] = p.tags[m]; + for (var m in p.meta || {}) f.properties["@" + m] = p.meta[m]; + // expose internal properties: + // * tainted: indicates that the feature's geometry is incomplete + if (p.tainted) f.properties["@tainted"] = p.tainted; + // * geometry: indicates that the feature's geometry is approximated via the Overpass geometry types "center" or "bounds" + if (p.geometry) f.properties["@geometry"] = p.geometry; + // expose relation membership (complex data type) + if (p.relations && p.relations.length > 0) + f.properties["@relations"] = p.relations; + // todo: expose way membership for nodes? + }); + geoJSON_str = JSON.stringify(gJ, undefined, 2); } - gJ.features.forEach(function(f) { - var p = f.properties; - f.id = p.type+"/"+p.id; - f.properties = { - "@id": f.id - }; - for (var m in p.tags||{}) - // escapes tags beginning with an @ with another @ - f.properties[m.replace(/^@/,"@@")] = p.tags[m]; - for (var m in p.meta||{}) - f.properties["@"+m] = p.meta[m]; - // expose internal properties: - // * tainted: indicates that the feature's geometry is incomplete - if (p.tainted) - f.properties["@tainted"] = p.tainted; - // * geometry: indicates that the feature's geometry is approximated via the Overpass geometry types "center" or "bounds" - if (p.geometry) - f.properties["@geometry"] = p.geometry; - // expose relation membership (complex data type) - if (p.relations && p.relations.length > 0) - f.properties["@relations"] = p.relations; - // todo: expose way membership for nodes? - }); - geoJSON_str = JSON.stringify(gJ, undefined, 2); + return geoJSON_str; } - return geoJSON_str; - } - $("#export-dialog a#export-geoJSON").unbind("click").on("click", function() { - var geoJSON_str = constructGeojsonString(overpass.geojson); - var d = $("#export-geojson-dialog"); + $("#export-dialog a#export-geoJSON") + .unbind("click") + .on("click", function() { + var geoJSON_str = constructGeojsonString(overpass.geojson); + var d = $("#export-geojson-dialog"); - // make content downloadable as file - if (overpass.geojson) { - var blob = new Blob([geoJSON_str], {type: "application/json;charset=utf-8"}); - saveAs(blob, "export.geojson"); - } else { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - d.dialog({ - modal:true, - width:500, - buttons: dialog_buttons, - }); - $(".message", d).text(geoJSON_str); - } - return false; - }); - $("#export-dialog a#export-geoJSON-gist").unbind("click").on("click", function() { - var geoJSON_str = constructGeojsonString(overpass.geojson); - $.ajax("https://api.github.com/gists", { - method: "POST", - data: JSON.stringify({ - description: "data exported by overpass turbo", // todo:descr - public: true, - files: { - "overpass.geojson": { // todo:name - content: geoJSON_str - } - } - }) - }).success(function(data,textStatus,jqXHR) { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.done")] = function() {$(this).dialog("close");}; - $('
    '+ - '

    '+i18n.t("export.geoJSON_gist.gist")+' '+data.id+'

    '+ - '

    '+i18n.t("export.geoJSON_gist.geojsonio")+' '+i18n.t("export.geoJSON_gist.geojsonio_link")+'

    '+ - '
    ').dialog({ - modal:true, - buttons: dialog_buttons, - }); - // data.html_url; - }).error(function(jqXHR,textStatus,errorStr) { - alert("an error occured during the creation of the overpass gist:\n"+JSON.stringify(jqXHR)); - }); - return false; - }); - $("#export-dialog a#export-GPX").unbind("click").on("click", function() { - var gpx_str; - var geojson = overpass.geojson; - if (!geojson) - gpx_str = i18n.t("export.GPX.no_data"); - else { - gpx_str = togpx(geojson, { - creator: configs.appname, - metadata: { - "copyright": overpass.copyright, - "desc": "Filtered OSM data converted to GPX by overpass turbo", - "time": overpass.timestamp - }, - featureTitle: function(props) { - if (props.tags) { - if (props.tags.name) - return props.tags.name; - if (props.tags.ref) - return props.tags.ref; - if (props.tags["addr:housenumber"] && props.tags["addr:street"]) - return props.tags["addr:street"] + " " + props.tags["addr:housenumber"]; - } - return props.type + "/" + props.id; - }, - //featureDescription: function(props) {}, - featureLink: function(props) { - return "http://osm.org/browse/"+props.type+"/"+props.id; + // make content downloadable as file + if (overpass.geojson) { + var blob = new Blob([geoJSON_str], { + type: "application/json;charset=utf-8" + }); + saveAs(blob, "export.geojson"); + } else { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + d.dialog({ + modal: true, + width: 500, + buttons: dialog_buttons + }); + $(".message", d).text(geoJSON_str); } + return false; }); - if (gpx_str[1] !== '?') - gpx_str = '\n' + gpx_str; - } - // make content downloadable as file - if (geojson) { - var blob = new Blob([gpx_str], {type: "application/xml;charset=utf-8"}); - saveAs(blob, "export.gpx"); - } else { - var d = $("#export-gpx-dialog"); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - d.dialog({ - modal:true, - width:500, - buttons: dialog_buttons, - }); - $(".message",d).text(gpx_str); - } - return false; - }); - $("#export-dialog a#export-KML").unbind("click").on("click", function() { - var geojson = overpass.geojson && JSON.parse(constructGeojsonString(overpass.geojson)); - if (!geojson) - kml_str = i18n.t("export.KML.no_data"); - else { - var kml_str = tokml(geojson, { - documentName: "overpass-turbo.eu export", - documentDescription: "Filtered OSM data converted to KML by overpass turbo.\n"+ - "Copyright: "+overpass.copyright+"\n"+ - "Timestamp: "+overpass.timestamp, - name: "name", - description: "description" - }); - } - // make content downloadable as file - if (geojson) { - var blob = new Blob([kml_str], {type: "application/xml;charset=utf-8"}); - saveAs(blob, "export.kml"); - } else { - var d = $("#export-kml-dialog"); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - d.dialog({ - modal:true, - width:500, - buttons: dialog_buttons, + $("#export-dialog a#export-geoJSON-gist") + .unbind("click") + .on("click", function() { + var geoJSON_str = constructGeojsonString(overpass.geojson); + $.ajax("https://api.github.com/gists", { + method: "POST", + data: JSON.stringify({ + description: "data exported by overpass turbo", // todo:descr + public: true, + files: { + "overpass.geojson": { + // todo:name + content: geoJSON_str + } + } + }) + }) + .success(function(data, textStatus, jqXHR) { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.done")] = function() { + $(this).dialog("close"); + }; + $( + '
    ' + + "

    " + + i18n.t("export.geoJSON_gist.gist") + + ' ' + + data.id + + "

    " + + "

    " + + i18n.t("export.geoJSON_gist.geojsonio") + + ' ' + + i18n.t("export.geoJSON_gist.geojsonio_link") + + "

    " + + "
    " + ).dialog({ + modal: true, + buttons: dialog_buttons + }); + // data.html_url; + }) + .error(function(jqXHR, textStatus, errorStr) { + alert( + "an error occured during the creation of the overpass gist:\n" + + JSON.stringify(jqXHR) + ); + }); + return false; }); - $(".message",d).text(kml_str); - } - return false; - }); - $("#export-dialog a#export-raw").unbind("click").on("click", function() { - var raw_str, raw_type; - var geojson = overpass.geojson; - if (!geojson) - raw_str = i18n.t("export.raw.no_data"); - else { - var data = overpass.data; - if (data instanceof XMLDocument) { - raw_str = (new XMLSerializer()).serializeToString(data); - raw_type = raw_str.match(/

    '+i18n.t("warning.incomplete.remote.expl.1")+'

    '+i18n.t("warning.incomplete.remote.expl.2")+'

    ').dialog({ - modal:true, - buttons: dialog_buttons, - }); + } + // make content downloadable as file + if (geojson) { + var blob = new Blob([kml_str], { + type: "application/xml;charset=utf-8" + }); + saveAs(blob, "export.kml"); + } else { + var d = $("#export-kml-dialog"); + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + d.dialog({ + modal: true, + width: 500, + buttons: dialog_buttons + }); + $(".message", d).text(kml_str); + } return false; }); - } - // * JOSM - $("#export-dialog a#export-editors-josm").unbind("click").on("click", function() { - var export_dialog = $(this).parents("div.ui-dialog-content").first(); - var send_to_josm = function(query) { - var JRC_url="http://127.0.0.1:8111/"; - if (location.protocol === "https:") JRC_url = "https://127.0.0.1:8112/" - $.getJSON(JRC_url+"version") - .success(function(d,s,xhr) { - if (d.protocolversion.major == 1) { - $.get(JRC_url+"import", { - url: - // JOSM doesn't handle protocol-less links very well - server.replace(/^\/\//,location.protocol+"//")+ - "interpreter?data="+ - encodeURIComponent(query), - }).error(function(xhr,s,e) { - alert("Error: Unexpected JOSM remote control error."); - }).success(function(d,s,xhr) { - console.log("successfully invoked JOSM remote constrol"); + $("#export-dialog a#export-raw").unbind("click").on("click", function() { + var raw_str, raw_type; + var geojson = overpass.geojson; + if (!geojson) raw_str = i18n.t("export.raw.no_data"); + else { + var data = overpass.data; + if (data instanceof XMLDocument) { + raw_str = new XMLSerializer().serializeToString(data); + raw_type = raw_str.match(/

    '+i18n.t("error.remote.incompat")+': '+d.protocolversion.major+"."+d.protocolversion.minor+" :(

    ").dialog({ - modal:true, - width:350, - buttons: dialog_buttons, + var blob = new Blob([raw_str], { + type: "application/octet-stream;charset=utf-8" }); + saveAs(blob, "export.dat"); } - }).error(function(xhr,s,e) { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.dismiss")] = function() {$(this).dialog("close");}; - $('

    '+i18n.t("error.remote.not_found")+'

    ').dialog({ - modal:true, - width:350, - buttons: dialog_buttons, + } else { + var d = $("#export-raw-dialog"); + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + d.dialog({ + modal: true, + width: 500, + buttons: dialog_buttons }); - }); - } - // first check for possible mistakes in query. - var valid = Autorepair.detect.editors(ide.getRawQuery(), ide.getQueryLang()); - if (valid) { - // now send the query to JOSM via remote control - send_to_josm(query); + $(".message", d).text(raw_str); + } return false; + }); + $("#export-dialog a#export-convert-xml")[0].href = + server + "convert?data=" + encodeURIComponent(query) + "&target=xml"; + $("#export-dialog a#export-convert-ql")[0].href = + server + "convert?data=" + encodeURIComponent(query) + "&target=mapql"; + $("#export-dialog a#export-convert-compact")[0].href = + server + + "convert?data=" + + encodeURIComponent(query) + + "&target=compact"; + + // OSM editors + // first check for possible mistakes in query. + var validEditorQuery = Autorepair.detect.editors( + ide.getRawQuery(), + ide.getQueryLang() + ); + // * Level0 + var exportToLevel0 = $("#export-dialog a#export-editors-level0"); + exportToLevel0.unbind("click"); + function constructLevel0Link(query) { + return ( + "http://level0.osmz.ru/?url=" + + encodeURIComponent( + server + "interpreter?data=" + encodeURIComponent(query) + ) + ); + } + if (validEditorQuery) { + exportToLevel0[0].href = constructLevel0Link(query); } else { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.repair_query")] = function() { - ide.repairQuery("xml+metadata"); - var message_dialog = $(this); - ide.getQuery(function(query) { - send_to_josm(query); - message_dialog.dialog("close"); - export_dialog.dialog("close"); + exportToLevel0[0].href = ""; + exportToLevel0.bind("click", function() { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.repair_query")] = function() { + ide.repairQuery("xml+metadata"); + var message_dialog = $(this); + ide.getQuery(function(query) { + exportToLevel0.unbind("click"); + exportToLevel0[0].href = constructLevel0Link(query); + message_dialog.dialog("close"); + }); + }; + dialog_buttons[i18n.t("dialog.continue_anyway")] = function() { + exportToLevel0.unbind("click"); + exportToLevel0[0].href = constructLevel0Link(query); + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("warning.incomplete.remote.expl.1") + + "

    " + + i18n.t("warning.incomplete.remote.expl.2") + + "

    " + ).dialog({ + modal: true, + buttons: dialog_buttons }); - }; - dialog_buttons[i18n.t("dialog.continue_anyway")] = function() { - send_to_josm(query); - $(this).dialog("close"); - export_dialog.dialog("close"); - }; - $('

    '+i18n.t("warning.incomplete.remote.expl.1")+'

    '+i18n.t("warning.incomplete.remote.expl.2")+'

    ').dialog({ - modal:true, - buttons: dialog_buttons, + return false; }); - return false; } + // * JOSM + $("#export-dialog a#export-editors-josm") + .unbind("click") + .on("click", function() { + var export_dialog = $(this).parents("div.ui-dialog-content").first(); + var send_to_josm = function(query) { + var JRC_url = "http://127.0.0.1:8111/"; + if (location.protocol === "https:") + JRC_url = "https://127.0.0.1:8112/"; + $.getJSON(JRC_url + "version") + .success(function(d, s, xhr) { + if (d.protocolversion.major == 1) { + $.get(JRC_url + "import", { + // JOSM doesn't handle protocol-less links very well + url: server.replace(/^\/\//, location.protocol + "//") + + "interpreter?data=" + + encodeURIComponent(query) + }) + .error(function(xhr, s, e) { + alert("Error: Unexpected JOSM remote control error."); + }) + .success(function(d, s, xhr) { + console.log("successfully invoked JOSM remote constrol"); + }); + } else { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.remote.incompat") + + ": " + + d.protocolversion.major + + "." + + d.protocolversion.minor + + " :(

    " + ).dialog({ + modal: true, + width: 350, + buttons: dialog_buttons + }); + } + }) + .error(function(xhr, s, e) { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.dismiss")] = function() { + $(this).dialog("close"); + }; + $( + '

    ' + + i18n.t("error.remote.not_found") + + "

    " + ).dialog({ + modal: true, + width: 350, + buttons: dialog_buttons + }); + }); + }; + // first check for possible mistakes in query. + var valid = Autorepair.detect.editors( + ide.getRawQuery(), + ide.getQueryLang() + ); + if (valid) { + // now send the query to JOSM via remote control + send_to_josm(query); + return false; + } else { + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.repair_query")] = function() { + ide.repairQuery("xml+metadata"); + var message_dialog = $(this); + ide.getQuery(function(query) { + send_to_josm(query); + message_dialog.dialog("close"); + export_dialog.dialog("close"); + }); + }; + dialog_buttons[i18n.t("dialog.continue_anyway")] = function() { + send_to_josm(query); + $(this).dialog("close"); + export_dialog.dialog("close"); + }; + $( + '

    ' + + i18n.t("warning.incomplete.remote.expl.1") + + "

    " + + i18n.t("warning.incomplete.remote.expl.2") + + "

    " + ).dialog({ + modal: true, + buttons: dialog_buttons + }); + return false; + } + }); + // open the export dialog + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.done")] = function() { + $(this).dialog("close"); + }; + $("#export-dialog").dialog({ + modal: true, + width: 350, + buttons: dialog_buttons + }); + $("#export-dialog").accordion(); }); - // open the export dialog - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.done")] = function() {$(this).dialog("close");}; - $("#export-dialog").dialog({ - modal:true, - width:350, - buttons: dialog_buttons, - }); - $("#export-dialog").accordion(); - }); - } + }; this.onExportImageClick = function() { ide.waiter.open(i18n.t("waiter.export_as_image")); // 1. render canvas from map tiles @@ -1582,143 +2150,212 @@ var ide = new(function() { useCORS: true, allowTaint: false, proxy: configs.html2canvas_use_proxy ? "/html2canvas_proxy/" : undefined, // use own proxy if necessary and available - onrendered: function(canvas) { - if (settings.export_image_attribution) attribControl.removeFrom(ide.map); - if (!settings.export_image_scale) scaleControl.addTo(ide.map); - if (settings.show_data_stats) - $("#data_stats").show(); - $("#map .leaflet-control-container .leaflet-top").show(); - ide.waiter.addInfo("rendering map data"); - // 2. render overlay data onto canvas - canvas.id = "render_canvas"; - var ctx = canvas.getContext("2d"); - // get geometry for svg rendering - var height = $("#map .leaflet-overlay-pane svg").height(); - var width = $("#map .leaflet-overlay-pane svg").width(); - var tmp = $("#map .leaflet-map-pane")[0].style.cssText.match(/.*?(-?\d+)px.*?(-?\d+)px.*/); - var offx = +tmp[1]; - var offy = +tmp[2]; - if ($("#map .leaflet-overlay-pane").html().length > 0) - ctx.drawSvg($("#map .leaflet-overlay-pane").html(),offx,offy,width,height); - ide.waiter.addInfo("converting to png image"); - // 3. export canvas as html image - var imgstr = canvas.toDataURL("image/png"); - var attrib_message = ""; - if (!settings.export_image_attribution) - attrib_message = '

    Make sure to include proper attributions when distributing this image!

    '; - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.done")] = function() { - $(this).dialog("close"); - // free dialog from DOM - $("#export_image_dialog").remove(); - }; - $('

    '+i18n.t(

    '+attrib_message+'
    ').dialog({ - modal:true, - width:500, - position:["center",60], - open: function() { - // close progress indicator - ide.waiter.close(); - }, - buttons: dialog_buttons, - }); - canvas.toBlob(function(blob) { - saveAs(blob, "export.png"); - }); - }}); - } + onrendered: function(canvas) { + if (settings.export_image_attribution) + attribControl.removeFrom(ide.map); + if (!settings.export_image_scale) scaleControl.addTo(ide.map); + if (settings.show_data_stats) $("#data_stats").show(); + $("#map .leaflet-control-container .leaflet-top").show(); + ide.waiter.addInfo("rendering map data"); + // 2. render overlay data onto canvas + canvas.id = "render_canvas"; + var ctx = canvas.getContext("2d"); + // get geometry for svg rendering + var height = $("#map .leaflet-overlay-pane svg").height(); + var width = $("#map .leaflet-overlay-pane svg").width(); + var tmp = $("#map .leaflet-map-pane")[0].style.cssText.match( + /.*?(-?\d+)px.*?(-?\d+)px.*/ + ); + var offx = +tmp[1]; + var offy = +tmp[2]; + if ($("#map .leaflet-overlay-pane").html().length > 0) + ctx.drawSvg( + $("#map .leaflet-overlay-pane").html(), + offx, + offy, + width, + height + ); + ide.waiter.addInfo("converting to png image"); + // 3. export canvas as html image + var imgstr = canvas.toDataURL("image/png"); + var attrib_message = ""; + if (!settings.export_image_attribution) + attrib_message = + '

    Make sure to include proper attributions when distributing this image!

    '; + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.done")] = function() { + $(this).dialog("close"); + // free dialog from DOM + $("#export_image_dialog").remove(); + }; + $( + '

    ' +
+            i18n.t(

    " + + attrib_message + + "
    " + ).dialog({ + modal: true, + width: 500, + position: ["center", 60], + open: function() { + // close progress indicator + ide.waiter.close(); + }, + buttons: dialog_buttons + }); + canvas.toBlob(function(blob) { + saveAs(blob, "export.png"); + }); + } + }); + }; this.onFfsClick = function() { $("#ffs-dialog #ffs-dialog-parse-error").hide(); $("#ffs-dialog #ffs-dialog-typo").hide(); var build_query = function(autorun) { // build query and run it immediately - ide.update_ffs_query(undefined, function(err, ffs_result) { - if (!err) { - $(this).dialog("close"); - if (autorun !== false) - ide.onRunClick(); - } else { - if (_.isArray(ffs_result)) { - // show parse error message - $("#ffs-dialog #ffs-dialog-parse-error").hide(); - $("#ffs-dialog #ffs-dialog-typo").show(); - var correction = ffs_result.join(""); - var correction_html = ffs_result.map(function(ffs_result_part,i) { - if (i%2===1) - return ""+ffs_result_part+""; - else - return ffs_result_part; - }).join(""); - $("#ffs-dialog #ffs-dialog-typo-correction").html(correction_html); - $("#ffs-dialog #ffs-dialog-typo-correction").unbind("click").bind("click", function(e) { - $("#ffs-dialog input[type=text]").val(correction); - $(this).parent().hide(); - e.preventDefault(); - }); - $("#ffs-dialog #ffs-dialog-typo").effect("shake", {direction:"right",distance:10,times:2}, 300); + ide.update_ffs_query( + undefined, + function(err, ffs_result) { + if (!err) { + $(this).dialog("close"); + if (autorun !== false) ide.onRunClick(); } else { - // show parse error message - $("#ffs-dialog #ffs-dialog-typo").hide(); - $("#ffs-dialog #ffs-dialog-parse-error").show(); - $("#ffs-dialog #ffs-dialog-parse-error").effect("shake", {direction:"right",distance:10,times:2}, 300); + if (_.isArray(ffs_result)) { + // show parse error message + $("#ffs-dialog #ffs-dialog-parse-error").hide(); + $("#ffs-dialog #ffs-dialog-typo").show(); + var correction = ffs_result.join(""); + var correction_html = ffs_result + .map(function(ffs_result_part, i) { + if (i % 2 === 1) return "" + ffs_result_part + ""; + else return ffs_result_part; + }) + .join(""); + $("#ffs-dialog #ffs-dialog-typo-correction").html( + correction_html + ); + $("#ffs-dialog #ffs-dialog-typo-correction") + .unbind("click") + .bind("click", function(e) { + $("#ffs-dialog input[type=text]").val(correction); + $(this).parent().hide(); + e.preventDefault(); + }); + $("#ffs-dialog #ffs-dialog-typo").effect( + "shake", + {direction: "right", distance: 10, times: 2}, + 300 + ); + } else { + // show parse error message + $("#ffs-dialog #ffs-dialog-typo").hide(); + $("#ffs-dialog #ffs-dialog-parse-error").show(); + $("#ffs-dialog #ffs-dialog-parse-error").effect( + "shake", + {direction: "right", distance: 10, times: 2}, + 300 + ); + } } + }.bind(this) + ); + }; + $("#ffs-dialog input[type=text]") + .unbind("keypress") + .bind("keypress", function(e) { + if (e.which == 13 || e.which == 10) { + build_query.bind(this.parentElement.parentElement)(); + e.preventDefault(); } - }.bind(this)); + }); + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.wizard_build")] = function() { + build_query.bind(this, false)(); }; - $("#ffs-dialog input[type=text]").unbind("keypress").bind("keypress", function(e) { - if (e.which==13 || e.which==10) { - build_query.bind(this.parentElement.parentElement)(); - e.preventDefault(); - } - }); - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.wizard_build")] = function() { build_query.bind(this, false)(); } dialog_buttons[i18n.t("dialog.wizard_run")] = build_query; - dialog_buttons[i18n.t("dialog.cancel")] = function() {$(this).dialog("close");}; + dialog_buttons[i18n.t("dialog.cancel")] = function() { + $(this).dialog("close"); + }; $("#ffs-dialog").dialog({ - modal:true, - minWidth:350, - buttons: dialog_buttons, + modal: true, + minWidth: 350, + buttons: dialog_buttons }); - } + }; this.onSettingsClick = function() { - $("#settings-dialog input[name=ui_language]")[0].value = settings.ui_language; + $("#settings-dialog input[name=ui_language]")[0].value = + settings.ui_language; var lngDescs = i18n.getSupportedLanguagesDescriptions(); make_combobox( $("#settings-dialog input[name=ui_language]"), - (["auto"].concat(i18n.getSupportedLanguages())).map(function(lng) { + ["auto"].concat(i18n.getSupportedLanguages()).map(function(lng) { return { value: lng, - label: lng=="auto" ? "auto" : lng+' - '+lngDescs[lng] - } + label: lng == "auto" ? "auto" : lng + " - " + lngDescs[lng] + }; }) ); $("#settings-dialog input[name=server]")[0].value = settings.server; - make_combobox($("#settings-dialog input[name=server]"), configs.suggestedServers); - $("#settings-dialog input[name=force_simple_cors_request]")[0].checked = settings.force_simple_cors_request; - $("#settings-dialog input[name=no_autorepair]")[0].checked = settings.no_autorepair; + make_combobox( + $("#settings-dialog input[name=server]"), + configs.suggestedServers + ); + $("#settings-dialog input[name=force_simple_cors_request]")[0].checked = + settings.force_simple_cors_request; + $("#settings-dialog input[name=no_autorepair]")[0].checked = + settings.no_autorepair; // editor options - $("#settings-dialog input[name=use_rich_editor]")[0].checked = settings.use_rich_editor; - $("#settings-dialog input[name=editor_width]")[0].value = settings.editor_width; + $("#settings-dialog input[name=use_rich_editor]")[0].checked = + settings.use_rich_editor; + $("#settings-dialog input[name=editor_width]")[0].value = + settings.editor_width; // sharing options - $("#settings-dialog input[name=share_include_pos]")[0].checked = settings.share_include_pos; - $("#settings-dialog input[name=share_compression]")[0].value = settings.share_compression; - make_combobox($("#settings-dialog input[name=share_compression]"),["auto","on","off"]); + $("#settings-dialog input[name=share_include_pos]")[0].checked = + settings.share_include_pos; + $("#settings-dialog input[name=share_compression]")[0].value = + settings.share_compression; + make_combobox($("#settings-dialog input[name=share_compression]"), [ + "auto", + "on", + "off" + ]); // map settings - $("#settings-dialog input[name=tile_server]")[0].value = settings.tile_server; - make_combobox($("#settings-dialog input[name=tile_server]"), configs.suggestedTiles); - $("#settings-dialog input[name=background_opacity]")[0].value = settings.background_opacity; - $("#settings-dialog input[name=enable_crosshairs]")[0].checked = settings.enable_crosshairs; - $("#settings-dialog input[name=disable_poiomatic]")[0].checked = settings.disable_poiomatic; - $("#settings-dialog input[name=show_data_stats]")[0].checked = settings.show_data_stats; + $("#settings-dialog input[name=tile_server]")[0].value = + settings.tile_server; + make_combobox( + $("#settings-dialog input[name=tile_server]"), + configs.suggestedTiles + ); + $("#settings-dialog input[name=background_opacity]")[0].value = + settings.background_opacity; + $("#settings-dialog input[name=enable_crosshairs]")[0].checked = + settings.enable_crosshairs; + $("#settings-dialog input[name=disable_poiomatic]")[0].checked = + settings.disable_poiomatic; + $("#settings-dialog input[name=show_data_stats]")[0].checked = + settings.show_data_stats; // export settings - $("#settings-dialog input[name=export_image_scale]")[0].checked = settings.export_image_scale; - $("#settings-dialog input[name=export_image_attribution]")[0].checked = settings.export_image_attribution; + $("#settings-dialog input[name=export_image_scale]")[0].checked = + settings.export_image_scale; + $("#settings-dialog input[name=export_image_attribution]")[0].checked = + settings.export_image_attribution; // open dialog - var dialog_buttons= {}; + var dialog_buttons = {}; dialog_buttons[i18n.t("dialog.save")] = function() { // save settings - var new_ui_language = $("#settings-dialog input[name=ui_language]")[0].value; + var new_ui_language = $("#settings-dialog input[name=ui_language]")[0] + .value; // reload ui if language has been changed if (settings.ui_language != new_ui_language) { i18n.translate(new_ui_language); @@ -1727,82 +2364,143 @@ var ide = new(function() { } settings.ui_language = new_ui_language; settings.server = $("#settings-dialog input[name=server]")[0].value; - settings.force_simple_cors_request = $("#settings-dialog input[name=force_simple_cors_request]")[0].checked; - settings.no_autorepair = $("#settings-dialog input[name=no_autorepair]")[0].checked; - settings.use_rich_editor = $("#settings-dialog input[name=use_rich_editor]")[0].checked; + settings.force_simple_cors_request = $( + "#settings-dialog input[name=force_simple_cors_request]" + )[0].checked; + settings.no_autorepair = $( + "#settings-dialog input[name=no_autorepair]" + )[0].checked; + settings.use_rich_editor = $( + "#settings-dialog input[name=use_rich_editor]" + )[0].checked; var prev_editor_width = settings.editor_width; - settings.editor_width = $("#settings-dialog input[name=editor_width]")[0].value; + settings.editor_width = $( + "#settings-dialog input[name=editor_width]" + )[0].value; // update editor width (if changed) if (prev_editor_width != settings.editor_width) { - $("#editor").css("width",settings.editor_width); - $("#dataviewer").css("left",settings.editor_width); + $("#editor").css("width", settings.editor_width); + $("#dataviewer").css("left", settings.editor_width); } - settings.share_include_pos = $("#settings-dialog input[name=share_include_pos]")[0].checked; - settings.share_compression = $("#settings-dialog input[name=share_compression]")[0].value; + settings.share_include_pos = $( + "#settings-dialog input[name=share_include_pos]" + )[0].checked; + settings.share_compression = $( + "#settings-dialog input[name=share_compression]" + )[0].value; var prev_tile_server = settings.tile_server; - settings.tile_server = $("#settings-dialog input[name=tile_server]")[0].value; + settings.tile_server = $( + "#settings-dialog input[name=tile_server]" + )[0].value; // update tile layer (if changed) if (prev_tile_server != settings.tile_server) ide.map.tile_layer.setUrl(settings.tile_server); var prev_background_opacity = settings.background_opacity; - settings.background_opacity = +$("#settings-dialog input[name=background_opacity]")[0].value; + settings.background_opacity = +$( + "#settings-dialog input[name=background_opacity]" + )[0].value; // update background opacity layer if (settings.background_opacity != prev_background_opacity) if (settings.background_opacity == 1) ide.map.removeLayer(ide.map.inv_opacity_layer); else - ide.map.inv_opacity_layer.setOpacity(1-settings.background_opacity).addTo(ide.map); - settings.enable_crosshairs = $("#settings-dialog input[name=enable_crosshairs]")[0].checked; - settings.disable_poiomatic = $("#settings-dialog input[name=disable_poiomatic]")[0].checked; - settings.show_data_stats = $("#settings-dialog input[name=show_data_stats]")[0].checked; + ide.map.inv_opacity_layer + .setOpacity(1 - settings.background_opacity) + .addTo(ide.map); + settings.enable_crosshairs = $( + "#settings-dialog input[name=enable_crosshairs]" + )[0].checked; + settings.disable_poiomatic = $( + "#settings-dialog input[name=disable_poiomatic]" + )[0].checked; + settings.show_data_stats = $( + "#settings-dialog input[name=show_data_stats]" + )[0].checked; $(".crosshairs").toggle(settings.enable_crosshairs); // show/hide crosshairs - settings.export_image_scale = $("#settings-dialog input[name=export_image_scale]")[0].checked; - settings.export_image_attribution = $("#settings-dialog input[name=export_image_attribution]")[0].checked; + settings.export_image_scale = $( + "#settings-dialog input[name=export_image_scale]" + )[0].checked; + settings.export_image_attribution = $( + "#settings-dialog input[name=export_image_attribution]" + )[0].checked; settings.save(); $(this).dialog("close"); }; $("#settings-dialog").dialog({ - modal:true, - width:400, - buttons: dialog_buttons, + modal: true, + width: 400, + buttons: dialog_buttons }); $("#settings-dialog").accordion(); - } + }; this.onHelpClick = function() { - var dialog_buttons= {}; - dialog_buttons[i18n.t("dialog.close")] = function() {$(this).dialog("close");}; + var dialog_buttons = {}; + dialog_buttons[i18n.t("dialog.close")] = function() { + $(this).dialog("close"); + }; $("#help-dialog").dialog({ - modal:false, - width:450, - buttons: dialog_buttons, + modal: false, + width: 450, + buttons: dialog_buttons }); $("#help-dialog").accordion({heightStyle: "content"}); - } + }; this.onKeyPress = function(event) { - if ((event.which == 120 && event.charCode == 0) || // F9 - ((event.which == 13 || event.which == 10) && (event.ctrlKey || event.metaKey))) { // Ctrl+Enter + if ( + (event.which == 120 && event.charCode == 0) || // F9 + ((event.which == 13 || event.which == 10) && + (event.ctrlKey || event.metaKey)) + ) { + // Ctrl+Enter ide.onRunClick(); // run query event.preventDefault(); } - if ((String.fromCharCode(event.which).toLowerCase() == 's') && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey ) { // Ctrl+S + if ( + String.fromCharCode(event.which).toLowerCase() == "s" && + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey + ) { + // Ctrl+S ide.onSaveClick(); event.preventDefault(); } - if ((String.fromCharCode(event.which).toLowerCase() == 'o') && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey ) { // Ctrl+O + if ( + String.fromCharCode(event.which).toLowerCase() == "o" && + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey + ) { + // Ctrl+O ide.onLoadClick(); event.preventDefault(); } - if ((String.fromCharCode(event.which).toLowerCase() == 'h') && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey ) { // Ctrl+H + if ( + String.fromCharCode(event.which).toLowerCase() == "h" && + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey + ) { + // Ctrl+H ide.onHelpClick(); event.preventDefault(); } - if (((String.fromCharCode(event.which).toLowerCase() == 'i') && (event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey ) || // Ctrl+I - ((String.fromCharCode(event.which).toLowerCase() == 'f') && (event.ctrlKey || event.metaKey) && event.shiftKey && !event.altKey ) ) { // Ctrl+Shift+F + if ( + (String.fromCharCode(event.which).toLowerCase() == "i" && + (event.ctrlKey || event.metaKey) && + !event.shiftKey && + !event.altKey) || // Ctrl+I + (String.fromCharCode(event.which).toLowerCase() == "f" && + (event.ctrlKey || event.metaKey) && + event.shiftKey && + !event.altKey) + ) { + // Ctrl+Shift+F ide.onFfsClick(); event.preventDefault(); } // todo: more shortcuts - } + }; this.update_map = function() { ide.waiter.open(i18n.t("waiter.processing_query")); ide.waiter.addInfo("resetting map"); @@ -1819,33 +2517,47 @@ var ide = new(function() { // run the query via the overpass object ide.getQuery(function(query) { var query_lang = ide.getQueryLang(); - var server = (ide.data_source && - ide.data_source.mode == "overpass" && - ide.data_source.options.server) ? - ide.data_source.options.server : settings.server; - overpass.run_query(query, query_lang, undefined, undefined, server, ide.mapcss); + var server = ide.data_source && + ide.data_source.mode == "overpass" && + ide.data_source.options.server + ? ide.data_source.options.server + : settings.server; + overpass.run_query( + query, + query_lang, + undefined, + undefined, + server, + ide.mapcss + ); }); - } + }; this.update_ffs_query = function(s, callback) { var search = s || $("#ffs-dialog input[type=text]").val(); - ffs.construct_query(search, undefined, function(err, query) { - if (err) { - ffs.repair_search(search, function(repaired) { - if (repaired) { - callback("repairable query", repaired); - } else { - if (s) return callback(true); - // try to parse as generic ffs search - this.update_ffs_query('"'+search+'"', callback); - } - }.bind(this)); - } else { - ide.setQuery(query); - callback(null); - } - }.bind(this)); - } - -})(); // end create ide object + ffs.construct_query( + search, + undefined, + function(err, query) { + if (err) { + ffs.repair_search( + search, + function(repaired) { + if (repaired) { + callback("repairable query", repaired); + } else { + if (s) return callback(true); + // try to parse as generic ffs search + this.update_ffs_query('"' + search + '"', callback); + } + }.bind(this) + ); + } else { + ide.setQuery(query); + callback(null); + } + }.bind(this) + ); + }; +}(); // end create ide object export default ide; diff --git a/js/index.js b/js/index.js index 9e4132b8..ef90cbeb 100644 --- a/js/index.js +++ b/js/index.js @@ -1,36 +1,36 @@ -import $ from 'jquery'; -import 'jquery-ui'; +import $ from "jquery"; +import "jquery-ui"; -import 'leaflet'; -import 'leaflet-polylineoffset'; -import 'leaflet.locationfilter'; +import "leaflet"; +import "leaflet-polylineoffset"; +import "leaflet.locationfilter"; -import 'codemirror/lib/codemirror'; -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/mode/xml/xml'; -import 'codemirror/mode/clike/clike'; -import 'codemirror/mode/css/css'; -import 'codemirror/lib/util/multiplex'; -import 'codemirror/lib/util/closetag'; +import "codemirror/lib/codemirror"; +import "codemirror/mode/javascript/javascript"; +import "codemirror/mode/xml/xml"; +import "codemirror/mode/clike/clike"; +import "codemirror/mode/css/css"; +import "codemirror/lib/util/multiplex"; +import "codemirror/lib/util/closetag"; -import 'html2canvas'; +import "html2canvas"; // include the CSS files -import 'codemirror/lib/codemirror.css'; -import 'leaflet/dist/leaflet.css'; -import 'leaflet.locationfilter/src/locationfilter.css'; -import 'jquery-ui/themes/base/jquery-ui.css'; -import '../css/default.css'; -import '../css/compact.css'; +import "codemirror/lib/codemirror.css"; +import "leaflet/dist/leaflet.css"; +import "leaflet.locationfilter/src/locationfilter.css"; +import "jquery-ui/themes/base/jquery-ui.css"; +import "../css/default.css"; +import "../css/compact.css"; // initialize ide on document ready -import ide from './ide'; +import ide from "./ide"; $(document).ready(ide.init); $(document).ready(initClickHandler); function initClickHandler() { - $('*[data-ide-handler]').each(function() { - var handlerDefinition = $(this).attr('data-ide-handler').split(/:/); + $("*[data-ide-handler]").each(function() { + var handlerDefinition = $(this).attr("data-ide-handler").split(/:/); var event = handlerDefinition[0]; var handlerName = handlerDefinition[1]; var handler = ide[handlerName]; diff --git a/js/jsmapcss/Condition.js b/js/jsmapcss/Condition.js index 2c26235f..397b8e21 100644 --- a/js/jsmapcss/Condition.js +++ b/js/jsmapcss/Condition.js @@ -1,41 +1,52 @@ // ---------------------------------------------------------------------- // Condition base class -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.Condition = function() {}; styleparser.Condition.prototype = { - type: '', // eq/ne/regex etc. - params: [], // what to test against + type: "", // eq/ne/regex etc. + params: [], // what to test against - init:function(_type) { - // summary: A condition to evaluate. - this.type =_type; - this.params=Array.prototype.slice.call(arguments,1); - return this; - }, + init: function(_type) { + // summary: A condition to evaluate. + this.type = _type; + this.params = Array.prototype.slice.call(arguments, 1); + return this; + }, - test:function(tags) { - // summary: Run the condition against the supplied tags. - var p=this.params; - switch (this.type) { - case 'eq': return (tags[p[0]]==p[1]); - case 'ne': return (tags[p[0]]!=p[1]); - case 'regex': var r=new RegExp(p[1],"i"); - return (r.test(tags[p[0]])); - case 'true': return (tags[p[0]]=='true' || tags[p[0]]=='yes' || tags[p[0]]=='1'); - case 'false': return (tags[p[0]]=='false' || tags[p[0]]=='no' || tags[p[0]]=='0'); - case 'set': return (tags[p[0]] !== undefined && tags[p[0]]!==''); - case 'unset': return (tags[p[0]] === undefined || tags[p[0]]===''); - case '<': return (Number(tags[p[0]])< Number(p[1])); - case '<=': return (Number(tags[p[0]])<=Number(p[1])); - case '>': return (Number(tags[p[0]])> Number(p[1])); - case '>=': return (Number(tags[p[0]])>=Number(p[1])); - } - return false; - }, + test: function(tags) { + // summary: Run the condition against the supplied tags. + var p = this.params; + switch (this.type) { + case "eq": + return tags[p[0]] == p[1]; + case "ne": + return tags[p[0]] != p[1]; + case "regex": + var r = new RegExp(p[1], "i"); + return r.test(tags[p[0]]); + case "true": + return tags[p[0]] == "true" || tags[p[0]] == "yes" || tags[p[0]] == "1"; + case "false": + return tags[p[0]] == "false" || tags[p[0]] == "no" || tags[p[0]] == "0"; + case "set": + return tags[p[0]] !== undefined && tags[p[0]] !== ""; + case "unset": + return tags[p[0]] === undefined || tags[p[0]] === ""; + case "<": + return Number(tags[p[0]]) < Number(p[1]); + case "<=": + return Number(tags[p[0]]) <= Number(p[1]); + case ">": + return Number(tags[p[0]]) > Number(p[1]); + case ">=": + return Number(tags[p[0]]) >= Number(p[1]); + } + return false; + }, - toString:function() { - return "["+this.type+": "+this.params+"]"; - } + toString: function() { + return "[" + this.type + ": " + this.params + "]"; + } }; diff --git a/js/jsmapcss/Rule.js b/js/jsmapcss/Rule.js index 0543116e..b1ec9ec6 100644 --- a/js/jsmapcss/Rule.js +++ b/js/jsmapcss/Rule.js @@ -1,50 +1,65 @@ // ---------------------------------------------------------------------- // Rule class -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.Rule = function() {}; styleparser.Rule.prototype = { + conditions: [], // the Conditions to be evaluated for the Rule to be fulfilled + isAnd: true, // do all Conditions need to be true for the Rule to be fulfilled? (Always =true for MapCSS) + minZoom: 0, // minimum zoom level at which the Rule is fulfilled + maxZoom: 255, // maximum zoom level at which the Rule is fulfilled + subject: "", // entity type to which the Rule applies: 'way', 'node', 'relation', 'area' (closed way) or 'line' (unclosed way) - conditions: [], // the Conditions to be evaluated for the Rule to be fulfilled - isAnd: true, // do all Conditions need to be true for the Rule to be fulfilled? (Always =true for MapCSS) - minZoom: 0, // minimum zoom level at which the Rule is fulfilled - maxZoom: 255, // maximum zoom level at which the Rule is fulfilled - subject: '', // entity type to which the Rule applies: 'way', 'node', 'relation', 'area' (closed way) or 'line' (unclosed way) + addSubject: function(_subject) { + // summary: A MapCSS selector. Contains a list of Conditions; the entity type to which the selector applies; + // and the zoom levels at which it is true. way[waterway=river][boat=yes] would be parsed into one Rule. + // The selectors and declaration together form a StyleChooser. + this.subject = _subject; + this.conditions = []; + }, - addSubject: function(_subject) { - // summary: A MapCSS selector. Contains a list of Conditions; the entity type to which the selector applies; - // and the zoom levels at which it is true. way[waterway=river][boat=yes] would be parsed into one Rule. - // The selectors and declaration together form a StyleChooser. - this.subject=_subject; - this.conditions=[]; - }, + addCondition: function(_condition) { + // summary: Add a condition to this rule. + this.conditions.push(_condition); + }, - addCondition: function(_condition) { - // summary: Add a condition to this rule. - this.conditions.push(_condition); - }, - - test: function(entity,tags,zoom) { - // summary: Evaluate the Rule on the given entity, tags and zoom level. - // returns: true if the Rule passes, false if the conditions aren't fulfilled. - if ((this.subject !== '') && !entity.isSubject(this.subject)) { - return false; - } - if (zoomthis.maxZoom) { return false; } + test: function(entity, tags, zoom) { + // summary: Evaluate the Rule on the given entity, tags and zoom level. + // returns: true if the Rule passes, false if the conditions aren't fulfilled. + if (this.subject !== "" && !entity.isSubject(this.subject)) { + return false; + } + if (zoom < this.minZoom || zoom > this.maxZoom) { + return false; + } - var v=true; var i=0; var isAnd=this.isAnd; - this.conditions.forEach(function(condition) { - var r=condition.test(tags); - if (i === 0) { v=r; } - else if (isAnd) { v=v && r; } - else { v = v || r; } - i++; - }); - return v; - }, + var v = true; + var i = 0; + var isAnd = this.isAnd; + this.conditions.forEach(function(condition) { + var r = condition.test(tags); + if (i === 0) { + v = r; + } else if (isAnd) { + v = v && r; + } else { + v = v || r; + } + i++; + }); + return v; + }, - toString:function() { - return this.subject+" z"+this.minZoom+"-"+this.maxZoom+": "+this.conditions; - } + toString: function() { + return ( + this.subject + + " z" + + this.minZoom + + "-" + + this.maxZoom + + ": " + + this.conditions + ); + } }; diff --git a/js/jsmapcss/RuleChain.js b/js/jsmapcss/RuleChain.js index 723468ea..13650dd0 100644 --- a/js/jsmapcss/RuleChain.js +++ b/js/jsmapcss/RuleChain.js @@ -14,59 +14,68 @@ */ -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.RuleChain = function() { - this.rules=[]; // list of Rules - this.subpart= 'default'; // subpart name, as in way[highway=primary]::centreline + this.rules = []; // list of Rules + this.subpart = "default"; // subpart name, as in way[highway=primary]::centreline }; styleparser.RuleChain.prototype = { - // Functions to define the RuleChain - addRule: function(_subject) { - this.rules.push(new styleparser.Rule()); - this.rules[this.rules.length-1].addSubject(_subject); - }, + // Functions to define the RuleChain + addRule: function(_subject) { + this.rules.push(new styleparser.Rule()); + this.rules[this.rules.length - 1].addSubject(_subject); + }, - addConditionToLast:function(_condition) { - this.rules[this.rules.length-1].addCondition(_condition); - }, + addConditionToLast: function(_condition) { + this.rules[this.rules.length - 1].addCondition(_condition); + }, - addZoomToLast:function(z1,z2) { - this.rules[this.rules.length-1].minZoom=z1; - this.rules[this.rules.length-1].maxZoom=z2; - }, + addZoomToLast: function(z1, z2) { + this.rules[this.rules.length - 1].minZoom = z1; + this.rules[this.rules.length - 1].maxZoom = z2; + }, + length: function() { + return this.rules.length; + }, - length:function() { - return this.rules.length; - }, + setSubpart: function(subpart) { + this.subpart = subpart || "default"; + }, - setSubpart:function(subpart) { - this.subpart = subpart || 'default'; - }, + // Test a ruleChain + // - run a set of tests in the chain + // works backwards from at position "pos" in array, or -1 for the last + // separate tags object is required in case they've been dynamically retagged + // - if they fail, return false + // - if they succeed, and it's the last in the chain, return happily + // - if they succeed, and there's more in the chain, rerun this for each parent until success - // Test a ruleChain - // - run a set of tests in the chain - // works backwards from at position "pos" in array, or -1 for the last - // separate tags object is required in case they've been dynamically retagged - // - if they fail, return false - // - if they succeed, and it's the last in the chain, return happily - // - if they succeed, and there's more in the chain, rerun this for each parent until success - - test:function(pos, entity, tags, zoom) { - // summary: Test a rule chain by running all the tests in reverse order. - if (this.rules.length === 0) { return true; } // orig: { return false; } // todo: wildcard selector "*" semms broken... - if (pos==-1) { pos=this.rules.length-1; } + test: function(pos, entity, tags, zoom) { + // summary: Test a rule chain by running all the tests in reverse order. + if (this.rules.length === 0) { + return true; + } // orig: { return false; } // todo: wildcard selector "*" semms broken... + if (pos == -1) { + pos = this.rules.length - 1; + } - var r = this.rules[pos]; - if (!r.test(entity, tags, zoom)) { return false; } - if (pos === 0) { return true; } + var r = this.rules[pos]; + if (!r.test(entity, tags, zoom)) { + return false; + } + if (pos === 0) { + return true; + } - var o = entity.getParentObjects();//TODO//entity.entity.parentObjects(); - for (var i = 0; i < o.length; i++) { - var p=o[i]; - if (this.test(pos-1, p, p.tags, zoom)) { return true; } - } - return false; + var o = entity.getParentObjects(); //TODO//entity.entity.parentObjects(); + for (var i = 0; i < o.length; i++) { + var p = o[i]; + if (this.test(pos - 1, p, p.tags, zoom)) { + return true; + } } + return false; + } }; diff --git a/js/jsmapcss/RuleSet.js b/js/jsmapcss/RuleSet.js index 5f27d80b..3a3efb5a 100644 --- a/js/jsmapcss/RuleSet.js +++ b/js/jsmapcss/RuleSet.js @@ -2,24 +2,23 @@ // needs to cope with nested CSS files // doesn't do untagged nodes optimisation -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.RuleSet = function() {}; styleparser.RuleSet.prototype = { - - choosers: [], // list of StyleChoosers - - getStyles: function(entity, tags, zoom) { - // summary: Find the styles for a given entity. - var sl=new styleparser.StyleList(); - for (var i in this.choosers) { - this.choosers[i].updateStyles(entity, tags, sl, zoom); - } - return sl; // styleparser.StyleList - }, - - /*loadFromCSS: function(url, callback) { + choosers: [], // list of StyleChoosers + + getStyles: function(entity, tags, zoom) { + // summary: Find the styles for a given entity. + var sl = new styleparser.StyleList(); + for (var i in this.choosers) { + this.choosers[i].updateStyles(entity, tags, sl, zoom); + } + return sl; // styleparser.StyleList + }, + + /*loadFromCSS: function(url, callback) { // summary: Load a MapCSS file from a URL, then throw it at the parser when it's loaded. this.callback = callback; $.ajax({ @@ -28,132 +27,178 @@ styleparser.RuleSet.prototype = { }); },*/ - parseCSS: function(css) { - // summary: Parse a CSS document into a set of StyleChoosers. - var previous=0; // what was the previous CSS word? - var sc = new styleparser.StyleChooser(); // currently being assembled - this.choosers = []; - css = css.replace(/[\r\n]/g,""); // strip linebreaks because JavaScript doesn't have the /s modifier - - var o = {}; - while (css.length>0) { - - // CSS comment - if ((o=this.COMMENT.exec(css))) { - css=css.replace(this.COMMENT,''); - - // Whitespace (probably only at beginning of file) - } else if ((o=this.WHITESPACE.exec(css))) { - css=css.replace(this.WHITESPACE,''); - - // Class - .motorway, .builtup, :hover - } else if ((o=this.CLASS.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - - css=css.replace(this.CLASS,''); - sc.currentChain().addConditionToLast((new styleparser.Condition()).init('set',o[1])); - previous=this.oCONDITION; - - // Not class - !.motorway, !.builtup, !:hover - } else if ((o=this.NOT_CLASS.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - - css=css.replace(this.NOT_CLASS,''); - sc.currentChain().addConditionToLast((new styleparser.Condition()).init('unset',o[1])); - previous=this.oCONDITION; - - // Zoom - } else if ((o=this.ZOOM.exec(css))) { - if (previous!=this.oOBJECT && previous!=this.oCONDITION) { sc.currentChain().addRule(); } - - css=css.replace(this.ZOOM,''); - var z=this.parseZoom(o[1]); - sc.currentChain().addZoomToLast(z[0],z[1]); - sc.zoomSpecific=true; - previous=this.oZOOM; - - // Grouping - just a comma - } else if ((o=this.GROUP.exec(css))) { - css=css.replace(this.GROUP,''); - sc.newRuleChain(); - previous=this.oGROUP; - - // Condition - [highway=primary] - } else if ((o=this.CONDITION.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - if (previous!=this.oOBJECT && previous!=this.oZOOM && previous!=this.oCONDITION) { sc.currentChain().addRule(); } - css=css.replace(this.CONDITION,''); - sc.currentChain().addConditionToLast(this.parseCondition(o[1])); - previous=this.oCONDITION; - - // Object - way, node, relation - } else if ((o=this.OBJECT.exec(css))) { - // TODO: raise error if object is none of node|way|relation|line|area|canvas|* ? - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - - css=css.replace(this.OBJECT,''); - sc.currentChain().addRule(o[1]); - previous=this.oOBJECT; - - // Subpart - ::centreline - } else if ((o=this.SUBPART.exec(css))) { - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - css=css.replace(this.SUBPART,''); - sc.currentChain().setSubpart(o[1]); - previous=this.oSUBPART; - - // Declaration - {...} - } else if ((o=this.DECLARATION.exec(css))) { - css=css.replace(this.DECLARATION,''); - sc.addStyles(this.parseDeclaration(o[1])); - previous=this.oDECLARATION; - - // Unknown pattern - } else if ((o=this.UNKNOWN.exec(css))) { - css=css.replace(this.UNKNOWN,''); - // TODO: own error class - throw new Error("Error while parsing MapCSS at \""+o[1]+(css.length>38?css.substr(0,36)+"...":css)+"\""); - // console.log("unknown: "+o[1]); - } else { - // console.log("choked on "+css); - throw new Error("MapCSS parsing choked on "+css); - return; - } + parseCSS: function(css) { + // summary: Parse a CSS document into a set of StyleChoosers. + var previous = 0; // what was the previous CSS word? + var sc = new styleparser.StyleChooser(); // currently being assembled + this.choosers = []; + css = css.replace(/[\r\n]/g, ""); // strip linebreaks because JavaScript doesn't have the /s modifier + + var o = {}; + while (css.length > 0) { + // CSS comment + if ((o = this.COMMENT.exec(css))) { + css = css.replace(this.COMMENT, ""); + + // Whitespace (probably only at beginning of file) + } else if ((o = this.WHITESPACE.exec(css))) { + css = css.replace(this.WHITESPACE, ""); + + // Class - .motorway, .builtup, :hover + } else if ((o = this.CLASS.exec(css))) { + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); + } + + css = css.replace(this.CLASS, ""); + sc + .currentChain() + .addConditionToLast(new styleparser.Condition().init("set", o[1])); + previous = this.oCONDITION; + + // Not class - !.motorway, !.builtup, !:hover + } else if ((o = this.NOT_CLASS.exec(css))) { + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); } - if (previous==this.oDECLARATION) { this.saveChooser(sc); sc=new styleparser.StyleChooser(); } - if (this.callback) { this.callback(); } - }, - - saveChooser:function(sc) { - this.choosers.push(sc); - }, - - parseDeclaration:function(s) { - var styles=[]; - var t={}; - var o={}; - var k, v; - - // Create styles - var ss = new styleparser.ShapeStyle(); - var ps = new styleparser.PointStyle(); - var ts = new styleparser.TextStyle(); - var hs = new styleparser.ShieldStyle(); - var xs = new styleparser.InstructionStyle(); - - var r=s.split(';'); - var isEval={}; - for (var i in r) { - var a=r[i]; - if ((o=this.ASSIGNMENT_EVAL.exec(a))) { k=o[1].replace(this.DASH,'_'); t[k]=o[2]; isEval[k]=true; } - else if ((o=this.ASSIGNMENT.exec(a))) { k=o[1].replace(this.DASH,'_'); t[k]=o[2]; } - else if ((o=this.SET_TAG_EVAL.exec(a))) { } // xs.addSetTag(o[1],this.saveEval(o[2])); - else if ((o=this.SET_TAG.exec(a))) { xs.addSetTag(o[1],o[2]); } - else if ((o=this.SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); } - else if ((o=this.EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); } + + css = css.replace(this.NOT_CLASS, ""); + sc + .currentChain() + .addConditionToLast(new styleparser.Condition().init("unset", o[1])); + previous = this.oCONDITION; + + // Zoom + } else if ((o = this.ZOOM.exec(css))) { + if (previous != this.oOBJECT && previous != this.oCONDITION) { + sc.currentChain().addRule(); } - /*// Find sublayer + css = css.replace(this.ZOOM, ""); + var z = this.parseZoom(o[1]); + sc.currentChain().addZoomToLast(z[0], z[1]); + sc.zoomSpecific = true; + previous = this.oZOOM; + + // Grouping - just a comma + } else if ((o = this.GROUP.exec(css))) { + css = css.replace(this.GROUP, ""); + sc.newRuleChain(); + previous = this.oGROUP; + + // Condition - [highway=primary] + } else if ((o = this.CONDITION.exec(css))) { + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); + } + if ( + previous != this.oOBJECT && + previous != this.oZOOM && + previous != this.oCONDITION + ) { + sc.currentChain().addRule(); + } + css = css.replace(this.CONDITION, ""); + sc.currentChain().addConditionToLast(this.parseCondition(o[1])); + previous = this.oCONDITION; + + // Object - way, node, relation + } else if ((o = this.OBJECT.exec(css))) { + // TODO: raise error if object is none of node|way|relation|line|area|canvas|* ? + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); + } + + css = css.replace(this.OBJECT, ""); + sc.currentChain().addRule(o[1]); + previous = this.oOBJECT; + + // Subpart - ::centreline + } else if ((o = this.SUBPART.exec(css))) { + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); + } + css = css.replace(this.SUBPART, ""); + sc.currentChain().setSubpart(o[1]); + previous = this.oSUBPART; + + // Declaration - {...} + } else if ((o = this.DECLARATION.exec(css))) { + css = css.replace(this.DECLARATION, ""); + sc.addStyles(this.parseDeclaration(o[1])); + previous = this.oDECLARATION; + + // Unknown pattern + } else if ((o = this.UNKNOWN.exec(css))) { + css = css.replace(this.UNKNOWN, ""); + // TODO: own error class + throw new Error( + 'Error while parsing MapCSS at "' + + o[1] + + (css.length > 38 ? css.substr(0, 36) + "..." : css) + + '"' + ); + // console.log("unknown: "+o[1]); + } else { + // console.log("choked on "+css); + throw new Error("MapCSS parsing choked on " + css); + return; + } + } + if (previous == this.oDECLARATION) { + this.saveChooser(sc); + sc = new styleparser.StyleChooser(); + } + if (this.callback) { + this.callback(); + } + }, + + saveChooser: function(sc) { + this.choosers.push(sc); + }, + + parseDeclaration: function(s) { + var styles = []; + var t = {}; + var o = {}; + var k, v; + + // Create styles + var ss = new styleparser.ShapeStyle(); + var ps = new styleparser.PointStyle(); + var ts = new styleparser.TextStyle(); + var hs = new styleparser.ShieldStyle(); + var xs = new styleparser.InstructionStyle(); + + var r = s.split(";"); + var isEval = {}; + for (var i in r) { + var a = r[i]; + if ((o = this.ASSIGNMENT_EVAL.exec(a))) { + k = o[1].replace(this.DASH, "_"); + t[k] = o[2]; + isEval[k] = true; + } else if ((o = this.ASSIGNMENT.exec(a))) { + k = o[1].replace(this.DASH, "_"); + t[k] = o[2]; + } else if ((o = this.SET_TAG_EVAL.exec(a))) { + } else if ((o = this.SET_TAG.exec(a))) { + // xs.addSetTag(o[1],this.saveEval(o[2])); + xs.addSetTag(o[1], o[2]); + } else if ((o = this.SET_TAG_TRUE.exec(a))) { + xs.addSetTag(o[1], true); + } else if ((o = this.EXIT.exec(a))) { + xs.setPropertyFromString("breaker", true); + } + } + + /*// Find sublayer // TODO: Why the renaming z_index -> sublayer? // This hardcoded variable isn't used anywhere. // Looks like this was replaced by the "subparts"... @@ -162,295 +207,349 @@ styleparser.RuleSet.prototype = { ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub; xs.sublayer=10;*/ - /*// Find "interactive" property - it's true unless explicitly set false + /*// Find "interactive" property - it's true unless explicitly set false var inter=true; if (t['interactive']) { inter=t['interactive'].match(this.FALSE) ? false : true; delete t['interactive']; } ss.interactive=ps.interactive=ts.interactive=hs.interactive=xs.interactive=inter;*/ - // Munge special values - // (we should stop doing this and do it in the style instead) - // TODO: font_bold??? wtf? -> support propper MapCSS properites! - if (t['font_weight'] ) { t['font_bold' ] = t['font_weight' ].match(this.BOLD ) ? true : false; delete t['font_weight']; } - if (t['font_style'] ) { t['font_italic'] = t['font_style' ].match(this.ITALIC) ? true : false; delete t['font_style']; } - if (t['text_decoration']) { t['font_underline'] = t['text_decoration'].match(this.UNDERLINE) ? true : false; delete t['text_decoration']; } - if (t['text_position'] ) { t['text_center'] = t['text_position' ].match(this.CENTER) ? true : false; delete t['text_position']; } - if (t['text_transform']) { - if (t['text_transform'].match(this.CAPS)) { t['font_caps']=true; } else { t['font_caps']=false; } - delete t['text_transform']; - } - - // Assign each property to the appropriate style - for (a in t) { - // Parse properties - v = t[a]; - - // Set in styles - if (ss.has(a)) { ss.setPropertyFromString(a,v,isEval[a]); } - else if (ps.has(a)) { ps.setPropertyFromString(a,v,isEval[a]); } - else if (ts.has(a)) { ts.setPropertyFromString(a,v,isEval[a]); } - else if (hs.has(a)) { hs.setPropertyFromString(a,v,isEval[a]); } - else { - // console.log(a+" not found"); - } - } - - // Add each style to list - if (ss.edited) { styles.push(ss); } - if (ps.edited) { styles.push(ps); } - if (ts.edited) { styles.push(ts); } - if (hs.edited) { styles.push(hs); } - if (xs.edited) { styles.push(xs); } - return styles; - }, - - parseZoom:function(s) { - var o={}; - var maxscale = 999; // TODO: hardcoded - var minscale = -999; // TODO: hardcoded - if ((o=this.ZOOM_MINMAX.exec(s))) { return [o[1],o[2]]; } - else if ((o=this.ZOOM_MIN.exec(s) )) { return [o[1], maxscale]; } - else if ((o=this.ZOOM_MAX.exec(s) )) { return [minscale, o[1]]; } - else if ((o=this.ZOOM_SINGLE.exec(s))) { return [o[1],o[1]]; } - return null; - }, - - parseCondition:function(s) { - var o={}; - if ((o=this.CONDITION_TRUE.exec(s))) { return (new styleparser.Condition()).init('true' ,o[1]); } - else if ((o=this.CONDITION_FALSE.exec(s))) { return (new styleparser.Condition()).init('false',o[1]); } - else if ((o=this.CONDITION_SET.exec(s))) { return (new styleparser.Condition()).init('set' ,o[1]); } - else if ((o=this.CONDITION_UNSET.exec(s))) { return (new styleparser.Condition()).init('unset',o[1]); } - else if ((o=this.CONDITION_NE.exec(s))) { return (new styleparser.Condition()).init('ne' ,o[1],o[2]); } - else if ((o=this.CONDITION_GT.exec(s))) { return (new styleparser.Condition()).init('>' ,o[1],o[2]); } - else if ((o=this.CONDITION_GE.exec(s))) { return (new styleparser.Condition()).init('>=' ,o[1],o[2]); } - else if ((o=this.CONDITION_LT.exec(s))) { return (new styleparser.Condition()).init('<' ,o[1],o[2]); } - else if ((o=this.CONDITION_LE.exec(s))) { return (new styleparser.Condition()).init('<=' ,o[1],o[2]); } - else if ((o=this.CONDITION_REGEX.exec(s))) { return (new styleparser.Condition()).init('regex',o[1],o[2]); } - else if ((o=this.CONDITION_EQ.exec(s))) { return (new styleparser.Condition()).init('eq' ,o[1],o[2]); } - return null; - }, - - parseCSSColor:function(colorStr) { - // todo: this should be done at user (=style consumer) side (if necessary). - // -> move to a more appropriate location - colorStr = colorStr.toLowerCase(); - if (this.CSSCOLORS[colorStr]) { - return this.CSSCOLORS[colorStr]; + // Munge special values + // (we should stop doing this and do it in the style instead) + // TODO: font_bold??? wtf? -> support propper MapCSS properites! + if (t["font_weight"]) { + t["font_bold"] = t["font_weight"].match(this.BOLD) ? true : false; + delete t["font_weight"]; + } + if (t["font_style"]) { + t["font_italic"] = t["font_style"].match(this.ITALIC) ? true : false; + delete t["font_style"]; + } + if (t["text_decoration"]) { + t["font_underline"] = t["text_decoration"].match(this.UNDERLINE) + ? true + : false; + delete t["text_decoration"]; + } + if (t["text_position"]) { + t["text_center"] = t["text_position"].match(this.CENTER) ? true : false; + delete t["text_position"]; + } + if (t["text_transform"]) { + if (t["text_transform"].match(this.CAPS)) { + t["font_caps"] = true; + } else { + t["font_caps"] = false; + } + delete t["text_transform"]; + } + + // Assign each property to the appropriate style + for (a in t) { + // Parse properties + v = t[a]; + + // Set in styles + if (ss.has(a)) { + ss.setPropertyFromString(a, v, isEval[a]); + } else if (ps.has(a)) { + ps.setPropertyFromString(a, v, isEval[a]); + } else if (ts.has(a)) { + ts.setPropertyFromString(a, v, isEval[a]); + } else if (hs.has(a)) { + hs.setPropertyFromString(a, v, isEval[a]); + } else { + // console.log(a+" not found"); + } + } + + // Add each style to list + if (ss.edited) { + styles.push(ss); + } + if (ps.edited) { + styles.push(ps); + } + if (ts.edited) { + styles.push(ts); + } + if (hs.edited) { + styles.push(hs); + } + if (xs.edited) { + styles.push(xs); + } + return styles; + }, + + parseZoom: function(s) { + var o = {}; + var maxscale = 999; // TODO: hardcoded + var minscale = -999; // TODO: hardcoded + if ((o = this.ZOOM_MINMAX.exec(s))) { + return [o[1], o[2]]; + } else if ((o = this.ZOOM_MIN.exec(s))) { + return [o[1], maxscale]; + } else if ((o = this.ZOOM_MAX.exec(s))) { + return [minscale, o[1]]; + } else if ((o = this.ZOOM_SINGLE.exec(s))) { + return [o[1], o[1]]; + } + return null; + }, + + parseCondition: function(s) { + var o = {}; + if ((o = this.CONDITION_TRUE.exec(s))) { + return new styleparser.Condition().init("true", o[1]); + } else if ((o = this.CONDITION_FALSE.exec(s))) { + return new styleparser.Condition().init("false", o[1]); + } else if ((o = this.CONDITION_SET.exec(s))) { + return new styleparser.Condition().init("set", o[1]); + } else if ((o = this.CONDITION_UNSET.exec(s))) { + return new styleparser.Condition().init("unset", o[1]); + } else if ((o = this.CONDITION_NE.exec(s))) { + return new styleparser.Condition().init("ne", o[1], o[2]); + } else if ((o = this.CONDITION_GT.exec(s))) { + return new styleparser.Condition().init(">", o[1], o[2]); + } else if ((o = this.CONDITION_GE.exec(s))) { + return new styleparser.Condition().init(">=", o[1], o[2]); + } else if ((o = this.CONDITION_LT.exec(s))) { + return new styleparser.Condition().init("<", o[1], o[2]); + } else if ((o = this.CONDITION_LE.exec(s))) { + return new styleparser.Condition().init("<=", o[1], o[2]); + } else if ((o = this.CONDITION_REGEX.exec(s))) { + return new styleparser.Condition().init("regex", o[1], o[2]); + } else if ((o = this.CONDITION_EQ.exec(s))) { + return new styleparser.Condition().init("eq", o[1], o[2]); + } + return null; + }, + + parseCSSColor: function(colorStr) { + // todo: this should be done at user (=style consumer) side (if necessary). + // -> move to a more appropriate location + colorStr = colorStr.toLowerCase(); + if (this.CSSCOLORS[colorStr]) { + return this.CSSCOLORS[colorStr]; + } else { + var match = this.HEX.exec(colorStr); + if (match) { + if (match[1].length == 3) { + // repeat digits. #abc => 0xaabbcc + return Number( + "0x" + + match[1].charAt(0) + + match[1].charAt(0) + + match[1].charAt(1) + + match[1].charAt(1) + + match[1].charAt(2) + + match[1].charAt(2) + ); + } else if (match[1].length == 6) { + return Number("0x" + match[1]); } else { - var match = this.HEX.exec(colorStr); - if ( match ) { - if ( match[1].length == 3) { - // repeat digits. #abc => 0xaabbcc - return Number("0x"+match[1].charAt(0)+match[1].charAt(0)+ - match[1].charAt(1)+match[1].charAt(1)+ - match[1].charAt(2)+match[1].charAt(2)); - } else if ( match[1].length == 6) { - return Number("0x"+match[1]); - } else { - return Number("0x000000"); //as good as any - } - } + return Number("0x000000"); //as good as any } - return 0; - }, - - // Regular expression tests and other constants - - WHITESPACE :/^\s+/, - COMMENT :/\/\*.+?\*\/\s*/, - CLASS :/^([\.:]\w+)\s*/, - NOT_CLASS :/^!([\.:]\w+)\s*/, - ZOOM :/^\|\s*z([\d\-]+)\s*/i, - GROUP :/^,\s*/i, - CONDITION :/^\[(.+?)\]\s*/, - OBJECT :/^(\w+)\s*/, // TODO: match also "*" (and handle the wildcard appropriately) - DECLARATION :/^\{(.+?)\}\s*/, - SUBPART :/^::(\w+)\s*/, - UNKNOWN :/^(\S+)\s*/, - - ZOOM_MINMAX :/^(\d+)\-(\d+)$/, - ZOOM_MIN :/^(\d+)\-$/, - ZOOM_MAX :/^\-(\d+)$/, - ZOOM_SINGLE :/^(\d+)$/, - - CONDITION_TRUE :/^\s*([:\w]+)\s*=\s*yes\s*$/i, - CONDITION_FALSE :/^\s*([:\w]+)\s*=\s*no\s*$/i, - CONDITION_SET :/^\s*([:\w]+)\s*$/, - CONDITION_UNSET :/^\s*!([:\w]+)\s*$/, - CONDITION_EQ :/^\s*([:\w]+)\s*=\s*(.+)\s*$/, - CONDITION_NE :/^\s*([:\w]+)\s*!=\s*(.+)\s*$/, - CONDITION_GT :/^\s*([:\w]+)\s*>\s*(.+)\s*$/, - CONDITION_GE :/^\s*([:\w]+)\s*>=\s*(.+)\s*$/, - CONDITION_LT :/^\s*([:\w]+)\s*<\s*(.+)\s*$/, - CONDITION_LE :/^\s*([:\w]+)\s*<=\s*(.+)\s*$/, - CONDITION_REGEX :/^\s*([:\w]+)\s*=~\/\s*(.+)\/\s*$/, - - ASSIGNMENT_EVAL :/^\s*(\S+)\s*\:\s*eval\s*\(\s*['"](.+?)['"]\s*\)\s*$/i, // TODO: match only two matching quotes - ASSIGNMENT :/^\s*(\S+)\s*\:\s*(.+?)\s*$/, - SET_TAG_EVAL :/^\s*set\s+(\S+)\s*=\s*eval\s*\(\s*'(.+?)'\s*\)\s*$/i, - SET_TAG :/^\s*set\s+(\S+)\s*=\s*(.+?)\s*$/i, - SET_TAG_TRUE :/^\s*set\s+(\S+)\s*$/i, - EXIT :/^\s*exit\s*$/i, - - oZOOM: 2, - oGROUP: 3, - oCONDITION: 4, - oOBJECT: 5, - oDECLARATION: 6, - oSUBPART: 7, - - DASH: /\-/g, - COLOR: /color$/, - BOLD: /^bold$/i, - ITALIC: /^italic|oblique$/i, - UNDERLINE: /^underline$/i, - CAPS: /^uppercase$/i, - CENTER: /^center$/i, - FALSE: /^(no|false|0)$/i, - - HEX: /^#([0-9a-f]+)$/i, - - CSSCOLORS: { - aliceblue:0xf0f8ff, - antiquewhite:0xfaebd7, - aqua:0x00ffff, - aquamarine:0x7fffd4, - azure:0xf0ffff, - beige:0xf5f5dc, - bisque:0xffe4c4, - black:0x000000, - blanchedalmond:0xffebcd, - blue:0x0000ff, - blueviolet:0x8a2be2, - brown:0xa52a2a, - burlywood:0xdeb887, - cadetblue:0x5f9ea0, - chartreuse:0x7fff00, - chocolate:0xd2691e, - coral:0xff7f50, - cornflowerblue:0x6495ed, - cornsilk:0xfff8dc, - crimson:0xdc143c, - cyan:0x00ffff, - darkblue:0x00008b, - darkcyan:0x008b8b, - darkgoldenrod:0xb8860b, - darkgray:0xa9a9a9, - darkgreen:0x006400, - darkkhaki:0xbdb76b, - darkmagenta:0x8b008b, - darkolivegreen:0x556b2f, - darkorange:0xff8c00, - darkorchid:0x9932cc, - darkred:0x8b0000, - darksalmon:0xe9967a, - darkseagreen:0x8fbc8f, - darkslateblue:0x483d8b, - darkslategray:0x2f4f4f, - darkturquoise:0x00ced1, - darkviolet:0x9400d3, - deeppink:0xff1493, - deepskyblue:0x00bfff, - dimgray:0x696969, - dodgerblue:0x1e90ff, - firebrick:0xb22222, - floralwhite:0xfffaf0, - forestgreen:0x228b22, - fuchsia:0xff00ff, - gainsboro:0xdcdcdc, - ghostwhite:0xf8f8ff, - gold:0xffd700, - goldenrod:0xdaa520, - gray:0x808080, - green:0x008000, - greenyellow:0xadff2f, - honeydew:0xf0fff0, - hotpink:0xff69b4, - indianred:0xcd5c5c, - indigo:0x4b0082, - ivory:0xfffff0, - khaki:0xf0e68c, - lavender:0xe6e6fa, - lavenderblush:0xfff0f5, - lawngreen:0x7cfc00, - lemonchiffon:0xfffacd, - lightblue:0xadd8e6, - lightcoral:0xf08080, - lightcyan:0xe0ffff, - lightgoldenrodyellow:0xfafad2, - lightgrey:0xd3d3d3, - lightgreen:0x90ee90, - lightpink:0xffb6c1, - lightsalmon:0xffa07a, - lightseagreen:0x20b2aa, - lightskyblue:0x87cefa, - lightslategray:0x778899, - lightsteelblue:0xb0c4de, - lightyellow:0xffffe0, - lime:0x00ff00, - limegreen:0x32cd32, - linen:0xfaf0e6, - magenta:0xff00ff, - maroon:0x800000, - mediumaquamarine:0x66cdaa, - mediumblue:0x0000cd, - mediumorchid:0xba55d3, - mediumpurple:0x9370d8, - mediumseagreen:0x3cb371, - mediumslateblue:0x7b68ee, - mediumspringgreen:0x00fa9a, - mediumturquoise:0x48d1cc, - mediumvioletred:0xc71585, - midnightblue:0x191970, - mintcream:0xf5fffa, - mistyrose:0xffe4e1, - moccasin:0xffe4b5, - navajowhite:0xffdead, - navy:0x000080, - oldlace:0xfdf5e6, - olive:0x808000, - olivedrab:0x6b8e23, - orange:0xffa500, - orangered:0xff4500, - orchid:0xda70d6, - palegoldenrod:0xeee8aa, - palegreen:0x98fb98, - paleturquoise:0xafeeee, - palevioletred:0xd87093, - papayawhip:0xffefd5, - peachpuff:0xffdab9, - peru:0xcd853f, - pink:0xffc0cb, - plum:0xdda0dd, - powderblue:0xb0e0e6, - purple:0x800080, - red:0xff0000, - rosybrown:0xbc8f8f, - royalblue:0x4169e1, - saddlebrown:0x8b4513, - salmon:0xfa8072, - sandybrown:0xf4a460, - seagreen:0x2e8b57, - seashell:0xfff5ee, - sienna:0xa0522d, - silver:0xc0c0c0, - skyblue:0x87ceeb, - slateblue:0x6a5acd, - slategray:0x708090, - snow:0xfffafa, - springgreen:0x00ff7f, - steelblue:0x4682b4, - tan:0xd2b48c, - teal:0x008080, - thistle:0xd8bfd8, - tomato:0xff6347, - turquoise:0x40e0d0, - violet:0xee82ee, - wheat:0xf5deb3, - white:0xffffff, - whitesmoke:0xf5f5f5, - yellow:0xffff00, - yellowgreen:0x9acd32 - }, - + } + } + return 0; + }, + + // Regular expression tests and other constants + + WHITESPACE: /^\s+/, + COMMENT: /\/\*.+?\*\/\s*/, + CLASS: /^([\.:]\w+)\s*/, + NOT_CLASS: /^!([\.:]\w+)\s*/, + ZOOM: /^\|\s*z([\d\-]+)\s*/i, + GROUP: /^,\s*/i, + CONDITION: /^\[(.+?)\]\s*/, + OBJECT: /^(\w+)\s*/, // TODO: match also "*" (and handle the wildcard appropriately) + DECLARATION: /^\{(.+?)\}\s*/, + SUBPART: /^::(\w+)\s*/, + UNKNOWN: /^(\S+)\s*/, + + ZOOM_MINMAX: /^(\d+)\-(\d+)$/, + ZOOM_MIN: /^(\d+)\-$/, + ZOOM_MAX: /^\-(\d+)$/, + ZOOM_SINGLE: /^(\d+)$/, + + CONDITION_TRUE: /^\s*([:\w]+)\s*=\s*yes\s*$/i, + CONDITION_FALSE: /^\s*([:\w]+)\s*=\s*no\s*$/i, + CONDITION_SET: /^\s*([:\w]+)\s*$/, + CONDITION_UNSET: /^\s*!([:\w]+)\s*$/, + CONDITION_EQ: /^\s*([:\w]+)\s*=\s*(.+)\s*$/, + CONDITION_NE: /^\s*([:\w]+)\s*!=\s*(.+)\s*$/, + CONDITION_GT: /^\s*([:\w]+)\s*>\s*(.+)\s*$/, + CONDITION_GE: /^\s*([:\w]+)\s*>=\s*(.+)\s*$/, + CONDITION_LT: /^\s*([:\w]+)\s*<\s*(.+)\s*$/, + CONDITION_LE: /^\s*([:\w]+)\s*<=\s*(.+)\s*$/, + CONDITION_REGEX: /^\s*([:\w]+)\s*=~\/\s*(.+)\/\s*$/, + + ASSIGNMENT_EVAL: /^\s*(\S+)\s*\:\s*eval\s*\(\s*['"](.+?)['"]\s*\)\s*$/i, // TODO: match only two matching quotes + ASSIGNMENT: /^\s*(\S+)\s*\:\s*(.+?)\s*$/, + SET_TAG_EVAL: /^\s*set\s+(\S+)\s*=\s*eval\s*\(\s*'(.+?)'\s*\)\s*$/i, + SET_TAG: /^\s*set\s+(\S+)\s*=\s*(.+?)\s*$/i, + SET_TAG_TRUE: /^\s*set\s+(\S+)\s*$/i, + EXIT: /^\s*exit\s*$/i, + + oZOOM: 2, + oGROUP: 3, + oCONDITION: 4, + oOBJECT: 5, + oDECLARATION: 6, + oSUBPART: 7, + + DASH: /\-/g, + COLOR: /color$/, + BOLD: /^bold$/i, + ITALIC: /^italic|oblique$/i, + UNDERLINE: /^underline$/i, + CAPS: /^uppercase$/i, + CENTER: /^center$/i, + FALSE: /^(no|false|0)$/i, + + HEX: /^#([0-9a-f]+)$/i, + + CSSCOLORS: { + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgreen: 0x006400, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgrey: 0xd3d3d3, + lightgreen: 0x90ee90, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370d8, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xd87093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 + } }; diff --git a/js/jsmapcss/Style.js b/js/jsmapcss/Style.js index d85fc2fd..84e29795 100644 --- a/js/jsmapcss/Style.js +++ b/js/jsmapcss/Style.js @@ -1,253 +1,377 @@ -import evalparser from './eval.pegjs'; +import evalparser from "./eval.pegjs"; var styleparser = {}; -styleparser.Style = function() {this.__init__()}; +styleparser.Style = function() { + this.__init__(); +}; styleparser.Style.prototype = { - merged: false, - edited: false, - //sublayer: 5, // TODO: commented out. see RuleSet.js - //interactive: true, // TODO: commented out. see RuleSet.js - properties: [], - styleType: 'Style', - evals: null, - - __init__: function() { - this.evals = {}; - }, - - drawn: function() { - return false; - }, - - has: function(k) { - return this.properties.indexOf(k)>-1; - }, - - mergeWith: function(additional) { - for (var prop in this.properties) { - if (additional[prop]) { - this[prop]=additional[prop]; - } - } - this.merged=true; - }, - - setPropertyFromString: function(k,v,isEval) { - this.edited=true; - if (isEval) { this.evals[k]=v; return; } - - if (typeof(this[k])=='boolean') { - v=Boolean(v); - } else if (typeof(this[k])=='number') { - v=Number(v); - } else if (this[k] && this[k].constructor==Array) { - v = v.split(',').map(function(a) { return Number(a); }); - } - this[k]=v; - return true; - }, - - runEvals: function(tags) { - // helper object for eval() properties - for (var k in this.evals) { - try { - evalparser.tag = function(t) {return tags[t] || "";}; - this.setPropertyFromString(k, evalparser.parse(this.evals[k])); - } catch(e) { console.error("Error while evaluating mapcss evals", e); } - } - }, - - toString: function() { - var str = ''; - for (var k in this.properties) { - if (this.hasOwnProperty(k)) { str+=k+"="+this[k]+"; "; } - } - return str; - } + merged: false, + edited: false, + //sublayer: 5, // TODO: commented out. see RuleSet.js + //interactive: true, // TODO: commented out. see RuleSet.js + properties: [], + styleType: "Style", + evals: null, + + __init__: function() { + this.evals = {}; + }, + + drawn: function() { + return false; + }, + + has: function(k) { + return this.properties.indexOf(k) > -1; + }, + + mergeWith: function(additional) { + for (var prop in this.properties) { + if (additional[prop]) { + this[prop] = additional[prop]; + } + } + this.merged = true; + }, + + setPropertyFromString: function(k, v, isEval) { + this.edited = true; + if (isEval) { + this.evals[k] = v; + return; + } + + if (typeof this[k] == "boolean") { + v = Boolean(v); + } else if (typeof this[k] == "number") { + v = Number(v); + } else if (this[k] && this[k].constructor == Array) { + v = v.split(",").map(function(a) { + return Number(a); + }); + } + this[k] = v; + return true; + }, + + runEvals: function(tags) { + // helper object for eval() properties + for (var k in this.evals) { + try { + evalparser.tag = function(t) { + return tags[t] || ""; + }; + this.setPropertyFromString(k, evalparser.parse(this.evals[k])); + } catch (e) { + console.error("Error while evaluating mapcss evals", e); + } + } + }, + + toString: function() { + var str = ""; + for (var k in this.properties) { + if (this.hasOwnProperty(k)) { + str += k + "=" + this[k] + "; "; + } + } + return str; + } }; styleparser.inherit_from_Style = function(target) { for (var p in styleparser.Style.prototype) - if (target[p] === undefined) - target[p] = styleparser.Style.prototype[p]; -} - + if (target[p] === undefined) target[p] = styleparser.Style.prototype[p]; +}; // ---------------------------------------------------------------------- // InstructionStyle class -styleparser.InstructionStyle = function() {this.__init__()}; +styleparser.InstructionStyle = function() { + this.__init__(); +}; styleparser.InstructionStyle.prototype = { - set_tags: null, - breaker: false, - styleType: 'InstructionStyle', + set_tags: null, + breaker: false, + styleType: "InstructionStyle", - __init__: function() { - }, - - addSetTag: function(k,v) { - this.edited=true; - if (!this.set_tags) this.set_tags={}; - this.set_tags[k]=v; - }, + __init__: function() {}, + addSetTag: function(k, v) { + this.edited = true; + if (!this.set_tags) this.set_tags = {}; + this.set_tags[k] = v; + } }; styleparser.inherit_from_Style(styleparser.InstructionStyle.prototype); // ---------------------------------------------------------------------- // PointStyle class -styleparser.PointStyle = function() {this.__init__()}; +styleparser.PointStyle = function() { + this.__init__(); +}; styleparser.PointStyle.prototype = { - properties: ['icon_image','icon_width','icon_height','icon_opacity','rotation'], - icon_image: null, - icon_width: 0, - icon_height: NaN, - rotation: NaN, - styleType: 'PointStyle', - - drawn:function() { - return (this.icon_image !== null); - }, - - maxwidth:function() { - return this.evals.icon_width ? 0 : this.icon_width; - }, - + properties: [ + "icon_image", + "icon_width", + "icon_height", + "icon_opacity", + "rotation" + ], + icon_image: null, + icon_width: 0, + icon_height: NaN, + rotation: NaN, + styleType: "PointStyle", + + drawn: function() { + return this.icon_image !== null; + }, + + maxwidth: function() { + return this.evals.icon_width ? 0 : this.icon_width; + } }; styleparser.inherit_from_Style(styleparser.PointStyle.prototype); // ---------------------------------------------------------------------- // ShapeStyle class -styleparser.ShapeStyle = function() {this.__init__()}; +styleparser.ShapeStyle = function() { + this.__init__(); +}; styleparser.ShapeStyle.prototype = { - properties: ['width','offset','color','opacity','dashes','linecap','linejoin','line_style', - 'fill_image','fill_color','fill_opacity','casing_width','casing_color','casing_opacity','casing_dashes','layer'], - - width:0, color:null, opacity:NaN, dashes:[], - linecap:null, linejoin:null, line_style:null, - fill_image:null, fill_color:null, fill_opacity:NaN, - casing_width:NaN, casing_color:null, casing_opacity:NaN, casing_dashes:[], - layer:NaN, // optional layer override (usually set by OSM tag) - styleType: 'ShapeStyle', - - drawn:function() { - return (this.fill_image || !isNaN(this.fill_color) || this.width || this.casing_width); - }, - maxwidth:function() { - // If width is set by an eval, then we can't use it to calculate maxwidth, or it'll just grow on each invocation... - if (this.evals.width || this.evals.casing_width) { return 0; } - return (this.width + (this.casing_width ? this.casing_width*2 : 0)); - }, - strokeStyler:function() { - var cap,join; - switch (this.linecap ) { case 'round': cap ='round'; break; case 'square': cap='square'; break; default: cap ='butt' ; break; } - switch (this.linejoin) { case 'bevel': join='bevel'; break; case 'miter' : join=4 ; break; default: join='round'; break; } - return { - color: this.dojoColor(this.color ? this.color : 0, this.opacity ? this.opacity : 1), - style: 'Solid', // needs to parse dashes - width: this.width, - cap: cap, - join: join - }; - }, - shapeStrokeStyler:function() { - if (isNaN(this.casing_color)) { return { width:0 }; } - return { - color: this.dojoColor(this.casing_color, this.casing_opacity ? this.casing_opacity : 1), - width: this.casing_width ? this.casing_width : 1 - }; - }, - shapeFillStyler:function() { - if (isNaN(this.color)) { return null; } - return this.dojoColor(this.color, this.opacity ? this.opacity : 1); - }, - fillStyler:function() { - return this.dojoColor(this.fill_color, this.fill_opacity ? this.fill_opacity : 1); - }, - casingStyler:function() { - var cap,join; - switch (this.linecap ) { case 'round': cap ='round'; break; case 'square': cap='square'; break; default: cap ='butt' ; break; } - switch (this.linejoin) { case 'bevel': join='bevel'; break; case 'miter' : join=4 ; break; default: join='round'; break; } - return { - color: this.dojoColor(this.casing_color ? this.casing_color : 0, this.casing_opacity ? this.casing_opacity : 1), - width: this.width+this.casing_width*2, - style: 'Solid', - cap: cap, - join: join - }; - }, - + properties: [ + "width", + "offset", + "color", + "opacity", + "dashes", + "linecap", + "linejoin", + "line_style", + "fill_image", + "fill_color", + "fill_opacity", + "casing_width", + "casing_color", + "casing_opacity", + "casing_dashes", + "layer" + ], + + width: 0, + color: null, + opacity: NaN, + dashes: [], + linecap: null, + linejoin: null, + line_style: null, + fill_image: null, + fill_color: null, + fill_opacity: NaN, + casing_width: NaN, + casing_color: null, + casing_opacity: NaN, + casing_dashes: [], + layer: NaN, // optional layer override (usually set by OSM tag) + styleType: "ShapeStyle", + + drawn: function() { + return ( + this.fill_image || + !isNaN(this.fill_color) || + this.width || + this.casing_width + ); + }, + maxwidth: function() { + // If width is set by an eval, then we can't use it to calculate maxwidth, or it'll just grow on each invocation... + if (this.evals.width || this.evals.casing_width) { + return 0; + } + return this.width + (this.casing_width ? this.casing_width * 2 : 0); + }, + strokeStyler: function() { + var cap, join; + switch (this.linecap) { + case "round": + cap = "round"; + break; + case "square": + cap = "square"; + break; + default: + cap = "butt"; + break; + } + switch (this.linejoin) { + case "bevel": + join = "bevel"; + break; + case "miter": + join = 4; + break; + default: + join = "round"; + break; + } + return { + color: this.dojoColor( + this.color ? this.color : 0, + this.opacity ? this.opacity : 1 + ), + style: "Solid", // needs to parse dashes + width: this.width, + cap: cap, + join: join + }; + }, + shapeStrokeStyler: function() { + if (isNaN(this.casing_color)) { + return {width: 0}; + } + return { + color: this.dojoColor( + this.casing_color, + this.casing_opacity ? this.casing_opacity : 1 + ), + width: this.casing_width ? this.casing_width : 1 + }; + }, + shapeFillStyler: function() { + if (isNaN(this.color)) { + return null; + } + return this.dojoColor(this.color, this.opacity ? this.opacity : 1); + }, + fillStyler: function() { + return this.dojoColor( + this.fill_color, + this.fill_opacity ? this.fill_opacity : 1 + ); + }, + casingStyler: function() { + var cap, join; + switch (this.linecap) { + case "round": + cap = "round"; + break; + case "square": + cap = "square"; + break; + default: + cap = "butt"; + break; + } + switch (this.linejoin) { + case "bevel": + join = "bevel"; + break; + case "miter": + join = 4; + break; + default: + join = "round"; + break; + } + return { + color: this.dojoColor( + this.casing_color ? this.casing_color : 0, + this.casing_opacity ? this.casing_opacity : 1 + ), + width: this.width + this.casing_width * 2, + style: "Solid", + cap: cap, + join: join + }; + } }; styleparser.inherit_from_Style(styleparser.ShapeStyle.prototype); // ---------------------------------------------------------------------- // TextStyle class -styleparser.TextStyle = function() {this.__init__()}; +styleparser.TextStyle = function() { + this.__init__(); +}; styleparser.TextStyle.prototype = { - properties: ['font_family','font_bold','font_italic','font_caps','font_underline','font_size', - 'text_color','text_offset','max_width', - 'text','text_halo_color','text_halo_radius','text_center', - 'letter_spacing'], - // TODO: font_bold??? wtf? -> support propper MapCSS properites! - - font_family: null, - font_bold: false, - font_italic: false, - font_underline: false, - font_caps: false, - font_size: NaN, - text_color: null, - text_offset: NaN, - max_width: NaN, - text: null, - text_halo_color: null, - text_halo_radius: 0, - text_center: true, - letter_spacing: 0, - styleType: 'TextStyle', - - fontStyler:function() { - return { - family: this.font_family ? this.font_family : 'Arial', - size: this.font_size ? this.font_size*2 : '10px' , - weight: this.font_bold ? 'bold' : 'normal', - style: this.font_italic ? 'italic' : 'normal' - }; - }, - textStyler:function(_text) { - return { - decoration: this.font_underline ? 'underline' : 'none', - align: 'middle', - text: _text - }; - }, - fillStyler:function() { - // not implemented yet - return this.dojoColor(0,1); - }, - - // getTextFormat, getHaloFilter, writeNameLabel + properties: [ + "font_family", + "font_bold", + "font_italic", + "font_caps", + "font_underline", + "font_size", + "text_color", + "text_offset", + "max_width", + "text", + "text_halo_color", + "text_halo_radius", + "text_center", + "letter_spacing" + ], + // TODO: font_bold??? wtf? -> support propper MapCSS properites! + + font_family: null, + font_bold: false, + font_italic: false, + font_underline: false, + font_caps: false, + font_size: NaN, + text_color: null, + text_offset: NaN, + max_width: NaN, + text: null, + text_halo_color: null, + text_halo_radius: 0, + text_center: true, + letter_spacing: 0, + styleType: "TextStyle", + + fontStyler: function() { + return { + family: this.font_family ? this.font_family : "Arial", + size: this.font_size ? this.font_size * 2 : "10px", + weight: this.font_bold ? "bold" : "normal", + style: this.font_italic ? "italic" : "normal" + }; + }, + textStyler: function(_text) { + return { + decoration: this.font_underline ? "underline" : "none", + align: "middle", + text: _text + }; + }, + fillStyler: function() { + // not implemented yet + return this.dojoColor(0, 1); + } + + // getTextFormat, getHaloFilter, writeNameLabel }; styleparser.inherit_from_Style(styleparser.TextStyle.prototype); // ---------------------------------------------------------------------- // ShieldStyle class -styleparser.ShieldStyle = function() {this.__init__()}; +styleparser.ShieldStyle = function() { + this.__init__(); +}; styleparser.ShieldStyle.prototype = { - has: function(k) { - return this.properties.indexOf(k)>-1; - }, - properties: ['shield_image','shield_width','shield_height'], - shield_image: null, - shield_width: NaN, - shield_height: NaN, - styleType: 'ShieldStyle', + has: function(k) { + return this.properties.indexOf(k) > -1; + }, + properties: ["shield_image", "shield_width", "shield_height"], + shield_image: null, + shield_width: NaN, + shield_height: NaN, + styleType: "ShieldStyle" }; styleparser.inherit_from_Style(styleparser.ShieldStyle.prototype); diff --git a/js/jsmapcss/StyleChooser.js b/js/jsmapcss/StyleChooser.js index 0a7c0f39..6d450a1d 100644 --- a/js/jsmapcss/StyleChooser.js +++ b/js/jsmapcss/StyleChooser.js @@ -1,95 +1,110 @@ // styleparser/StyleChooser.js -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.StyleChooser = function() { - this.ruleChains = [new styleparser.RuleChain()]; - this.styles = []; + this.ruleChains = [new styleparser.RuleChain()]; + this.styles = []; }; styleparser.StyleChooser.prototype = { - // UpdateStyles doesn't support image-widths yet - // or setting maxwidth/_width - ruleChains: [], // array of RuleChains (each one an array of Rules) - styles: [], // array of ShapeStyle/ShieldStyle/TextStyle/PointStyle - zoomSpecific:false, // are any of the rules zoom-specific? + // UpdateStyles doesn't support image-widths yet + // or setting maxwidth/_width + ruleChains: [], // array of RuleChains (each one an array of Rules) + styles: [], // array of ShapeStyle/ShieldStyle/TextStyle/PointStyle + zoomSpecific: false, // are any of the rules zoom-specific? - rcpos:0, - stylepos:0, + rcpos: 0, + stylepos: 0, - constructor:function() { - // summary: A combination of the selectors (ruleChains) and declaration (styles). - // For example, way[highway=footway] node[barrier=gate] { icon: gate.png; } is one StyleChooser. - }, + constructor: function() { + // summary: A combination of the selectors (ruleChains) and declaration (styles). + // For example, way[highway=footway] node[barrier=gate] { icon: gate.png; } is one StyleChooser. + }, - currentChain:function() { - return this.ruleChains[this.ruleChains.length-1]; - }, + currentChain: function() { + return this.ruleChains[this.ruleChains.length - 1]; + }, - newRuleChain:function() { - // summary: Starts a new ruleChain in this.ruleChains. - if (this.ruleChains[this.ruleChains.length-1].length()>0) { - this.ruleChains.push(new styleparser.RuleChain()); - } - }, + newRuleChain: function() { + // summary: Starts a new ruleChain in this.ruleChains. + if (this.ruleChains[this.ruleChains.length - 1].length() > 0) { + this.ruleChains.push(new styleparser.RuleChain()); + } + }, - addStyles:function(a) { - this.styles=this.styles.concat(a); - }, + addStyles: function(a) { + this.styles = this.styles.concat(a); + }, - updateStyles:function(entity, tags, sl, zoom) { - if (this.zoomSpecific) { sl.validAt=zoom; } + updateStyles: function(entity, tags, sl, zoom) { + if (this.zoomSpecific) { + sl.validAt = zoom; + } - // Are any of the ruleChains fulfilled? - var w; - for (var i in this.ruleChains) { - var c=this.ruleChains[i]; - if (c.test(-1, entity, tags, zoom)) { - sl.addSubpart(c.subpart); + // Are any of the ruleChains fulfilled? + var w; + for (var i in this.ruleChains) { + var c = this.ruleChains[i]; + if (c.test(-1, entity, tags, zoom)) { + sl.addSubpart(c.subpart); - // Update StyleList - for (var j in this.styles) { - var r=this.styles[j]; - var a; - switch (r.styleType) { + // Update StyleList + for (var j in this.styles) { + var r = this.styles[j]; + var a; + switch (r.styleType) { + case "ShapeStyle": + sl.maxwidth = Math.max(sl.maxwidth, r.maxwidth()); + a = sl.shapeStyles; + break; + case "ShieldStyle": + a = sl.shieldStyles; + break; + case "TextStyle": + a = sl.textStyles; + break; + case "PointStyle": + sl.maxwidth = Math.max(sl.maxwidth, r.maxwidth()); + a = sl.pointStyles; + break; + case "InstructionStyle": + if (r.breaker) { + return; + } + for (var k in r.set_tags) { + tags[k] = r.set_tags[k]; + } + a = {}; // "dev/null" stylechooser reciever + break; + } + if (r.drawn()) { + tags[":drawn"] = "yes"; + } + tags._width = sl.maxwidth; - case 'ShapeStyle' : sl.maxwidth=Math.max(sl.maxwidth,r.maxwidth()); - a=sl.shapeStyles; break; - case 'ShieldStyle': a=sl.shieldStyles; break; - case 'TextStyle' : a=sl.textStyles; break; - case 'PointStyle' : sl.maxwidth=Math.max(sl.maxwidth,r.maxwidth()); - a=sl.pointStyles; break; - case 'InstructionStyle': - if (r.breaker) { return; } - for (var k in r.set_tags) { tags[k]=r.set_tags[k]; } - a = {}; // "dev/null" stylechooser reciever - break; - } - if (r.drawn()) { tags[':drawn']='yes'; } - tags._width = sl.maxwidth; - - r.runEvals(tags); - // helper function - if (a[c.subpart]) { - // // If there's already a style on this sublayer, then merge them - // // (making a deep copy if necessary to avoid altering the root style) - // if (!a[c.subpart].merged) { a[c.subpart]=extend({},a[c.subpart]); } - extend(a[c.subpart], r); - } else { - // // Otherwise, just assign it - a[c.subpart]=extend({},r); - } - } - } - } - } + r.runEvals(tags); + // helper function + if (a[c.subpart]) { + // // If there's already a style on this sublayer, then merge them + // // (making a deep copy if necessary to avoid altering the root style) + // if (!a[c.subpart].merged) { a[c.subpart]=extend({},a[c.subpart]); } + extend(a[c.subpart], r); + } else { + // // Otherwise, just assign it + a[c.subpart] = extend({}, r); + } + } + } + } + } }; function extend(destination, source) { - for (var property in source) { - if (source.hasOwnProperty(property)) { - destination[property] = source[property]; - } - } - return destination; + for (var property in source) { + if (source.hasOwnProperty(property)) { + destination[property] = source[property]; + } + } + return destination; } diff --git a/js/jsmapcss/StyleList.js b/js/jsmapcss/StyleList.js index 314164c5..fb420897 100644 --- a/js/jsmapcss/StyleList.js +++ b/js/jsmapcss/StyleList.js @@ -1,65 +1,102 @@ // ---------------------------------------------------------------------- // StyleList class -import styleparser from './Style.js'; +import styleparser from "./Style.js"; styleparser.StyleList = function() { - this.shapeStyles = {}; - this.textStyles = {}; - this.pointStyles = {}; - this.shieldStyles = {}; + this.shapeStyles = {}; + this.textStyles = {}; + this.pointStyles = {}; + this.shieldStyles = {}; }; styleparser.StyleList.prototype = { + maxwidth: 0, + subparts: [], // List of subparts used in this StyleList + validAt: -1, // Zoom level this is valid at (or -1 at all levels - saves recomputing) - maxwidth: 0, - subparts: [], // List of subparts used in this StyleList - validAt: -1, // Zoom level this is valid at (or -1 at all levels - saves recomputing) + hasStyles: function() { + // summary: Does this StyleList contain any styles? + return ( + this.hasShapeStyles() || + this.hasTextStyles() || + this.hasPointStyles() || + this.hasShieldStyles() + ); + }, + hasFills: function() { + // summary: Does this StyleList contain any styles with a fill? + for (var s in this.shapeStyles) { + if ( + !isNaN(this.shapeStyles(s).fill_color) || + this.shapeStyles(s).fill_image + ) + return true; + } + return false; + }, - hasStyles:function() { - // summary: Does this StyleList contain any styles? - return (this.hasShapeStyles() || this.hasTextStyles() || this.hasPointStyles() || this.hasShieldStyles()); - }, + layerOverride: function() { + // summary: If this StyleList manually forces an OSM layer, return it, otherwise null. + for (var s in this.shapeStyles) { + if (!isNaN(this.shapeStyles[s].layer)) return this.shapeStyles[s].layer; + } + return NaN; + }, - hasFills:function() { - // summary: Does this StyleList contain any styles with a fill? - for (var s in this.shapeStyles) { - if (!isNaN(this.shapeStyles(s).fill_color) || this.shapeStyles(s).fill_image) return true; - } - return false; - }, + addSubpart: function(s) { + // summary: Record that a subpart is used in this StyleList. + if (this.subparts.indexOf(s) == -1) { + this.subparts.push(s); + } + }, - layerOverride:function() { - // summary: If this StyleList manually forces an OSM layer, return it, otherwise null. - for (var s in this.shapeStyles) { - if (!isNaN(this.shapeStyles[s].layer)) return this.shapeStyles[s].layer; - } - return NaN; - }, + isValidAt: function(zoom) { + // summary: Is this StyleList valid at a given zoom? + return this.validAt == -1 || this.validAt == zoom; + }, - addSubpart:function(s) { - // summary: Record that a subpart is used in this StyleList. - if (this.subparts.indexOf(s)==-1) { this.subparts.push(s); } - }, + toString: function() { + // summary: Summarise StyleList as String - for debugging + var str = ""; + var k; + for (k in this.shapeStyles) { + str += "- SS " + k + "=" + this.shapeStyles[k] + "\n"; + } + for (k in this.textStyles) { + str += "- TS " + k + "=" + this.textStyles[k] + "\n"; + } + for (k in this.pointStyles) { + str += "- PS " + k + "=" + this.pointStyles[k] + "\n"; + } + for (k in this.shieldStyles) { + str += "- sS " + k + "=" + this.shieldStyles[k] + "\n"; + } + return str; + }, - isValidAt:function(zoom) { - // summary: Is this StyleList valid at a given zoom? - return (this.validAt==-1 || this.validAt==zoom); - }, - - toString:function() { - // summary: Summarise StyleList as String - for debugging - var str = ''; - var k; - for (k in this.shapeStyles ) { str+="- SS "+k+"="+this.shapeStyles[k]+"\n"; } - for (k in this.textStyles ) { str+="- TS "+k+"="+this.textStyles[k]+"\n"; } - for (k in this.pointStyles ) { str+="- PS "+k+"="+this.pointStyles[k]+"\n"; } - for (k in this.shieldStyles) { str+="- sS "+k+"="+this.shieldStyles[k]+"\n"; } - return str; - }, - - hasShapeStyles:function() { for (var a in this.shapeStyles ) { return true; } return false; }, - hasTextStyles:function() { for (var a in this.textStyles ) { return true; } return false; }, - hasPointStyles:function() { for (var a in this.pointStyles ) { return true; } return false; }, - hasShieldStyles:function() { for (var a in this.shieldStyles) { return true; } return false; } + hasShapeStyles: function() { + for (var a in this.shapeStyles) { + return true; + } + return false; + }, + hasTextStyles: function() { + for (var a in this.textStyles) { + return true; + } + return false; + }, + hasPointStyles: function() { + for (var a in this.pointStyles) { + return true; + } + return false; + }, + hasShieldStyles: function() { + for (var a in this.shieldStyles) { + return true; + } + return false; + } }; diff --git a/js/jsmapcss/index.js b/js/jsmapcss/index.js index 4a635c5b..a82f092a 100644 --- a/js/jsmapcss/index.js +++ b/js/jsmapcss/index.js @@ -1,10 +1,10 @@ /*! this is mostly based on work by Richard Fairhurst, initially developed for iD editor, but abbandoned. See: http://lists.openstreetmap.org/pipermail/mapcss/2013-February/000341.html . This is under WTFPL. */ //export {default} from './eval.pegjs'; -export {default} from './Style.js'; -import './Condition'; -import './Rule'; -import './RuleChain'; -import './RuleSet'; -import './Style'; -import './StyleChooser'; -import './StyleList'; +export {default} from "./Style.js"; +import "./Condition"; +import "./Rule"; +import "./RuleChain"; +import "./RuleSet"; +import "./Style"; +import "./StyleChooser"; +import "./StyleList"; diff --git a/js/map.js b/js/map.js index c234d7ae..01ef941d 100644 --- a/js/map.js +++ b/js/map.js @@ -1,48 +1,58 @@ // escape strings to show them directly in the html. -import $ from 'jquery'; -import L from 'leaflet'; +import $ from "jquery"; +import L from "leaflet"; // include the CSS files -import 'leaflet/dist/leaflet.css'; -import '../css/map.css'; +import "leaflet/dist/leaflet.css"; +import "../css/map.css"; -import configs from './configs'; -import overpass from './overpass'; -import Query from './query'; +import configs from "./configs"; +import overpass from "./overpass"; +import Query from "./query"; $(document).ready(function() { // main map cache var cache = {}; - window.addEventListener("message", function (evt) { - var data = typeof evt.data === 'string' ? JSON.parse(evt.data): {}; - switch(data.cmd){ - case 'update_map': + window.addEventListener( + "message", + function(evt) { + var data = typeof evt.data === "string" ? JSON.parse(evt.data) : {}; + switch (data.cmd) { + case "update_map": settings.code["overpass"] = data.value[0]; ide.update_map(); break; - case 'cache': + case "cache": settings.code["overpass"] = data.value[0]; - ide.getQuery(function(query){ - var query_lang = ide.getQueryLang(); - overpass.run_query(query, query_lang, cache, true, undefined, ide.mapcss); + ide.getQuery(function(query) { + var query_lang = ide.getQueryLang(); + overpass.run_query( + query, + query_lang, + cache, + true, + undefined, + ide.mapcss + ); }); break; } - } - , false); + }, + false + ); // some initalizations $.fn.dialog = function() { - alert("error :( "+$(this).html()); + alert("error :( " + $(this).html()); }; configs.appname = "overpass-ide-map"; var settings = { - code:{}, + code: {}, server: configs.defaultServer, tileServer: configs.defaultTiles, force_simple_cors_request: true, - disable_poiomatic: false, + disable_poiomatic: false }; var ide = { getQuery: function(callback) { @@ -50,74 +60,93 @@ $(document).ready(function() { var queryParser = Query(); queryParser.parse(query, {}, function(query) { - // parse mapcss declarations - var mapcss = ""; - if (queryParser.hasStatement("style")) - mapcss = queryParser.getStatement("style"); - ide.mapcss = mapcss; - // parse data-source statements - var data_source = null; - if (queryParser.hasStatement("data")) { - data_source = queryParser.getStatement("data"); - data_source = data_source.split(','); - var data_mode = data_source[0].toLowerCase(); - data_source = data_source.slice(1); - var options = {}; - for (var i=0; i'+ - '

    The browser you are currently using, is not capable of running this Application. It has to support cross origin resource sharing (CORS).

    '+ - '

    Please update to a more up-to-date version of your browser or switch to a more capable browser! Recent versions of Opera, Chrome and Firefox have been tested to work.

    '+ - '').dialog({modal:true}); + $( + '
    ' + + '

    The browser you are currently using, is not capable of running this Application. It has to support cross origin resource sharing (CORS).

    ' + + '

    Please update to a more up-to-date version of your browser or switch to a more capable browser! Recent versions of Opera, Chrome and Firefox have been tested to work.

    ' + + "
    " + ).dialog({modal: true}); } // check for any get-parameters var get = location.search.substring(1).split("&"); - for (var i=0; iOpenStreetMap contributors Data:ODbL, Map:cc-by-sa'; + var tiles = new L.TileLayer(tilesUrl, {attribution: tilesAttrib}); + ide.map.setView([0, 0], 1).addLayer(tiles); + var scaleControl = new L.Control.Scale({metric: true, imperial: false}); scaleControl.addTo(ide.map); // wait spinner $(document).on({ @@ -126,33 +155,74 @@ $(document).ready(function() { }, ajaxStop: function() { $("body").removeClass("loading"); - }, + } }); ide.map.on("layeradd", function(e) { if (!(e.layer instanceof L.GeoJSON)) return; - ide.map.setView([0,0],18,true); + ide.map.setView([0, 0], 18, true); try { - ide.map.fitBounds(e.layer.getBounds() ); - } - catch(err){ - - } + ide.map.fitBounds(e.layer.getBounds()); + } catch (err) {} }); // overpass functionality - overpass.handlers["onEmptyMap"] = function(empty_msg, data_mode) {$('
    This map intentionally left blank. ('+empty_msg+')
    ').appendTo("#map");}; + overpass.handlers["onEmptyMap"] = function(empty_msg, data_mode) { + $( + '
    This map intentionally left blank. (' + + empty_msg + + ")
    " + ).appendTo("#map"); + }; if (settings.silent) { - overpass.handlers["onAjaxError"] = function(errmsg) {parent.postMessage(JSON.stringify({handler: "onAjaxError", msg: errmsg}), "*"); } - overpass.handlers["onQueryError"] = function(errmsg) {parent.postMessage(JSON.stringify({handler: "onQueryError", msg: errmsg }), "*"); } + overpass.handlers["onAjaxError"] = function(errmsg) { + parent.postMessage( + JSON.stringify({handler: "onAjaxError", msg: errmsg}), + "*" + ); + }; + overpass.handlers["onQueryError"] = function(errmsg) { + parent.postMessage( + JSON.stringify({handler: "onQueryError", msg: errmsg}), + "*" + ); + }; } else { - overpass.handlers["onAjaxError"] = function(errmsg) {alert("An error occured during the execution of the overpass query!\n" + errmsg);}; - overpass.handlers["onQueryError"] = function(errmsg) {alert("An error occured during the execution of the overpass query!\nThis is what overpass API returned:\n" + errmsg);}; + overpass.handlers["onAjaxError"] = function(errmsg) { + alert( + "An error occured during the execution of the overpass query!\n" + + errmsg + ); + }; + overpass.handlers["onQueryError"] = function(errmsg) { + alert( + "An error occured during the execution of the overpass query!\nThis is what overpass API returned:\n" + + errmsg + ); + }; } - overpass.handlers["onGeoJsonReady"] = function() {ide.map.addLayer(overpass.osmLayer);}; - overpass.handlers["onPopupReady"] = function(p) {p.openOn(ide.map);}; - overpass.handlers["onDataRecieved"] = function(amount,txt, abortCB,continueCB) {continueCB();}; + overpass.handlers["onGeoJsonReady"] = function() { + ide.map.addLayer(overpass.osmLayer); + }; + overpass.handlers["onPopupReady"] = function(p) { + p.openOn(ide.map); + }; + overpass.handlers["onDataRecieved"] = function( + amount, + txt, + abortCB, + continueCB + ) { + continueCB(); + }; overpass.handlers["onRawDataPresent"] = function() { - parent.postMessage(JSON.stringify({query:settings.code['overpass'], resultType: overpass.resultType, resultText:overpass.resultText}), '*'); - } + parent.postMessage( + JSON.stringify({ + query: settings.code["overpass"], + resultType: overpass.resultType, + resultText: overpass.resultText + }), + "*" + ); + }; // load the data ide.update_map(); }); diff --git a/js/misc.js b/js/misc.js index 0595b586..59f02c30 100644 --- a/js/misc.js +++ b/js/misc.js @@ -11,12 +11,11 @@ * Levenshtein Distance from iD editor project, WTFPL */ export var Base64 = { - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", // public method for encoding - encode : function (input, not_base64url) { + encode: function(input, not_base64url) { var output = ""; //input = Base64._utf8_encode(input); input = unescape(encodeURIComponent(input)); @@ -28,7 +27,6 @@ export var Base64 = { var i = 0; while (i < input.length) { - chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); @@ -44,27 +42,27 @@ export var Base64 = { enc4 = 64; } - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); - + output = + output + + this._keyStr.charAt(enc1) + + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + + this._keyStr.charAt(enc4); } } - if (!not_base64url) - return this._convert_to_base64url(output); - else - return output; + if (!not_base64url) return this._convert_to_base64url(output); + else return output; }, // public method for decoding // this decodes base64url as well as standard base64 with or without padding) - decode : function (input, binary) { + decode: function(input, binary) { var output = ""; input = this._convert_to_base64nopad(input); input = input.replace(/[^A-Za-z0-9\+\/]/g, ""); //reappend the padding - input = input + "==".substring(0,(4-input.length%4)%4); + input = input + "==".substring(0, (4 - input.length % 4) % 4); if (typeof window.btoa == "function") { output = window.atob(input); @@ -74,7 +72,6 @@ export var Base64 = { var i = 0; while (i < input.length) { - enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); @@ -92,14 +89,13 @@ export var Base64 = { if (enc4 != 64) { output = output + String.fromCharCode(chr3); } - } } - + function str2ab(str) { var buf = new ArrayBuffer(str.length); // 1 byte for each char var bufView = new Uint8Array(buf); - for (var i=0, strLen=str.length; i 0) { - output = this._keyStr.charAt(num%64)+output; - num -= num%64; + output = this._keyStr.charAt(num % 64) + output; + num -= num % 64; num /= 64; } - if (neg) - output = "~"+output; - if (!not_base64url) - return this._convert_to_base64url(output); - else - return output; + if (neg) output = "~" + output; + if (!not_base64url) return this._convert_to_base64url(output); + else return output; }, - decodeNum : function(input) { + decodeNum: function(input) { input = this._convert_to_base64nopad(input); input = input.replace(/[^A-Za-z0-9\+\/.]/g, ""); var num = 0; var neg = false; - if (input.charAt(0) == '.') { + if (input.charAt(0) == ".") { neg = true; input = input.substr(1); } - for (var i=0; i 127) && (c < 2048)) { + } else if (c > 127 && c < 2048) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); - } - else { + } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } - } return utftext; }, // private method for UTF-8 decoding - _utf8_decode : function (utftext) { + _utf8_decode: function(utftext) { var string = ""; var i = 0; - var c = c1 = c2 = 0; - - while ( i < utftext.length ) { + var c = (c1 = c2 = 0); + while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; - } - else if((c > 191) && (c < 224)) { - c2 = utftext.charCodeAt(i+1); + } else if (c > 191 && c < 224) { + c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; - } - else { - c2 = utftext.charCodeAt(i+1); - c3 = utftext.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + } else { + c2 = utftext.charCodeAt(i + 1); + c3 = utftext.charCodeAt(i + 2); + string += String.fromCharCode( + ((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63) + ); i += 3; } - } return string; - }, - -} - + } +}; // LZW-compress a string export function lzw_encode(s) { - //s = Base64._utf8_encode(s); - s = unescape(encodeURIComponent(s)); - var dict = {}; - var data = (s + "").split(""); - var out = []; - var currChar; - var phrase = data[0]; - var code = 256; - for (var i=1; i 1 ? dict[phrase] : phrase.charCodeAt(0)); - dict[phrase + currChar] = code; - code++; - phrase=currChar; - } - } - out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0)); - for (var i=0; i 1 ? dict[phrase] : phrase.charCodeAt(0)); + dict[phrase + currChar] = code; + code++; + phrase = currChar; } - return out.join(""); + } + out.push(phrase.length > 1 ? dict[phrase] : phrase.charCodeAt(0)); + for (var i = 0; i < out.length; i++) { + out[i] = String.fromCharCode(out[i]); + } + return out.join(""); } // Decompress an LZW-encoded string export function lzw_decode(s) { - var dict = {}; - var data = (s + "").split(""); - var currChar = data[0]; - var oldPhrase = currChar; - var out = [currChar]; - var code = 256; - var phrase; - for (var i=1; i/g, '>').replace(/"/g, '"'); + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); } - // Levenshtein Distance // from https://github.com/systemed/iD/blob/1e78ee5c87669aac407c69493f3f532c823346ef/js/id/util.js#L97-L115 export function levenshteinDistance(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 - } - } + 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]; + } + return matrix[b.length][a.length]; } - diff --git a/js/nominatim.js b/js/nominatim.js index 9adb7be2..b27b2bcb 100644 --- a/js/nominatim.js +++ b/js/nominatim.js @@ -1,7 +1,7 @@ // nominatim module -import $ from 'jquery'; +import $ from "jquery"; -import configs from './configs'; +import configs from "./configs"; export default function() { var cache = {}; @@ -10,51 +10,56 @@ export default function() { function request(search, callback) { // ajax (GET) request to nominatim - $.ajax("https://nominatim.openstreetmap.org/search"+"?X-Requested-With="+configs.appname, { - data:{ - format:"json", - q: search - }, - success: function(data) { - // hacky firefox hack :( (it is not properly detecting json from the content-type header) - if (typeof data == "string") { // if the data is a string, but looks more like a json object - try { - data = JSON.parse(data); - } catch (e) {} + $.ajax( + "https://nominatim.openstreetmap.org/search" + + "?X-Requested-With=" + + configs.appname, + { + data: { + format: "json", + q: search + }, + success: function(data) { + // hacky firefox hack :( (it is not properly detecting json from the content-type header) + if (typeof data == "string") { + // if the data is a string, but looks more like a json object + try { + data = JSON.parse(data); + } catch (e) {} + } + cache[search] = data; + callback(undefined, data); + }, + error: function() { + var err = + "An error occurred while contacting the osm search server nominatim.openstreetmap.org :("; + console.log(err); + callback(err, null); } - cache[search] = data; - callback(undefined,data); - }, - error: function() { - var err = "An error occurred while contacting the osm search server nominatim.openstreetmap.org :("; - console.log(err); - callback(err,null); - }, - }); + } + ); } nominatim.get = function(search, callback) { - if (cache[search] === undefined) - request(search,callback); - else - callback(undefined,cache[search]); + if (cache[search] === undefined) request(search, callback); + else callback(undefined, cache[search]); return nominatim; }; - nominatim.getBest = function(search,filter, callback) { + nominatim.getBest = function(search, filter, callback) { // shift parameters if filter is omitted - if (!callback) { callback = filter; filter=null; } + if (!callback) { + callback = filter; + filter = null; + } nominatim.get(search, function(err, data) { if (err) { - callback(err,null); + callback(err, null); return; } - if (filter) - data = data.filter(filter); - if (data.length === 0) - callback("No result found",null) - else - callback(err, data[0]); + if (filter) data = data.filter(filter); + if (data.length === 0) callback("No result found", null); + else callback(err, data[0]); }); return nominatim; }; diff --git a/js/overpass.js b/js/overpass.js index 2b00fca8..8585054a 100644 --- a/js/overpass.js +++ b/js/overpass.js @@ -1,19 +1,19 @@ // global overpass object -import $ from 'jquery'; -import _ from 'lodash'; -import L from 'leaflet'; -import L_PopupIcon from 'mapbbcode/src/controls/PopupIcon.js'; -import L_OSM4Leaflet from './OSM4Leaflet'; -import L_GeoJsonNoVanish from './GeoJsonNoVanish'; -import polylabel from 'polylabel'; +import $ from "jquery"; +import _ from "lodash"; +import L from "leaflet"; +import L_PopupIcon from "mapbbcode/src/controls/PopupIcon.js"; +import L_OSM4Leaflet from "./OSM4Leaflet"; +import L_GeoJsonNoVanish from "./GeoJsonNoVanish"; +import polylabel from "polylabel"; -import configs from './configs'; -import settings from './settings'; -import overpass from './overpass'; -import {htmlentities} from './misc'; -import styleparser from './jsmapcss'; +import configs from "./configs"; +import settings from "./settings"; +import overpass from "./overpass"; +import {htmlentities} from "./misc"; +import styleparser from "./jsmapcss"; -var overpass = new(function() { +var overpass = new function() { // == private members == var originalGeom2Layer; // == public members == @@ -22,13 +22,12 @@ var overpass = new(function() { // == private methods == var fire = function() { var name = arguments[0]; - if (typeof overpass.handlers[name] != "function") - return undefined; + if (typeof overpass.handlers[name] != "function") return undefined; var handler_args = []; - for (var i=1; i'; - query = ''+query; + query = "" + query + ""; + query = '' + query; } } fire("onProgress", "calling Overpass API interpreter", function(callback) { // kill the query on abort overpass.ajax_request.abort(); // try to abort queries via kill_my_queries - $.get(server+"kill_my_queries").success(callback).error(function() { + $.get(server + "kill_my_queries").success(callback).error(function() { console.log("Warning: failed to kill query."); callback(); }); @@ -75,7 +89,7 @@ var overpass = new(function() { var request_headers = {}; var additional_get_data = ""; if (settings.force_simple_cors_request) { - additional_get_data = "?X-Requested-With="+configs.appname; + additional_get_data = "?X-Requested-With=" + configs.appname; } else { request_headers["X-Requested-With"] = configs.appname; } @@ -86,580 +100,906 @@ var overpass = new(function() { var data_amount = jqXHR.responseText.length; var data_txt; // round amount of data - var scale = Math.floor(Math.log(data_amount)/Math.log(10)); - data_amount = Math.round(data_amount / Math.pow(10,scale)) * Math.pow(10,scale); - if (data_amount < 1000) - data_txt = data_amount + " bytes"; - else if (data_amount < 1000000) - data_txt = data_amount / 1000 + " kB"; - else - data_txt = data_amount / 1000000 + " MB"; - fire("onProgress", "received about "+data_txt+" of data"); - fire("onDataRecieved", data_amount, data_txt, - function() { // abort callback - fire("onAbort"); - return; - }, function() { // continue callback - // different cases of loaded data: json data, xml data or error message? - var data_mode = null; - var geojson; - var stats = {}; - fire("onProgress", "parsing data"); -setTimeout(function() { - // hacky firefox hack :( (it is not properly detecting json from the content-type header) - if (typeof data == "string" && data[0] == "{") { // if the data is a string, but looks more like a json object - try { - data = $.parseJSON(data); - } catch (e) {} - } - // hacky firefox hack :( (it is not properly detecting xml from the content-type header) - if (typeof data == "string" && data.substr(0,5) == " 0) || - (typeof data == "object" && data.remark && data.remark.length > 0) - ) { // maybe an error message - data_mode = "unknown"; - var is_error = false; - is_error = is_error || (typeof data == "string" && // html coded error messages - data.indexOf("Error") != -1 && - data.indexOf("Public Transport Stops") == -1); // detect output="popup" content - is_error = is_error || (typeof data == "object" && - jqXHR.responseXML && - $("remark",data).length > 0); - is_error = is_error || (typeof data == "object" && - data.remark && - data.remark.length > 0); - if (is_error) { - // this really looks like an error message, so lets open an additional modal error message - var errmsg = "?"; - if (typeof data == "string") { - errmsg = data.replace(/([\S\s]*)/,"").replace(/(<\/body>[\S\s]*)/,""); - // do some magic cleanup for better legibility of the actual error message - errmsg = errmsg.replace(/

    The data included in this document is from .*?<\/p>/,""); - var fullerrmsg = errmsg; - errmsg = errmsg.replace(/open64: 0 Success \/osm3s_v\d+\.\d+\.\d+_osm_base (\w+::)*\w+\./,"[…]"); - } - if (typeof data == "object" && jqXHR.responseXML) - errmsg = "

    "+$.trim($("remark",data).text())+"

    "; - if (typeof data == "object" && data.remark) - errmsg = "

    "+$.trim(data.remark)+"

    "; - console.log("Overpass API error", fullerrmsg || errmsg); // write (full) error message to console for easier debugging - fire("onQueryError", errmsg); - data_mode = "error"; - // parse errors and highlight error lines - var errlines = errmsg.match(/line \d+:/g) || []; - for (var i=0; i meta:first-of-type",data).attr("osm_base"); - overpass.timestampAreas = $("osm > meta:first-of-type",data).attr("areas"); - overpass.copyright = $("osm > note:first-of-type",data).text(); - stats.data = { - nodes: $("osm > node",data).length, - ways: $("osm > way",data).length, - relations: $("osm > relation",data).length, - areas: $("osm > area",data).length - }; - //// convert to geoJSON - //geojson = overpass.overpassXML2geoJSON(data); - } else { // maybe json data - overpass.resultType = "javascript"; - data_mode = "json"; - overpass.timestamp = data.osm3s.timestamp_osm_base; - overpass.timestampAreas = data.osm3s.timestamp_areas_base; - overpass.copyright = data.osm3s.copyright; - stats.data = { - nodes: $.grep(data.elements, function(d) {return d.type=="node"}).length, - ways: $.grep(data.elements, function(d) {return d.type=="way"}).length, - relations: $.grep(data.elements, function(d) {return d.type=="relation"}).length, - areas: $.grep(data.elements, function(d) {return d.type=="area"}).length, - }; - //// convert to geoJSON - //geojson = overpass.overpassJSON2geoJSON(data); - } - - //fire("onProgress", "applying styles"); // doesn't correspond to what's really going on. (the whole code could in principle be put further up and called "preparing mapcss styles" or something, but it's probably not worth the effort) -setTimeout(function() { - // test user supplied mapcss stylesheet - try { - var dummy_mapcss = new styleparser.RuleSet(); - dummy_mapcss.parseCSS(user_mapcss); - try { - dummy_mapcss.getStyles({ - isSubject:function() {return true;}, - getParentObjects: function() {return [];}, - }, [], 18); - } catch(e) { - throw new Error("MapCSS runtime error."); - } - } catch(e) { - user_mapcss = ""; - fire("onStyleError", "

    "+e.message+"

    "); - } - var mapcss = new styleparser.RuleSet(); - mapcss.parseCSS("" - +"node, way, relation {color:black; fill-color:black; opacity:1; fill-opacity: 1; width:10;} \n" - // point features - +"node {color:#03f; width:2; opacity:0.7; fill-color:#fc0; fill-opacity:0.3;} \n" - // line features - +"line {color:#03f; width:5; opacity:0.6;} \n" - // polygon features - +"area {color:#03f; width:2; opacity:0.7; fill-color:#fc0; fill-opacity:0.3;} \n" - // style modifications - // objects in relations - +"relation node, relation way, relation {color:#d0f;} \n" - // tainted objects - +"way:tainted, relation:tainted {dashes:5,8;} \n" - // placeholder points - +"way:placeholder, relation:placeholder {fill-color:#f22;} \n" - // highlighted features - +"node:active, way:active, relation:active {color:#f50; fill-color:#f50;} \n" - // user supplied mapcss - +user_mapcss - ); - var get_feature_style = function(feature, highlight) { - function hasInterestingTags(props) { - // this checks if the node has any tags other than "created_by" - return props && - props.tags && - (function(o){for(var k in o) if(k!="created_by"&&k!="source") return true; return false;})(props.tags); - } - var s = mapcss.getStyles({ - isSubject: function(subject) { - switch (subject) { - case "node": return feature.properties.type == "node" || feature.geometry.type == "Point"; - case "area": return feature.geometry.type == "Polygon" || feature.geometry.type == "MultiPolygon"; - case "line": return feature.geometry.type == "LineString" || feature.geometry.type == "MultiLineString"; - case "way": return feature.properties.type == "way"; - case "relation": return feature.properties.type == "relation"; + var scale = Math.floor(Math.log(data_amount) / Math.log(10)); + data_amount = + Math.round(data_amount / Math.pow(10, scale)) * Math.pow(10, scale); + if (data_amount < 1000) data_txt = data_amount + " bytes"; + else if (data_amount < 1000000) data_txt = data_amount / 1000 + " kB"; + else data_txt = data_amount / 1000000 + " MB"; + fire("onProgress", "received about " + data_txt + " of data"); + fire( + "onDataRecieved", + data_amount, + data_txt, + function() { + // abort callback + fire("onAbort"); + return; + }, + function() { + // continue callback + // different cases of loaded data: json data, xml data or error message? + var data_mode = null; + var geojson; + var stats = {}; + fire("onProgress", "parsing data"); + setTimeout(function() { + // hacky firefox hack :( (it is not properly detecting json from the content-type header) + if (typeof data == "string" && data[0] == "{") { + // if the data is a string, but looks more like a json object + try { + data = $.parseJSON(data); + } catch (e) {} } - return false; - }, - getParentObjects: function() { - if (feature.properties.relations.length == 0) - return []; - else - return feature.properties.relations.map(function(rel) { - return { - tags: rel.reltags, - isSubject: function(subject) { - return subject=="relation" || - (subject=="area" && rel.reltags.type=="multipolyon"); - }, - getParentObjects: function() {return [];}, - } - }); - } - }, $.extend( - feature.properties && feature.properties.tainted ? {":tainted": true} : {}, - feature.properties && feature.properties.geometry ? {":placeholder": true} : {}, - feature.is_placeholder ? {":placeholder": true} : {}, - hasInterestingTags(feature.properties) ? {":tagged":true} : {":untagged": true}, - highlight ? {":active": true} : {}, - feature.properties.tags) - , 18 /*restyle on zoom??*/); - return s; - }; - - L.GeoJSON.geometryToLayer = function(feature, pointToLayer /*,…*/) { - var s = get_feature_style(feature); - var stl = s.textStyles["default"] || {}; - var layer = originalGeom2Layer.apply(this, arguments); - - function getFeatureLabelPosition(feature) { - var latlng; - switch (feature.geometry.type) { - case "Point": - latlng = layer.getLatLng(); - break; - case "MultiPolygon": - var labelPolygon, bestVal = -Infinity; - layer.getLayers().forEach(function(layer) { - var size = layer.getBounds().getNorthEast().distanceTo(layer.getBounds().getSouthWest()); - if (size > bestVal) { - labelPolygon = layer; - bestVal = size; + // hacky firefox hack :( (it is not properly detecting xml from the content-type header) + if ( + typeof data == "string" && + data.substr(0, 5) == " bestVal) { - labelLayer = layer; - bestVal = size; - } - }); - case "LineString": - if (!labelLayer) labelLayer = layer; - var latlngs = labelLayer.getLatLngs(); - if (latlngs.length % 2 == 1) - latlng = latlngs[Math.floor(latlngs.length/2)]; - else { - var latlng1 = latlngs[Math.floor(latlngs.length/2)], - latlng2 = latlngs[Math.floor(latlngs.length/2-1)]; - latlng = L.latLng([ (latlng1.lat+latlng2.lat)/2, (latlng1.lng+latlng2.lng)/2 ]); } - break; - default: - // todo: multipoints - console.error("unsupported geometry type while constructing text label:", feature.geometry.type) - } - return latlng; - } - var text; - if ((stl["text"] && stl.evals["text"] && (text = stl["text"])) - || (stl["text"] && (text = feature.properties.tags[stl["text"]]))) { - var textIcon = new L.PopupIcon(htmlentities(text), {color: "rgba(255,255,255,0.8)"}); - var textmarker = new L.Marker(getFeatureLabelPosition(feature), {icon: textIcon}); - return new L.FeatureGroup(_.compact([layer, textmarker])); - } - return layer; - } - //overpass.geojsonLayer = - //new L.GeoJSON(null, { - //new L.GeoJsonNoVanish(null, { - overpass.osmLayer = - new L_OSM4Leaflet(null, { - afterParse: function() {fire("onProgress", "rendering geoJSON");}, - baseLayerClass: settings.disable_poiomatic ? L.GeoJSON : L_GeoJsonNoVanish, - baseLayerOptions: { - threshold: 9*Math.sqrt(2)*2, - compress: function(feature) { - return true; - }, - style: function(feature, highlight) { - var stl = {}; - var s = get_feature_style(feature, highlight); - // apply mapcss styles - function get_property(styles, properties) { - for (var i=properties.length-1; i>=0; i--) - if (styles[properties[i]] !== undefined) return styles[properties[i]]; - return undefined; - } - switch (feature.geometry.type) { - case "Point": - var styles = $.extend({},s.shapeStyles["default"],s.pointStyles["default"]); - var p = get_property(styles, ["color","symbol_stroke_color"]); - if (p !== undefined) stl.color = p; - var p = get_property(styles, ["opacity","symbol_stroke_opacity"]); - if (p !== undefined) stl.opacity = p; - var p = get_property(styles, ["width","symbol_stroke_width"]); - if (p !== undefined) stl.weight = p; - var p = get_property(styles, ["fill_color", "symbol_fill_color"]); - if (p !== undefined) stl.fillColor = p; - var p = get_property(styles, ["fill_opacity", "symbol_fill_opacity"]); - if (p !== undefined) stl.fillOpacity = p; - var p = get_property(styles, ["dashes"]); - if (p !== undefined) stl.dashArray = p.join(","); - break; - case "LineString": - case "MultiLineString": - var styles = s.shapeStyles["default"]; - var p = get_property(styles, ["color"]); - if (p !== undefined) stl.color = p; - var p = get_property(styles, ["opacity"]); - if (p !== undefined) stl.opacity = p; - var p = get_property(styles, ["width"]); - if (p !== undefined) stl.weight = p; - var p = get_property(styles, ["offset"]); - if (p !== undefined) stl.offset = -p; // MapCSS and PolylineOffset definitions use different signs - var p = get_property(styles, ["dashes"]); - if (p !== undefined) stl.dashArray = p.join(","); - break; - case "Polygon": - case "MultiPolygon": - var styles = s.shapeStyles["default"]; - var p = get_property(styles, ["color","casing_color"]); - if (p !== undefined) stl.color = p; - var p = get_property(styles, ["opacity","casing_opacity"]); - if (p !== undefined) stl.opacity = p; - var p = get_property(styles, ["width","casing_width"]); - if (p !== undefined) stl.weight = p; - var p = get_property(styles, ["fill_color"]); - if (p !== undefined) stl.fillColor = p; - var p = get_property(styles, ["fill_opacity"]); - if (p !== undefined) stl.fillOpacity = p; - var p = get_property(styles, ["dashes"]); - if (p !== undefined) stl.dashArray = p.join(","); - break; - } - // todo: more style properties? linecap, linejoin? - // return style object - return stl; - }, - pointToLayer: function (feature, latlng) { - // todo: labels! - var s = get_feature_style(feature); - var stl = s.pointStyles["default"] || {}; - var text; - var marker; - if (stl["icon_image"]) { - // return image marker - var iconUrl = stl["icon_image"].match(/^url\(['"](.*)['"]\)$/)[1]; - var iconSize; - if (stl["icon_width"]) iconSize=[stl["icon_width"],stl["icon_width"]]; - if (stl["icon_height"] && iconSize) iconSize[1] = stl["icon_height"]; - var icon = new L.Icon({ - iconUrl: iconUrl, - iconSize: iconSize, - // todo: anchor, shadow?, ... - }); - marker = new L.Marker(latlng, {icon: icon}); - } else if (stl["symbol_shape"]=="none") { - marker = new L.Marker(latlng, {icon: new L.DivIcon({iconSize: [0,0], html: "", className: "leaflet-dummy-none-marker"})}); - } else if (stl["symbol_shape"]=="circle" || true /*if nothing else is specified*/) { - // return circle marker - var r = stl["symbol_size"] || 9; - marker = new L.CircleMarker(latlng, { - radius: r, - }); - } - return marker; - }, - onEachFeature : function (feature, layer) { - layer.on('click', function(e) { - var popup = ""; - if (feature.properties.type == "node") - popup += "

    Node "+feature.properties.id+"

    "; - else if (feature.properties.type == "way") - popup += "

    Way "+feature.properties.id+"

    "; - else if (feature.properties.type == "relation") - popup += "

    Relation "+feature.properties.id+"

    "; - else - popup += "

    "+feature.properties.type+" #"+feature.properties.id+"

    "; - if (feature.properties && feature.properties.tags && !$.isEmptyObject(feature.properties.tags)) { - popup += '

    Tags:

      '; - $.each(feature.properties.tags, function(k,v) { - k = htmlentities(k); // escaping strings! - v = htmlentities(v); - // hyperlinks for http,https and ftp URLs - var url; - if (url = v.match(/\b((?:(https?|ftp):\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi)) { - url = url[0]; - var href = url.match(/^(https?|ftp):\/\//) ? url : 'http://'+url; - v = ''+url+'' - } else { - // hyperlinks for email adresses - v = v.replace(/(([^\s()<>]+)@([^\s()<>]+[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/g,'$1'); + if ( + typeof data == "string" || + (typeof data == "object" && + jqXHR.responseXML && + $("remark", data).length > 0) || + (typeof data == "object" && data.remark && data.remark.length > 0) + ) { + // maybe an error message + data_mode = "unknown"; + var is_error = false; + is_error = + is_error || + (typeof data == "string" && // html coded error messages + data.indexOf("Error") != -1 && + data.indexOf("Public Transport Stops") == -1); // detect output="popup" content + is_error = + is_error || + (typeof data == "object" && + jqXHR.responseXML && + $("remark", data).length > 0); + is_error = + is_error || + (typeof data == "object" && + data.remark && + data.remark.length > 0); + if (is_error) { + // this really looks like an error message, so lets open an additional modal error message + var errmsg = "?"; + if (typeof data == "string") { + errmsg = data + .replace(/([\S\s]*)/, "") + .replace(/(<\/body>[\S\s]*)/, ""); + // do some magic cleanup for better legibility of the actual error message + errmsg = errmsg.replace( + /

      The data included in this document is from .*?<\/p>/, + "" + ); + var fullerrmsg = errmsg; + errmsg = errmsg.replace( + /open64: 0 Success \/osm3s_v\d+\.\d+\.\d+_osm_base (\w+::)*\w+\./, + "[…]" + ); + } + if (typeof data == "object" && jqXHR.responseXML) + errmsg = "

      " + $.trim($("remark", data).text()) + "

      "; + if (typeof data == "object" && data.remark) + errmsg = "

      " + $.trim(data.remark) + "

      "; + console.log("Overpass API error", fullerrmsg || errmsg); // write (full) error message to console for easier debugging + fire("onQueryError", errmsg); + data_mode = "error"; + // parse errors and highlight error lines + var errlines = errmsg.match(/line \d+:/g) || []; + for (var i = 0; i < errlines.length; i++) { + fire("onQueryErrorLine", 1 * errlines[i].match(/\d+/)[0]); } - // hyperlinks for wikipedia entries - var wiki_lang, wiki_page; - if (((wiki_lang = k.match(/^wikipedia\:(.*)$/)) && (wiki_page = v)) || - ((k == "wikipedia") && (wiki_lang = v.match(/^([a-zA-Z]+)\:(.*)$/)) && (wiki_page = wiki_lang[2]))) - v = ''+v+''; - // hyperlinks for wikidata entries - var wikidata_page; - if (((k == "wikidata") && (wikidata_page = v.match(/^Q[0-9]+$/))) || - (k.match(/:wikidata$/) && (wikidata_page = v.match(/^Q[0-9]+$/)))) - v = ''+v+''; - popup += "
    • "+k+"="+v+"
    • " - }); - popup += "
    "; - } - if (feature.properties && feature.properties.relations && !$.isEmptyObject(feature.properties.relations)) { - popup += '

    Relations:

      '; - $.each(feature.properties.relations, function (k,v) { - popup += "
    • "+v["rel"]+""; - if (v.reltags && - (v.reltags.name || v.reltags.ref || v.reltags.type)) - popup += " " + - $.trim((v.reltags.type ? htmlentities(v.reltags.type)+" " : "") + - (v.reltags.ref ? htmlentities(v.reltags.ref)+" " : "") + - (v.reltags.name ? htmlentities(v.reltags.name)+" " : "")) + - ""; - if (v["role"]) - popup += " as "+htmlentities(v["role"])+""; - popup += "
    • "; - }); - popup += "
    "; - } - if (feature.properties && feature.properties.meta && !$.isEmptyObject(feature.properties.meta)) { - popup += '

    Meta:

      '; - $.each(feature.properties.meta, function (k,v) { - k = htmlentities(k); - v = htmlentities(v); - if (k == "user") - v = ''+v+''; - if (k == "changeset") - v = ''+v+''; - popup += "
    • "+k+"="+v+"
    • "; - }); - popup += "
    "; - } - if (feature.geometry.type == "Point") - popup += "

    Coordinates:

    "+feature.geometry.coordinates[1]+" / "+feature.geometry.coordinates[0]+" (lat/lon)

    "; - if ($.inArray(feature.geometry.type, ["LineString","Polygon","MultiPolygon"]) != -1) { - if (feature.properties && feature.properties.tainted==true) { - popup += "

    Attention: incomplete geometry (e.g. some nodes missing)

    "; } + // the html error message returned by overpass API looks goods also in xml mode ^^ + overpass.resultType = "error"; + data = {elements: []}; + overpass.timestamp = undefined; + overpass.timestampAreas = undefined; + overpass.copyright = undefined; + stats.data = {nodes: 0, ways: 0, relations: 0, areas: 0}; + //geojson = [{features:[]}, {features:[]}, {features:[]}]; + } else if (typeof data == "object" && jqXHR.responseXML) { + // xml data + overpass.resultType = "xml"; + data_mode = "xml"; + overpass.timestamp = $("osm > meta:first-of-type", data).attr( + "osm_base" + ); + overpass.timestampAreas = $( + "osm > meta:first-of-type", + data + ).attr("areas"); + overpass.copyright = $("osm > note:first-of-type", data).text(); + stats.data = { + nodes: $("osm > node", data).length, + ways: $("osm > way", data).length, + relations: $("osm > relation", data).length, + areas: $("osm > area", data).length + }; + //// convert to geoJSON + //geojson = overpass.overpassXML2geoJSON(data); + } else { + // maybe json data + overpass.resultType = "javascript"; + data_mode = "json"; + overpass.timestamp = data.osm3s.timestamp_osm_base; + overpass.timestampAreas = data.osm3s.timestamp_areas_base; + overpass.copyright = data.osm3s.copyright; + stats.data = { + nodes: $.grep(data.elements, function(d) { + return d.type == "node"; + }).length, + ways: $.grep(data.elements, function(d) { + return d.type == "way"; + }).length, + relations: $.grep(data.elements, function(d) { + return d.type == "relation"; + }).length, + areas: $.grep(data.elements, function(d) { + return d.type == "area"; + }).length + }; + //// convert to geoJSON + //geojson = overpass.overpassJSON2geoJSON(data); } - var latlng; - if (typeof e.target.getLatLng == "function") - latlng = e.target.getLatLng(); // node-ish features (circles, markers, icons, placeholders) - else - latlng = e.latlng; // all other (lines, polygons, multipolygons) - var p = L.popup({maxHeight:600},this).setLatLng(latlng).setContent(popup); - p.layer = layer; - fire("onPopupReady", p); - }); - }, - }}); -setTimeout(function() { - overpass.osmLayer.addData(data,function() { + //fire("onProgress", "applying styles"); // doesn't correspond to what's really going on. (the whole code could in principle be put further up and called "preparing mapcss styles" or something, but it's probably not worth the effort) + setTimeout(function() { + // test user supplied mapcss stylesheet + try { + var dummy_mapcss = new styleparser.RuleSet(); + dummy_mapcss.parseCSS(user_mapcss); + try { + dummy_mapcss.getStyles( + { + isSubject: function() { + return true; + }, + getParentObjects: function() { + return []; + } + }, + [], + 18 + ); + } catch (e) { + throw new Error("MapCSS runtime error."); + } + } catch (e) { + user_mapcss = ""; + fire("onStyleError", "

    " + e.message + "

    "); + } + var mapcss = new styleparser.RuleSet(); + mapcss.parseCSS( + "" + + "node, way, relation {color:black; fill-color:black; opacity:1; fill-opacity: 1; width:10;} \n" + + // point features + "node {color:#03f; width:2; opacity:0.7; fill-color:#fc0; fill-opacity:0.3;} \n" + + // line features + "line {color:#03f; width:5; opacity:0.6;} \n" + + // polygon features + "area {color:#03f; width:2; opacity:0.7; fill-color:#fc0; fill-opacity:0.3;} \n" + + // style modifications + // objects in relations + "relation node, relation way, relation {color:#d0f;} \n" + + // tainted objects + "way:tainted, relation:tainted {dashes:5,8;} \n" + + // placeholder points + "way:placeholder, relation:placeholder {fill-color:#f22;} \n" + + // highlighted features + "node:active, way:active, relation:active {color:#f50; fill-color:#f50;} \n" + + // user supplied mapcss + user_mapcss + ); + var get_feature_style = function(feature, highlight) { + function hasInterestingTags(props) { + // this checks if the node has any tags other than "created_by" + return ( + props && + props.tags && + (function(o) { + for (var k in o) + if (k != "created_by" && k != "source") return true; + return false; + })(props.tags) + ); + } + var s = mapcss.getStyles( + { + isSubject: function(subject) { + switch (subject) { + case "node": + return ( + feature.properties.type == "node" || + feature.geometry.type == "Point" + ); + case "area": + return ( + feature.geometry.type == "Polygon" || + feature.geometry.type == "MultiPolygon" + ); + case "line": + return ( + feature.geometry.type == "LineString" || + feature.geometry.type == "MultiLineString" + ); + case "way": + return feature.properties.type == "way"; + case "relation": + return feature.properties.type == "relation"; + } + return false; + }, + getParentObjects: function() { + if (feature.properties.relations.length == 0) return []; + else + return feature.properties.relations.map(function(rel) { + return { + tags: rel.reltags, + isSubject: function(subject) { + return ( + subject == "relation" || + (subject == "area" && + rel.reltags.type == "multipolyon") + ); + }, + getParentObjects: function() { + return []; + } + }; + }); + } + }, + $.extend( + feature.properties && feature.properties.tainted + ? {":tainted": true} + : {}, + feature.properties && feature.properties.geometry + ? {":placeholder": true} + : {}, + feature.is_placeholder ? {":placeholder": true} : {}, + hasInterestingTags(feature.properties) + ? {":tagged": true} + : {":untagged": true}, + highlight ? {":active": true} : {}, + feature.properties.tags + ), + 18 /*restyle on zoom??*/ + ); + return s; + }; - // save geojson and raw data - geojson = overpass.osmLayer.getGeoJSON(); - overpass.geojson = geojson; - overpass.data = data; + L.GeoJSON.geometryToLayer = function( + feature, + pointToLayer /*,…*/ + ) { + var s = get_feature_style(feature); + var stl = s.textStyles["default"] || {}; + var layer = originalGeom2Layer.apply(this, arguments); - // calc stats - stats.geojson = { - polys: 0, - lines: 0, - pois: 0 - }; - for (var i=0; i bestVal) { + labelPolygon = layer; + bestVal = size; + } + }); + case "Polygon": + if (!labelPolygon) labelPolygon = layer; + // FIXME ide.map is not defined since ide is not imported + latlng = ide.map.unproject( + polylabel( + [labelPolygon.getLatLngs()] + .concat(labelPolygon._holes) + .map(function(ring) { + return ring + .map(function(latlng) { + return ide.map.project(latlng); + }) + .map(function(p) { + return [p.x, p.y]; + }); + }) + ) + ); + break; + case "MultiLineString": + var labelLayer, bestVal = -Infinity; + layer.getLayers().forEach(function(layer) { + var size = layer + .getBounds() + .getNorthEast() + .distanceTo(layer.getBounds().getSouthWest()); + if (size > bestVal) { + labelLayer = layer; + bestVal = size; + } + }); + case "LineString": + if (!labelLayer) labelLayer = layer; + var latlngs = labelLayer.getLatLngs(); + if (latlngs.length % 2 == 1) + latlng = latlngs[Math.floor(latlngs.length / 2)]; + else { + var latlng1 = latlngs[Math.floor(latlngs.length / 2)], + latlng2 = latlngs[Math.floor(latlngs.length / 2 - 1)]; + latlng = L.latLng([ + (latlng1.lat + latlng2.lat) / 2, + (latlng1.lng + latlng2.lng) / 2 + ]); + } + break; + default: + // todo: multipoints + console.error( + "unsupported geometry type while constructing text label:", + feature.geometry.type + ); + } + return latlng; + } + var text; + if ( + (stl["text"] && stl.evals["text"] && (text = stl["text"])) || + (stl["text"] && (text = feature.properties.tags[stl["text"]])) + ) { + var textIcon = new L.PopupIcon(htmlentities(text), { + color: "rgba(255,255,255,0.8)" + }); + var textmarker = new L.Marker( + getFeatureLabelPosition(feature), + {icon: textIcon} + ); + return new L.FeatureGroup(_.compact([layer, textmarker])); + } + return layer; + }; + //overpass.geojsonLayer = + //new L.GeoJSON(null, { + //new L.GeoJsonNoVanish(null, { + overpass.osmLayer = new L_OSM4Leaflet(null, { + afterParse: function() { + fire("onProgress", "rendering geoJSON"); + }, + baseLayerClass: settings.disable_poiomatic + ? L.GeoJSON + : L_GeoJsonNoVanish, + baseLayerOptions: { + threshold: 9 * Math.sqrt(2) * 2, + compress: function(feature) { + return true; + }, + style: function(feature, highlight) { + var stl = {}; + var s = get_feature_style(feature, highlight); + // apply mapcss styles + function get_property(styles, properties) { + for (var i = properties.length - 1; i >= 0; i--) + if (styles[properties[i]] !== undefined) + return styles[properties[i]]; + return undefined; + } + switch (feature.geometry.type) { + case "Point": + var styles = $.extend( + {}, + s.shapeStyles["default"], + s.pointStyles["default"] + ); + var p = get_property(styles, [ + "color", + "symbol_stroke_color" + ]); + if (p !== undefined) stl.color = p; + var p = get_property(styles, [ + "opacity", + "symbol_stroke_opacity" + ]); + if (p !== undefined) stl.opacity = p; + var p = get_property(styles, [ + "width", + "symbol_stroke_width" + ]); + if (p !== undefined) stl.weight = p; + var p = get_property(styles, [ + "fill_color", + "symbol_fill_color" + ]); + if (p !== undefined) stl.fillColor = p; + var p = get_property(styles, [ + "fill_opacity", + "symbol_fill_opacity" + ]); + if (p !== undefined) stl.fillOpacity = p; + var p = get_property(styles, ["dashes"]); + if (p !== undefined) stl.dashArray = p.join(","); + break; + case "LineString": + case "MultiLineString": + var styles = s.shapeStyles["default"]; + var p = get_property(styles, ["color"]); + if (p !== undefined) stl.color = p; + var p = get_property(styles, ["opacity"]); + if (p !== undefined) stl.opacity = p; + var p = get_property(styles, ["width"]); + if (p !== undefined) stl.weight = p; + var p = get_property(styles, ["offset"]); + if (p !== undefined) stl.offset = -p; // MapCSS and PolylineOffset definitions use different signs + var p = get_property(styles, ["dashes"]); + if (p !== undefined) stl.dashArray = p.join(","); + break; + case "Polygon": + case "MultiPolygon": + var styles = s.shapeStyles["default"]; + var p = get_property(styles, ["color", "casing_color"]); + if (p !== undefined) stl.color = p; + var p = get_property(styles, [ + "opacity", + "casing_opacity" + ]); + if (p !== undefined) stl.opacity = p; + var p = get_property(styles, ["width", "casing_width"]); + if (p !== undefined) stl.weight = p; + var p = get_property(styles, ["fill_color"]); + if (p !== undefined) stl.fillColor = p; + var p = get_property(styles, ["fill_opacity"]); + if (p !== undefined) stl.fillOpacity = p; + var p = get_property(styles, ["dashes"]); + if (p !== undefined) stl.dashArray = p.join(","); + break; + } + // todo: more style properties? linecap, linejoin? + // return style object + return stl; + }, + pointToLayer: function(feature, latlng) { + // todo: labels! + var s = get_feature_style(feature); + var stl = s.pointStyles["default"] || {}; + var text; + var marker; + if (stl["icon_image"]) { + // return image marker + var iconUrl = stl["icon_image"].match( + /^url\(['"](.*)['"]\)$/ + )[1]; + var iconSize; + if (stl["icon_width"]) + iconSize = [stl["icon_width"], stl["icon_width"]]; + if (stl["icon_height"] && iconSize) + iconSize[1] = stl["icon_height"]; + var icon = new L.Icon({ + iconUrl: iconUrl, + iconSize: iconSize + // todo: anchor, shadow?, ... + }); + marker = new L.Marker(latlng, {icon: icon}); + } else if (stl["symbol_shape"] == "none") { + marker = new L.Marker(latlng, { + icon: new L.DivIcon({ + iconSize: [0, 0], + html: "", + className: "leaflet-dummy-none-marker" + }) + }); + } else if ( + stl["symbol_shape"] == "circle" || + true /*if nothing else is specified*/ + ) { + // return circle marker + var r = stl["symbol_size"] || 9; + marker = new L.CircleMarker(latlng, { + radius: r + }); + } + return marker; + }, + onEachFeature: function(feature, layer) { + layer.on("click", function(e) { + var popup = ""; + if (feature.properties.type == "node") + popup += + "

    Node " + + feature.properties.id + + "

    "; + else if (feature.properties.type == "way") + popup += + "

    Way " + + feature.properties.id + + "

    "; + else if (feature.properties.type == "relation") + popup += + "

    Relation " + + feature.properties.id + + "

    "; + else + popup += + "

    " + + feature.properties.type + + " #" + + feature.properties.id + + "

    "; + if ( + feature.properties && + feature.properties.tags && + !$.isEmptyObject(feature.properties.tags) + ) { + popup += '

    Tags:

      '; + $.each(feature.properties.tags, function(k, v) { + k = htmlentities(k); // escaping strings! + v = htmlentities(v); + // hyperlinks for http,https and ftp URLs + var url; + if ( + (url = v.match( + /\b((?:(https?|ftp):\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi + )) + ) { + url = url[0]; + var href = url.match(/^(https?|ftp):\/\//) + ? url + : "http://" + url; + v = + '' + + url + + ""; + } else { + // hyperlinks for email adresses + v = v.replace( + /(([^\s()<>]+)@([^\s()<>]+[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/g, + '$1' + ); + } + // hyperlinks for wikipedia entries + var wiki_lang, wiki_page; + if ( + ((wiki_lang = k.match(/^wikipedia\:(.*)$/)) && + (wiki_page = v)) || + (k == "wikipedia" && + (wiki_lang = v.match(/^([a-zA-Z]+)\:(.*)$/)) && + (wiki_page = wiki_lang[2])) + ) + v = + '' + + v + + ""; + // hyperlinks for wikidata entries + var wikidata_page; + if ( + (k == "wikidata" && + (wikidata_page = v.match(/^Q[0-9]+$/))) || + (k.match(/:wikidata$/) && + (wikidata_page = v.match(/^Q[0-9]+$/))) + ) + v = + '' + + v + + ""; + popup += "
    • " + k + "=" + v + "
    • "; + }); + popup += "
    "; + } + if ( + feature.properties && + feature.properties.relations && + !$.isEmptyObject(feature.properties.relations) + ) { + popup += '

    Relations:

      '; + $.each(feature.properties.relations, function(k, v) { + popup += + "
    • " + + v["rel"] + + ""; + if ( + v.reltags && + (v.reltags.name || v.reltags.ref || v.reltags.type) + ) + popup += + " " + + $.trim( + (v.reltags.type + ? htmlentities(v.reltags.type) + " " + : "") + + (v.reltags.ref + ? htmlentities(v.reltags.ref) + " " + : "") + + (v.reltags.name + ? htmlentities(v.reltags.name) + " " + : "") + ) + + ""; + if (v["role"]) + popup += + " as " + htmlentities(v["role"]) + ""; + popup += "
    • "; + }); + popup += "
    "; + } + if ( + feature.properties && + feature.properties.meta && + !$.isEmptyObject(feature.properties.meta) + ) { + popup += '

    Meta:

      '; + $.each(feature.properties.meta, function(k, v) { + k = htmlentities(k); + v = htmlentities(v); + if (k == "user") + v = + '' + + v + + ""; + if (k == "changeset") + v = + '' + + v + + ""; + popup += "
    • " + k + "=" + v + "
    • "; + }); + popup += "
    "; + } + if (feature.geometry.type == "Point") + popup += + "

    Coordinates:

    " + + feature.geometry.coordinates[1] + + " / " + + feature.geometry.coordinates[0] + + " (lat/lon)

    "; + if ( + $.inArray(feature.geometry.type, [ + "LineString", + "Polygon", + "MultiPolygon" + ]) != -1 + ) { + if ( + feature.properties && + feature.properties.tainted == true + ) { + popup += + "

    Attention: incomplete geometry (e.g. some nodes missing)

    "; + } + } + var latlng; + if (typeof e.target.getLatLng == "function") + latlng = e.target.getLatLng(); // node-ish features (circles, markers, icons, placeholders) + else latlng = e.latlng; // all other (lines, polygons, multipolygons) + var p = L.popup({maxHeight: 600}, this) + .setLatLng(latlng) + .setContent(popup); + p.layer = layer; + fire("onPopupReady", p); + }); + } + } + }); - if (!shouldCacheOnly) - fire("onGeoJsonReady"); + setTimeout(function() { + overpass.osmLayer.addData(data, function() { + // save geojson and raw data + geojson = overpass.osmLayer.getGeoJSON(); + overpass.geojson = geojson; + overpass.data = data; - // print raw data - fire("onProgress", "printing raw data"); -setTimeout(function() { - overpass.resultText = jqXHR.responseText; - fire("onRawDataPresent"); + // calc stats + stats.geojson = { + polys: 0, + lines: 0, + pois: 0 + }; + for ( + var i = 0; + i < geojson.features.length; + i++ + ) switch (geojson.features[i].geometry.type) { + case "Polygon": + case "MultiPolygon": + stats.geojson.polys++; + break; + case "LineString": + case "MultiLineString": + stats.geojson.lines++; + break; + case "Point": + case "MultiPoint": + stats.geojson.pois++; + break; + } + overpass.stats = stats; - // todo: the following would profit from some unit testing - // this is needed for auto-tab-switching: if there is only non map-visible data, show it directly - if (geojson.features.length === 0) { // no visible data - // switch only if there is some unplottable data in the returned json/xml. - var empty_msg; - if ((data_mode == "json" && data.elements.length > 0) || - (data_mode == "xml" && $("osm",data).children().not("note,meta,bounds").length > 0)) { - // check for "only areas returned" - if ((data_mode == "json" && _.all(data.elements, {type:"area"})) || - (data_mode == "xml" && $("osm",data).children().not("note,meta,bounds,area").length == 0)) - empty_msg = "only areas returned"; - // check for "ids_only" or "tags" on nodes - else if ((data_mode == "json" && _.some(data.elements, {type:"node"})) || - (data_mode == "xml" && $("osm",data).children().filter("node").length > 0)) - empty_msg = "no coordinates returned"; - // check for "ids_only" or "tags" on ways - else if ((data_mode == "json" && _.some(data.elements, {type:"way"}) && !_.some(_.filter(data.elements, {type:"way"}), "nodes")) || - (data_mode == "xml" && $("osm",data).children().filter("way").length > 0 && $("osm",data).children().filter("way").children().filter("nd").length == 0)) - empty_msg = "no coordinates returned"; - // check for "ids_only" or "tags" on relations - else if ((data_mode == "json" && _.some(data.elements, {type:"relation"}) && !_.some(_.filter(data.elements, {type:"relation"}), "members")) || - (data_mode == "xml" && $("osm",data).children().filter("relation").length > 0 && $("osm",data).children().filter("relation").children().filter("member").length == 0)) - empty_msg = "no coordinates returned"; - else - empty_msg = "no visible data"; - } else if(data_mode == "error") { - empty_msg = "an error occured"; - } else if(data_mode == "unknown") { - empty_msg = "unstructured data returned"; - } else { - empty_msg = "received empty dataset"; - } - // show why there is an empty map - fire("onEmptyMap", empty_msg, data_mode); - } + if (!shouldCacheOnly) fire("onGeoJsonReady"); - // closing wait spinner - fire("onDone"); -},1); // end setTimeout - }); -},1); // end setTimeout -},1); // end setTimeout -},1); // end setTimeout - }); + // print raw data + fire("onProgress", "printing raw data"); + setTimeout(function() { + overpass.resultText = jqXHR.responseText; + fire("onRawDataPresent"); + + // todo: the following would profit from some unit testing + // this is needed for auto-tab-switching: if there is only non map-visible data, show it directly + if (geojson.features.length === 0) { + // no visible data + // switch only if there is some unplottable data in the returned json/xml. + var empty_msg; + if ( + (data_mode == "json" && data.elements.length > 0) || + (data_mode == "xml" && + $("osm", data).children().not("note,meta,bounds") + .length > 0) + ) { + // check for "only areas returned" + if ( + (data_mode == "json" && + _.all(data.elements, {type: "area"})) || + (data_mode == "xml" && + $("osm", data) + .children() + .not("note,meta,bounds,area").length == 0) + ) + empty_msg = "only areas returned"; + else if ( + (data_mode == "json" && + _.some(data.elements, {type: "node"})) || + (data_mode == "xml" && + $("osm", data).children().filter("node").length > 0) + ) + // check for "ids_only" or "tags" on nodes + empty_msg = "no coordinates returned"; + else if ( + (data_mode == "json" && + _.some(data.elements, {type: "way"}) && + !_.some( + _.filter(data.elements, {type: "way"}), + "nodes" + )) || + (data_mode == "xml" && + $("osm", data).children().filter("way").length > + 0 && + $("osm", data) + .children() + .filter("way") + .children() + .filter("nd").length == 0) + ) + // check for "ids_only" or "tags" on ways + empty_msg = "no coordinates returned"; + else if ( + (data_mode == "json" && + _.some(data.elements, {type: "relation"}) && + !_.some( + _.filter(data.elements, {type: "relation"}), + "members" + )) || + (data_mode == "xml" && + $("osm", data).children().filter("relation") + .length > 0 && + $("osm", data) + .children() + .filter("relation") + .children() + .filter("member").length == 0) + ) + // check for "ids_only" or "tags" on relations + empty_msg = "no coordinates returned"; + else empty_msg = "no visible data"; + } else if (data_mode == "error") { + empty_msg = "an error occured"; + } else if (data_mode == "unknown") { + empty_msg = "unstructured data returned"; + } else { + empty_msg = "received empty dataset"; + } + // show why there is an empty map + fire("onEmptyMap", empty_msg, data_mode); + } + + // closing wait spinner + fire("onDone"); + }, 1); // end setTimeout + }); + }, 1); // end setTimeout + }, 1); // end setTimeout + }, 1); // end setTimeout + } + ); }; - if(cache && cache.hasOwnProperty(query)) { + if (cache && cache.hasOwnProperty(query)) { onSuccessCb.apply(this, cache[query]); } else { - overpass.ajax_request = $.ajax(server+"interpreter"+additional_get_data, { - type: 'POST', - data: {data:query}, - headers: request_headers, - success: onSuccessCb, - error: function(jqXHR, textStatus, errorThrown) { - if (textStatus == "abort") - return; // ignore aborted queries. - fire("onProgress", "error during ajax call"); - if (jqXHR.status == 400 || jqXHR.status == 504 || jqXHR.status == 429) { // todo: handle those in a separate routine - // pass 400 Bad Request errors to the standard result parser, as this is most likely going to be a syntax error in the query. - this.success(jqXHR.responseText, textStatus, jqXHR); - return; + overpass.ajax_request = $.ajax( + server + "interpreter" + additional_get_data, + { + type: "POST", + data: {data: query}, + headers: request_headers, + success: onSuccessCb, + error: function(jqXHR, textStatus, errorThrown) { + if (textStatus == "abort") return; // ignore aborted queries. + fire("onProgress", "error during ajax call"); + if ( + jqXHR.status == 400 || + jqXHR.status == 504 || + jqXHR.status == 429 + ) { + // todo: handle those in a separate routine + // pass 400 Bad Request errors to the standard result parser, as this is most likely going to be a syntax error in the query. + this.success(jqXHR.responseText, textStatus, jqXHR); + return; + } + overpass.resultText = jqXHR.resultText; + var errmsg = ""; + if (jqXHR.state() == "rejected") + errmsg += + "

    Request rejected. (e.g. server not found, request blocked by browser addon, request redirected, internal server errors, etc.)

    "; + if (textStatus == "parsererror") + errmsg += "

    Error while parsing the data (parsererror).

    "; + else if (textStatus != "error" && textStatus != jqXHR.statusText) + errmsg += "

    Error-Code: " + textStatus + "

    "; + if ( + (jqXHR.status != 0 && jqXHR.status != 200) || + jqXHR.statusText != "OK" // note to me: jqXHR.status "should" give http status codes + ) + errmsg += + "

    Error-Code: " + + jqXHR.statusText + + " (" + + jqXHR.status + + ")

    "; + fire("onAjaxError", errmsg); + // closing wait spinner + fire("onDone"); + } } - overpass.resultText = jqXHR.resultText; - var errmsg = ""; - if (jqXHR.state() == "rejected") - errmsg += "

    Request rejected. (e.g. server not found, request blocked by browser addon, request redirected, internal server errors, etc.)

    "; - if (textStatus == "parsererror") - errmsg += "

    Error while parsing the data (parsererror).

    "; - else if (textStatus != "error" && textStatus != jqXHR.statusText) - errmsg += "

    Error-Code: "+textStatus+"

    "; - if ((jqXHR.status != 0 && jqXHR.status != 200) || jqXHR.statusText != "OK") // note to me: jqXHR.status "should" give http status codes - errmsg += "

    Error-Code: "+jqXHR.statusText+" ("+jqXHR.status+")

    "; - fire("onAjaxError", errmsg); - // closing wait spinner - fire("onDone"); - }, - }); // getJSON - + ); // getJSON } - - } + }; // == initializations == -})(); // end create overpass object +}(); // end create overpass object export default overpass; diff --git a/js/promise-polyfill.js b/js/promise-polyfill.js index 914eff42..3a34b78a 100644 --- a/js/promise-polyfill.js +++ b/js/promise-polyfill.js @@ -1,4 +1,4 @@ -import Promise from 'promise-polyfill'; +import Promise from "promise-polyfill"; // To add to window if (!window.Promise) { diff --git a/js/query.js b/js/query.js index 82b676e3..e20808a6 100644 --- a/js/query.js +++ b/js/query.js @@ -1,5 +1,5 @@ // query parser module -import _ from 'lodash'; +import _ from "lodash"; export default function query() { var statements = {}; @@ -12,7 +12,7 @@ export default function query() { if (_found_statements) statements = _found_statements; var statement = /{{([A-Za-z0-9_]+):([\s\S]*?)}}/; var s; - while (s = query.match(statement)) { + while ((s = query.match(statement))) { var s_name = s[1]; var s_instr = s[2]; var s_replace = ""; @@ -24,44 +24,51 @@ export default function query() { // these shortcuts can also be callback functions, like {{date:-1day}} if (typeof shortcuts[s_name] === "function") { shortcuts[s_name](s_instr, function(res) { - var seed = Math.round(Math.random()*Math.pow(2,22)); // todo: use some kind of checksum of s_instr if possible - shortcuts["__statement__"+s_name+"__"+seed] = res; - query = query.replace("{{"+s_name+":"+s_instr+"}}", "{{__statement__"+s_name+"__"+seed+":"+s_instr+"}}"); + var seed = Math.round(Math.random() * Math.pow(2, 22)); // todo: use some kind of checksum of s_instr if possible + shortcuts["__statement__" + s_name + "__" + seed] = res; + query = query.replace( + "{{" + s_name + ":" + s_instr + "}}", + "{{__statement__" + s_name + "__" + seed + ":" + s_instr + "}}" + ); // recursively call the parser with updated shortcuts parser.parse(query, shortcuts, callback, statements); }); return; - } else - s_replace = shortcuts[s_name]; + } else s_replace = shortcuts[s_name]; } // remove statement, but preserve number of newlines var lc = s_instr.split(/\r?\n|\r/).length; - query = query.replace("{{"+s_name+":"+s_instr+"}}", s_replace+Array(lc).join('\n')); + query = query.replace( + "{{" + s_name + ":" + s_instr + "}}", + s_replace + Array(lc).join("\n") + ); } // 2. get user defined constants var constants = {}; var constant = /{{([A-Za-z0-9_]+)=(.+?)}}/; var c; - while (c = query.match(constant)) { + while ((c = query.match(constant))) { var c_name = c[1]; var c_val = c[2]; constants[c_name] = c_val; // remove constant definitions - query = query.replace(constant, ''); + query = query.replace(constant, ""); } // 3. expand shortcuts (global and user defined) - _.extend(constants, shortcuts, function(a,b) { return (typeof a == 'undefined') ? b : a; }); + _.extend(constants, shortcuts, function(a, b) { + return typeof a == "undefined" ? b : a; + }); for (var c_name in constants) { var c_val = constants[c_name]; - query = query.replace(new RegExp('{{'+c_name+'}}','g'), c_val); + query = query.replace(new RegExp("{{" + c_name + "}}", "g"), c_val); } // 4. remove remaining (e.g. unknown) mustache templates: var m; - while (m = query.match(/{{[\S\s]*?}}/gm)) { - // count lines in template and replace mustache with same number of newlines + while ((m = query.match(/{{[\S\s]*?}}/gm))) { + // count lines in template and replace mustache with same number of newlines var lc = m[0].split(/\r?\n|\r/).length; query = query.replace(m[0], Array(lc).join("\n")); - }; + } // return the query callback(query); }; @@ -75,4 +82,4 @@ export default function query() { }; return parser; -}; \ No newline at end of file +} diff --git a/js/settings.js b/js/settings.js index 0c3eb824..95497cfd 100644 --- a/js/settings.js +++ b/js/settings.js @@ -1,39 +1,56 @@ // Settings class -import _ from 'lodash'; +import _ from "lodash"; -import configs from './configs'; +import configs from "./configs"; -function Settings(namespace,version) { +function Settings(namespace, version) { // == private members == - var prefix = namespace+"_"; - var ls = {setItem:function(n,v){this[n]=v;}, getItem:function(n){return this[n]!==undefined?this[n]:null;}}; try { localStorage.setItem(prefix+"test",123); localStorage.removeItem(prefix+"test"); ls = localStorage; } catch(e) {}; + var prefix = namespace + "_"; + var ls = { + setItem: function(n, v) { + this[n] = v; + }, + getItem: function(n) { + return this[n] !== undefined ? this[n] : null; + } + }; + try { + localStorage.setItem(prefix + "test", 123); + localStorage.removeItem(prefix + "test"); + ls = localStorage; + } catch (e) {} var settings_version = version; - var version = +ls.getItem(prefix+"version"); + var version = +ls.getItem(prefix + "version"); var settings = {}; var upgrade_callbacks = []; - + // == public methods == - this.define_setting = function(name,type,preset,version) { - settings[name] = {"type":type,"preset":preset,"version":version}; + this.define_setting = function(name, type, preset, version) { + settings[name] = {type: type, preset: preset, version: version}; }; - this.define_upgrade_callback = function(version,fun) { + this.define_upgrade_callback = function(version, fun) { upgrade_callbacks[version] = fun; }; - this.set = function(name,value) { - if (value === undefined) // use preset if no value is given + this.set = function(name, value) { + if ( + value === undefined // use preset if no value is given + ) value = settings[name].preset; - if(settings[name].type != "String") // stringify all non-string values. + if ( + settings[name].type != "String" // stringify all non-string values. + ) value = JSON.stringify(value); - ls.setItem(prefix+name, value); + ls.setItem(prefix + name, value); }; this.get = function(name) { // initialize new settings - if (settings[name].version > version) - this.set(name,undefined); + if (settings[name].version > version) this.set(name, undefined); // load the setting - var value = ls.getItem(prefix+name); - if (settings[name].type != "String") // parse all non-string values. + var value = ls.getItem(prefix + name); + if ( + settings[name].type != "String" // parse all non-string values. + ) value = JSON.parse(value); return value; }; @@ -44,32 +61,43 @@ function Settings(namespace,version) { this[name] = this.get(name); } // version upgrade - if (version == 0) - this.first_time_visit = true; + if (version == 0) this.first_time_visit = true; if (version < settings_version) { - for (var v = version+1; v<=settings_version; v++) { + for (var v = version + 1; v <= settings_version; v++) { if (typeof upgrade_callbacks[v] == "function") upgrade_callbacks[v](this); } version = settings_version; - ls.setItem(prefix+"version",version); + ls.setItem(prefix + "version", version); } }; this.save = function() { // save all settings from the objects namespace for (var name in settings) { - this.set(name,this[name]); + this.set(name, this[name]); } }; } // examples var examples = { - "Drinking Water":{"overpass":"/*\nThis is an example Overpass query.\nTry it out by pressing the Run button above!\nYou can find more examples with the Load tool.\n*/\nnode\n [amenity=drinking_water]\n ({{bbox}});\nout;"}, - "Cycle Network":{"overpass":"/*\nThis shows the cycleway and cycleroute network.\n*/\n\n[out:json];\n\n(\n // get cycle route relatoins\n relation[route=bicycle]({{bbox}})->.cr;\n // get cycleways\n way[highway=cycleway]({{bbox}});\n way[highway=path][bicycle=designated]({{bbox}});\n);\n\nout body;\n>;\nout skel qt;"}, - "Where am I?":{"overpass":"/*\nThis lists all areas which include the map center point.\n*/\n[out:json];\nis_in({{center}});\nout;"}, - "Mountains in Area":{"overpass":"/*\nThis shows all mountains (peaks) in the Dolomites.\nYou may want to use the \"zoom onto data\" button. =>\n*/\n\n[out:json];\n\n// search the area of the Dolmites\narea\n [place=region]\n [\"region:type\"=\"mountain_area\"]\n [\"name:en\"=\"Dolomites\"];\nout body;\n\n// get all peaks in the area\nnode\n [natural=peak]\n (area);\nout body qt;\n\n// additionally, show the outline of the area\nrelation\n [place=region]\n [\"region:type\"=\"mountain_area\"]\n [\"name:en\"=\"Dolomites\"];\nout body;\n>;\nout skel qt;"}, - "Map Call":{"overpass":"/*\nThis is a simple map call.\nIt returns all data in the bounding box.\n*/\n[out:xml];\n(\n node({{bbox}});\n <;\n);\nout meta;"}, - "MapCSS styling": {"overpass": "/*\nThis example shows how the data can be styled.\nHere, some common amenities are displayed in \ndifferent colors.\n\nRead more: http://wiki.openstreetmap.org/wiki/Overpass_turbo/MapCSS\n*/\n[out:json];\n\n(\n node[amenity]({{bbox}});\n way[amenity]({{bbox}});\n relation[amenity]({{bbox}});\n);\nout body;\n>;\nout skel qt;\n\n{{style: /* this is the MapCSS stylesheet */\nnode, area\n{ color:gray; fill-color:gray; }\n\nnode[amenity=drinking_water],\nnode[amenity=fountain]\n{ color:blue; fill-color:blue; }\n\nnode[amenity=place_of_worship],\narea[amenity=place_of_worship]\n{ color:grey; fill-color:grey; }\n\nnode[amenity=~/(restaurant|hotel|cafe)/],\narea[amenity=~/(restaurant|hotel|cafe)/]\n{ color:red; fill-color:red; }\n\nnode[amenity=parking],\narea[amenity=parking]\n{ color:yellow; fill-color:yellow; }\n\nnode[amenity=bench]\n{ color:brown; fill-color:brown; }\n\nnode[amenity=~/(kindergarten|school|university)/],\narea[amenity=~/(kindergarten|school|university)/]\n{ color:green; fill-color:green; }\n}}"}, + "Drinking Water": { + overpass: "/*\nThis is an example Overpass query.\nTry it out by pressing the Run button above!\nYou can find more examples with the Load tool.\n*/\nnode\n [amenity=drinking_water]\n ({{bbox}});\nout;" + }, + "Cycle Network": { + overpass: "/*\nThis shows the cycleway and cycleroute network.\n*/\n\n[out:json];\n\n(\n // get cycle route relatoins\n relation[route=bicycle]({{bbox}})->.cr;\n // get cycleways\n way[highway=cycleway]({{bbox}});\n way[highway=path][bicycle=designated]({{bbox}});\n);\n\nout body;\n>;\nout skel qt;" + }, + "Where am I?": { + overpass: "/*\nThis lists all areas which include the map center point.\n*/\n[out:json];\nis_in({{center}});\nout;" + }, + "Mountains in Area": { + overpass: '/*\nThis shows all mountains (peaks) in the Dolomites.\nYou may want to use the "zoom onto data" button. =>\n*/\n\n[out:json];\n\n// search the area of the Dolmites\narea\n [place=region]\n ["region:type"="mountain_area"]\n ["name:en"="Dolomites"];\nout body;\n\n// get all peaks in the area\nnode\n [natural=peak]\n (area);\nout body qt;\n\n// additionally, show the outline of the area\nrelation\n [place=region]\n ["region:type"="mountain_area"]\n ["name:en"="Dolomites"];\nout body;\n>;\nout skel qt;' + }, + "Map Call": { + overpass: "/*\nThis is a simple map call.\nIt returns all data in the bounding box.\n*/\n[out:xml];\n(\n node({{bbox}});\n <;\n);\nout meta;" + }, + "MapCSS styling": { + overpass: "/*\nThis example shows how the data can be styled.\nHere, some common amenities are displayed in \ndifferent colors.\n\nRead more: http://wiki.openstreetmap.org/wiki/Overpass_turbo/MapCSS\n*/\n[out:json];\n\n(\n node[amenity]({{bbox}});\n way[amenity]({{bbox}});\n relation[amenity]({{bbox}});\n);\nout body;\n>;\nout skel qt;\n\n{{style: /* this is the MapCSS stylesheet */\nnode, area\n{ color:gray; fill-color:gray; }\n\nnode[amenity=drinking_water],\nnode[amenity=fountain]\n{ color:blue; fill-color:blue; }\n\nnode[amenity=place_of_worship],\narea[amenity=place_of_worship]\n{ color:grey; fill-color:grey; }\n\nnode[amenity=~/(restaurant|hotel|cafe)/],\narea[amenity=~/(restaurant|hotel|cafe)/]\n{ color:red; fill-color:red; }\n\nnode[amenity=parking],\narea[amenity=parking]\n{ color:yellow; fill-color:yellow; }\n\nnode[amenity=bench]\n{ color:brown; fill-color:brown; }\n\nnode[amenity=~/(kindergarten|school|university)/],\narea[amenity=~/(kindergarten|school|university)/]\n{ color:green; fill-color:green; }\n}}" + } }; var examples_initial_example = "Drinking Water"; @@ -82,38 +110,48 @@ var settings = new Settings( export default settings; // map coordinates -settings.define_setting("coords_lat","Float",configs.defaultMapView.lat,1); -settings.define_setting("coords_lon","Float",configs.defaultMapView.lon,1); -settings.define_setting("coords_zoom","Integer",configs.defaultMapView.zoom,1); +settings.define_setting("coords_lat", "Float", configs.defaultMapView.lat, 1); +settings.define_setting("coords_lon", "Float", configs.defaultMapView.lon, 1); +settings.define_setting( + "coords_zoom", + "Integer", + configs.defaultMapView.zoom, + 1 +); // saves -settings.define_setting("code","Object",examples[examples_initial_example],1); -settings.define_setting("saves","Object",examples,1); +settings.define_setting( + "code", + "Object", + examples[examples_initial_example], + 1 +); +settings.define_setting("saves", "Object", examples, 1); // api server -settings.define_setting("server","String",configs.defaultServer,1); +settings.define_setting("server", "String", configs.defaultServer, 1); // sharing options -settings.define_setting("share_compression","String","auto",1); -settings.define_setting("share_include_pos","Boolean",false,1); +settings.define_setting("share_compression", "String", "auto", 1); +settings.define_setting("share_include_pos", "Boolean", false, 1); // code editor & map view -settings.define_setting("use_rich_editor","Boolean",true,1); -settings.define_setting("tile_server","String",configs.defaultTiles,1); -settings.define_setting("enable_crosshairs","Boolean",false,1); +settings.define_setting("use_rich_editor", "Boolean", true, 1); +settings.define_setting("tile_server", "String", configs.defaultTiles, 1); +settings.define_setting("enable_crosshairs", "Boolean", false, 1); // export settings -settings.define_setting("export_image_scale","Boolean",true,1); -settings.define_setting("export_image_attribution","Boolean",true,1); +settings.define_setting("export_image_scale", "Boolean", true, 1); +settings.define_setting("export_image_attribution", "Boolean", true, 1); // CORS/ajax/etc. settings -settings.define_setting("force_simple_cors_request","Boolean",false,11); +settings.define_setting("force_simple_cors_request", "Boolean", false, 11); // background opacity -settings.define_setting("background_opacity","Float",1.0,13); +settings.define_setting("background_opacity", "Float", 1.0, 13); // autorepair message on "no visible data" -settings.define_setting("no_autorepair","Boolean",false,16); +settings.define_setting("no_autorepair", "Boolean", false, 16); // resizable panels -settings.define_setting("editor_width","String","",17); +settings.define_setting("editor_width", "String", "", 17); // UI language -settings.define_setting("ui_language","String","auto",19); +settings.define_setting("ui_language", "String", "auto", 19); // disable poi-o-matic -settings.define_setting("disable_poiomatic","boolean",false,21); +settings.define_setting("disable_poiomatic", "boolean", false, 21); // show data stats -settings.define_setting("show_data_stats","boolean",true,21); +settings.define_setting("show_data_stats", "boolean", true, 21); //settings.define_setting(,,,); @@ -121,11 +159,17 @@ settings.define_setting("show_data_stats","boolean",true,21); settings.define_upgrade_callback(12, function(s) { // migrate code and saved examples to new mustache style syntax var migrate = function(code) { - code.overpass = code.overpass.replace(/\(bbox\)/g,"({{bbox}})"); - code.overpass = code.overpass.replace(//g,""); - code.overpass = code.overpass.replace(//g,""); + code.overpass = code.overpass.replace(/\(bbox\)/g, "({{bbox}})"); + code.overpass = code.overpass.replace( + //g, + "" + ); + code.overpass = code.overpass.replace( + //g, + "" + ); return code; - } + }; s.code = migrate(s.code); for (var ex in s.saves) { s.saves[ex] = migrate(s.saves[ex]); @@ -139,37 +183,35 @@ settings.define_upgrade_callback(18, function(s) { }); settings.define_upgrade_callback(20, function(s) { // update "Mountains in Area" example - s.saves["Mountains in Area"]=examples["Mountains in Area"]; + s.saves["Mountains in Area"] = examples["Mountains in Area"]; s.save(); }); settings.define_upgrade_callback(22, function(s) { // categorize saved queries for (var q in s.saves) { - if (examples[q]) - s.saves[q].type = "example"; - else - s.saves[q].type = "saved_query"; + if (examples[q]) s.saves[q].type = "example"; + else s.saves[q].type = "saved_query"; } // define some templates s.saves["key"] = { type: "template", parameters: ["key"], - overpass: "\n{{key=???}}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n" + overpass: '\n{{key=???}}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n' }; s.saves["key-type"] = { type: "template", parameters: ["key", "type"], - overpass: "\n{{key=???}}\n{{type=???}}\n\n \n \n \n \n \n \n \n" + overpass: '\n{{key=???}}\n{{type=???}}\n\n \n \n \n \n \n \n \n' }; s.saves["key-value"] = { type: "template", parameters: ["key", "value"], - overpass: "\n{{key=???}}\n{{value=???}}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n" + overpass: '\n{{key=???}}\n{{value=???}}\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n' }; s.saves["key-value-type"] = { type: "template", parameters: ["key", "value", "type"], - overpass: "\n{{key=???}}\n{{value=???}}\n{{type=???}}\n\n \n \n \n \n \n \n \n" + overpass: '\n{{key=???}}\n{{value=???}}\n{{type=???}}\n\n \n \n \n \n \n \n \n' }; s.save(); }); @@ -177,15 +219,14 @@ settings.define_upgrade_callback(23, function(s) { s.saves["type-id"] = { type: "template", parameters: ["type", "id"], - overpass: "\n{{type=???}}\n{{id=???}}\n\n \n \n \n \n" + overpass: '\n{{type=???}}\n{{id=???}}\n\n \n \n \n \n' }; s.save(); }); settings.define_upgrade_callback(24, function(s) { // categorize saved queries for (var q in s.saves) { - if (!s.saves[q].type) - s.saves[q].type = "saved_query"; + if (!s.saves[q].type) s.saves[q].type = "saved_query"; } s.save(); }); @@ -193,8 +234,11 @@ settings.define_upgrade_callback(25, function(s) { // upgrade template description text for (var q in s.saves) { if (s.saves[q].type == "template") { - s.saves[q].overpass = s.saves[q].overpass.replace("","\nChoose your region and hit the Run button above!\n-->"); + s.saves[q].overpass = s.saves[q].overpass.replace("", + "\nChoose your region and hit the Run button above!\n-->" + ); } } s.save(); @@ -214,8 +258,8 @@ settings.define_upgrade_callback(27, function(s) { }); settings.define_upgrade_callback(28, function(s) { // generalize URLs to not explicitly use http protocol - s.server = s.server.replace(/^http:\/\//,"//"); - s.tile_server = s.tile_server.replace(/^http:\/\//,"//"); + s.server = s.server.replace(/^http:\/\//, "//"); + s.tile_server = s.tile_server.replace(/^http:\/\//, "//"); s.save(); }); settings.define_upgrade_callback(29, function(s) { @@ -225,19 +269,19 @@ settings.define_upgrade_callback(29, function(s) { switch (name) { case "key": save.wizard = "{{key}}=*"; - break; + break; case "key-type": save.wizard = "{{key}}=* and type:{{type}}"; - break; + break; case "key-value": save.wizard = "{{key}}={{value}}"; - break; + break; case "key-value-type": save.wizard = "{{key}}={{value}} and type:{{type}}"; - break; + break; case "type-id": save.wizard = "type:{{type}} and id:{{id}} global"; - break; + break; default: return; } @@ -253,25 +297,30 @@ settings.define_upgrade_callback(30, function(s) { if (save.type !== "template") return; switch (name) { case "key": - save.comment = "This query looks for nodes, ways and relations \nwith the given key."; + save.comment = + "This query looks for nodes, ways and relations \nwith the given key."; save.comment += chooseAndRun; - break; + break; case "key-type": - save.comment = "This query looks for nodes, ways or relations \nwith the given key."; + save.comment = + "This query looks for nodes, ways or relations \nwith the given key."; save.comment += chooseAndRun; - break; + break; case "key-value": - save.comment = "This query looks for nodes, ways and relations \nwith the given key/value combination."; + save.comment = + "This query looks for nodes, ways and relations \nwith the given key/value combination."; save.comment += chooseAndRun; - break; + break; case "key-value-type": - save.comment = "This query looks for nodes, ways or relations \nwith the given key/value combination."; + save.comment = + "This query looks for nodes, ways or relations \nwith the given key/value combination."; save.comment += chooseAndRun; - break; + break; case "type-id": - save.comment = "This query looks for a node, way or relation \nwith the given id."; + save.comment = + "This query looks for a node, way or relation \nwith the given id."; save.comment += "\nTo execute, hit the Run button above!"; - break; + break; default: return; } @@ -292,7 +341,7 @@ settings.define_upgrade_callback(31, function(s) { case "Where am I?": case "MapCSS styling": save.overpass = examples[name].overpass; - break; + break; default: return; } @@ -303,7 +352,9 @@ settings.define_upgrade_callback(31, function(s) { settings.define_upgrade_callback(32, function(s) { // fix typo in query definition - s.saves["MapCSS styling"].overpass = s.saves["MapCSS styling"].overpass.replace("<;",">;"); + s.saves["MapCSS styling"].overpass = s.saves[ + "MapCSS styling" + ].overpass.replace("<;", ">;"); s.save(); }); @@ -311,22 +362,23 @@ settings.define_upgrade_callback(33, function(s) { s.saves["Attic date query"] = { type: "example", overpass: [ - '/* This query loads all objects as of 2016-01-01 */', + "/* This query loads all objects as of 2016-01-01 */", '[date:"2016-01-01T00:00:00Z"]', - '(', - ' node({{bbox}});', - ' way({{bbox}});', - ' relation({{bbox}});', - ');', - 'out body;', - '>;', - 'out skel qt;' - ].join('\n') + "(", + " node({{bbox}});", + " way({{bbox}});", + " relation({{bbox}});", + ");", + "out body;", + ">;", + "out skel qt;" + ].join("\n") }; s.save(); }); settings.define_upgrade_callback(34, function(s) { - s.saves["Attic date query"].overpass = s.saves["Attic date query"].overpass.replace('00:00Z"]\n', '00:00Z"];\n'); + s.saves["Attic date query"].overpass = s.saves[ + "Attic date query" + ].overpass.replace('00:00Z"]\n', '00:00Z"];\n'); }); - diff --git a/js/shortcuts.js b/js/shortcuts.js index ba00f15e..ae8e2d4b 100644 --- a/js/shortcuts.js +++ b/js/shortcuts.js @@ -1,13 +1,11 @@ // shortcuts module // see http://wiki.openstreetmap.org/wiki/Overpass_turbo/Extended_Overpass_Queries -import ide from './ide'; -import nominatim_ from './nominatim'; +import ide from "./ide"; +import nominatim_ from "./nominatim"; export default function shortcuts(nominatim) { + if (!nominatim) nominatim = nominatim_(); - if (!nominatim) - nominatim = nominatim_(); - // helpers // returns the current visible bbox as a bbox-query @@ -15,34 +13,38 @@ export default function shortcuts(nominatim) { var bbox; if (!(ide.map.bboxfilter && ide.map.bboxfilter.isEnabled())) bbox = ide.map.getBounds(); - else - bbox = ide.map.bboxfilter.getBounds(); - var lat1 = Math.min(Math.max(bbox.getSouthWest().lat,-90),90); - var lat2 = Math.min(Math.max(bbox.getNorthEast().lat,-90),90); - var lng1 = Math.min(Math.max(bbox.getSouthWest().lng,-180),180); - var lng2 = Math.min(Math.max(bbox.getNorthEast().lng,-180),180); - if (lang=="OverpassQL") - return lat1+','+lng1+','+lat2+','+lng2; - else if (lang=="xml") - return 's="'+lat1+'" w="'+lng1+'" n="'+lat2+'" e="'+lng2+'"'; + else bbox = ide.map.bboxfilter.getBounds(); + var lat1 = Math.min(Math.max(bbox.getSouthWest().lat, -90), 90); + var lat2 = Math.min(Math.max(bbox.getNorthEast().lat, -90), 90); + var lng1 = Math.min(Math.max(bbox.getSouthWest().lng, -180), 180); + var lng2 = Math.min(Math.max(bbox.getNorthEast().lng, -180), 180); + if (lang == "OverpassQL") + return lat1 + "," + lng1 + "," + lat2 + "," + lng2; + else if (lang == "xml") + return ( + 's="' + lat1 + '" w="' + lng1 + '" n="' + lat2 + '" e="' + lng2 + '"' + ); } // returns the current visible map center as a coord-query function map2coord(lang) { var center = ide.map.getCenter(); - if (lang=="OverpassQL") - return center.lat+','+center.lng; - else if (lang=="xml") - return 'lat="'+center.lat+'" lon="'+center.lng+'"'; + if (lang == "OverpassQL") return center.lat + "," + center.lng; + else if (lang == "xml") + return 'lat="' + center.lat + '" lon="' + center.lng + '"'; } // converts relative time to ISO time string function relativeTime(instr, callback) { var now = Date.now(); // very basic differential date - instr = instr.toLowerCase().match(/(-?[0-9]+) ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?/); + instr = instr + .toLowerCase() + .match( + /(-?[0-9]+) ?(seconds?|minutes?|hours?|days?|weeks?|months?|years?)?/ + ); if (instr === null) { - callback(''); // todo: throw an error. do not silently fail + callback(""); // todo: throw an error. do not silently fail return; } var count = parseInt(instr[1]); @@ -50,29 +52,36 @@ export default function shortcuts(nominatim) { switch (instr[2]) { case "second": case "seconds": - interval=1; break; + interval = 1; + break; case "minute": case "minutes": - interval=60; break; + interval = 60; + break; case "hour": case "hours": - interval=3600; break; + interval = 3600; + break; case "day": case "days": default: - interval=86400; break; + interval = 86400; + break; case "week": case "weeks": - interval=604800; break; + interval = 604800; + break; case "month": case "months": - interval=2628000; break; + interval = 2628000; + break; case "year": case "years": - interval=31536000; break; + interval = 31536000; + break; } - var date = now - count*interval*1000; - callback((new Date(date)).toISOString()); + var date = now - count * interval * 1000; + callback(new Date(date).toISOString()); } // geocoded values (object/area ids, coords, bbox) @@ -80,58 +89,53 @@ export default function shortcuts(nominatim) { var lang = ide.getQueryLang(); function filter(n) { return n.osm_type && n.osm_id; - } - nominatim.getBest(instr,filter, function(err, res) { - if (err) return ide.onNominatimError(instr,"Id"); - if (lang=="OverpassQL") - res = res.osm_type+"("+res.osm_id+")"; - else if (lang=="xml") - res = 'type="'+res.osm_type+'" ref="'+res.osm_id+'"'; + } + nominatim.getBest(instr, filter, function(err, res) { + if (err) return ide.onNominatimError(instr, "Id"); + if (lang == "OverpassQL") res = res.osm_type + "(" + res.osm_id + ")"; + else if (lang == "xml") + res = 'type="' + res.osm_type + '" ref="' + res.osm_id + '"'; callback(res); }); } function geocodeArea(instr, callback) { var lang = ide.getQueryLang(); function filter(n) { - return n.osm_type && n.osm_id && n.osm_type!=="node"; - } - nominatim.getBest(instr,filter, function(err, res) { - if (err) return ide.onNominatimError(instr,"Area"); - var area_ref = 1*res.osm_id; - if (res.osm_type == "way") - area_ref += 2400000000; - if (res.osm_type == "relation") - area_ref += 3600000000; - if (lang=="OverpassQL") - res = "area("+area_ref+")"; - else if (lang=="xml") - res = 'type="area" ref="'+area_ref+'"'; + return n.osm_type && n.osm_id && n.osm_type !== "node"; + } + nominatim.getBest(instr, filter, function(err, res) { + if (err) return ide.onNominatimError(instr, "Area"); + var area_ref = 1 * res.osm_id; + if (res.osm_type == "way") area_ref += 2400000000; + if (res.osm_type == "relation") area_ref += 3600000000; + if (lang == "OverpassQL") res = "area(" + area_ref + ")"; + else if (lang == "xml") res = 'type="area" ref="' + area_ref + '"'; callback(res); }); } function geocodeBbox(instr, callback) { var lang = ide.getQueryLang(); nominatim.getBest(instr, function(err, res) { - if (err) return ide.onNominatimError(instr,"Bbox"); - var lat1 = Math.min(Math.max(res.boundingbox[0],-90),90); - var lat2 = Math.min(Math.max(res.boundingbox[1],-90),90); - var lng1 = Math.min(Math.max(res.boundingbox[2],-180),180); - var lng2 = Math.min(Math.max(res.boundingbox[3],-180),180); - if (lang=="OverpassQL") - res = lat1+','+lng1+','+lat2+','+lng2; - else if (lang=="xml") - res = 's="'+lat1+'" w="'+lng1+'" n="'+lat2+'" e="'+lng2+'"'; + if (err) return ide.onNominatimError(instr, "Bbox"); + var lat1 = Math.min(Math.max(res.boundingbox[0], -90), 90); + var lat2 = Math.min(Math.max(res.boundingbox[1], -90), 90); + var lng1 = Math.min(Math.max(res.boundingbox[2], -180), 180); + var lng2 = Math.min(Math.max(res.boundingbox[3], -180), 180); + if (lang == "OverpassQL") + res = lat1 + "," + lng1 + "," + lat2 + "," + lng2; + else if (lang == "xml") + res = + 's="' + lat1 + '" w="' + lng1 + '" n="' + lat2 + '" e="' + lng2 + '"'; callback(res); }); } function geocodeCoords(instr, callback) { var lang = ide.getQueryLang(); nominatim.getBest(instr, function(err, res) { - if (err) return ide.onNominatimError(instr,"Coords"); - if (lang=="OverpassQL") - res = res.lat+','+res.lon; - else if (lang=="xml") - res = 'lat="'+res.lat+'" lon="'+res.lon+'"'; + if (err) return ide.onNominatimError(instr, "Coords"); + if (lang == "OverpassQL") res = res.lat + "," + res.lon; + else if (lang == "xml") + res = 'lat="' + res.lat + '" lon="' + res.lon + '"'; callback(res); }); } @@ -139,24 +143,32 @@ export default function shortcuts(nominatim) { return function getShortcuts() { var queryLang = ide.getQueryLang(); return { - "bbox": map2bbox(queryLang), - "center": map2coord(queryLang), + bbox: map2bbox(queryLang), + center: map2coord(queryLang), // special handling for global bbox in xml queries (which uses an OverpassQL-like notation instead of n/s/e/w parameters): - "__bbox__global_bbox_xml__ezs4K8__": map2bbox("OverpassQL"), - "date": relativeTime, - "geocodeId": geocodeId, - "geocodeArea": geocodeArea, - "geocodeBbox": geocodeBbox, - "geocodeCoords": geocodeCoords, - // legacy - "nominatimId": queryLang=="xml" ? geocodeId : function(instr,callback) { - geocodeId(instr, function(result) { callback(result+';'); }); - }, - "nominatimArea": queryLang=="xml" ? geocodeArea : function(instr,callback) { - geocodeArea(instr, function(result) { callback(result+';'); }); - }, - "nominatimBbox": geocodeBbox, - "nominatimCoords": geocodeCoords, + __bbox__global_bbox_xml__ezs4K8__: map2bbox("OverpassQL"), + date: relativeTime, + geocodeId: geocodeId, + geocodeArea: geocodeArea, + geocodeBbox: geocodeBbox, + geocodeCoords: geocodeCoords, + // legacy + nominatimId: queryLang == "xml" + ? geocodeId + : function(instr, callback) { + geocodeId(instr, function(result) { + callback(result + ";"); + }); + }, + nominatimArea: queryLang == "xml" + ? geocodeArea + : function(instr, callback) { + geocodeArea(instr, function(result) { + callback(result + ";"); + }); + }, + nominatimBbox: geocodeBbox, + nominatimCoords: geocodeCoords }; - } + }; } diff --git a/js/urlParameters.js b/js/urlParameters.js index c5ecb4f0..fe279976 100644 --- a/js/urlParameters.js +++ b/js/urlParameters.js @@ -1,7 +1,7 @@ // urlParameters module -import ffs from './ffs'; -import settings from './settings'; -import {Base64, lzw_decode} from './misc'; +import ffs from "./ffs"; +import settings from "./settings"; +import {Base64, lzw_decode} from "./misc"; export default function urlParameters(param_str) { // defaults @@ -13,19 +13,19 @@ export default function urlParameters(param_str) { has_zoom: false, zoom: undefined, run_query: false - } + }; // split parameter string to arguments function split(param_str) { var args = {}; if (typeof param_str === "string" && param_str.length > 0) { var get = param_str.substring(1).split("&"); - for (var i=0; i 0) ? - decodeURIComponent(kv.join("=").replace(/\+/g,"%20")) : - true; + var key = decodeURIComponent(kv.shift().replace(/\+/g, "%20")); + var val = kv.length > 0 + ? decodeURIComponent(kv.join("=").replace(/\+/g, "%20")) + : true; args[key] = val; } } @@ -34,73 +34,84 @@ export default function urlParameters(param_str) { var args = split(param_str); // interpret arguments - if (args.q) { // compressed query set in url + if (args.q) { + // compressed query set in url t.query = lzw_decode(Base64.decode(args.q)); t.has_query = true; } - if (args.Q) { // uncompressed query set in url + if (args.Q) { + // uncompressed query set in url t.query = args.Q; t.has_query = true; } - if (args.c) { // map center & zoom (compressed) + if (args.c) { + // map center & zoom (compressed) var tmp = args.c.match(/([A-Za-z0-9\-_]+)([A-Za-z0-9\-_])/); var decode_coords = function(str) { var coords_cpr = Base64.decodeNum(str); var res = {}; - res.lat = coords_cpr % (180*100000) / 100000 - 90; - res.lng = Math.floor(coords_cpr / (180*100000)) / 100000 - 180; + res.lat = coords_cpr % (180 * 100000) / 100000 - 90; + res.lng = Math.floor(coords_cpr / (180 * 100000)) / 100000 - 180; return res; - } + }; t.coords = decode_coords(tmp[1]); t.has_coords = true; t.zoom = Base64.decodeNum(tmp[2]); t.has_zoom = true; } - if (args.C) { // map center & zoom (uncompressed) + if (args.C) { + // map center & zoom (uncompressed) var tmp = args.C.match(/(-?[\d.]+);(-?[\d.]+);(\d+)/); t.coords = {lat: +tmp[1], lng: +tmp[2]}; t.has_coords = true; t.zoom = +tmp[3]; t.has_zoom = true; } - if (args.lat && args.lon) { // map center coords (ols style osm.org parameters) + if (args.lat && args.lon) { + // map center coords (ols style osm.org parameters) t.coords = {lat: +args.lat, lng: +args.lon}; t.has_coords = true; } - if (args.zoom) { // map zoom level (old style osm.org parameter) + if (args.zoom) { + // map zoom level (old style osm.org parameter) t.zoom = +args.zoom; t.has_zoom = true; } - if (args.template) { // load a template + if (args.template) { + // load a template var template = settings.saves[args.template]; if (template && template.type == "template") { // build query var q = template.wizard; var params = template.parameters; - for (var i=0; i no merging required + locale = locales[0]; // only 1 resource -> no merging required - for (var i in locale) { - if (i === 'en') continue; - fs.writeFileSync(outdir + i + '.json', JSON.stringify(locale[i], null, 2)); - } + for (var i in locale) { + if (i === "en") continue; + fs.writeFileSync(outdir + i + ".json", JSON.stringify(locale[i], null, 2)); + } }); function getResource(resource, callback) { - resource = project + 'resource/' + resource + '/'; - getLanguages(resource, function(err, codes) { - if (err) return callback(err); + resource = project + "resource/" + resource + "/"; + getLanguages(resource, function(err, codes) { + if (err) return callback(err); - asyncMap(codes, getLanguage(resource), function(err, results) { - if (err) return callback(err); + asyncMap(codes, getLanguage(resource), function(err, results) { + if (err) return callback(err); - var locale = {}; - results.forEach(function(result, i) { - locale[codes[i]] = result; - }); + var locale = {}; + results.forEach(function(result, i) { + locale[codes[i]] = result; + }); - callback(null, locale); - }); + callback(null, locale); }); + }); } function getLanguage(resource) { - return function(code, callback) { - code = code.replace(/-/g, '_'); - var url = resource + 'translation/' + code; - request.get(url, { auth : auth }, - function(err, resp, body) { - if (err) return callback(err); - callback(null, JSON.parse(JSON.parse(body).content)); - }); - }; + return function(code, callback) { + code = code.replace(/-/g, "_"); + var url = resource + "translation/" + code; + request.get(url, {auth: auth}, function(err, resp, body) { + if (err) return callback(err); + callback(null, JSON.parse(JSON.parse(body).content)); + }); + }; } function getLanguages(resource, callback) { - request.get(resource + '?details', { auth: auth }, - function(err, resp, body) { - if (err) return callback(err); - callback(null, JSON.parse(body).available_languages.map(function(d) { - return d.code.replace(/_/g, '-'); - }).filter(function(d) { - return d !== 'en'; - })); - }); + request.get(resource + "?details", {auth: auth}, function(err, resp, body) { + if (err) return callback(err); + callback( + null, + JSON.parse(body) + .available_languages.map(function(d) { + return d.code.replace(/_/g, "-"); + }) + .filter(function(d) { + return d !== "en"; + }) + ); + }); } function asyncMap(inputs, func, callback) { - var remaining = inputs.length, - results = [], - error; + var remaining = inputs.length, results = [], error; - inputs.forEach(function(d, i) { - func(d, function done(err, data) { - if (err) error = err; - results[i] = data; - remaining --; - if (!remaining) callback(error, results); - }); + inputs.forEach(function(d, i) { + func(d, function done(err, data) { + if (err) error = err; + results[i] = data; + remaining--; + if (!remaining) callback(error, results); }); -} \ No newline at end of file + }); +} diff --git a/tests/test.autorepair.josm.js b/tests/test.autorepair.josm.js index fdf0009b..decf1070 100644 --- a/tests/test.autorepair.josm.js +++ b/tests/test.autorepair.josm.js @@ -1,29 +1,32 @@ -import chai from 'chai'; +import chai from "chai"; var expect = chai.expect; -import sinon from 'sinon'; -import ide from '../js/ide'; +import sinon from "sinon"; +import ide from "../js/ide"; -describe("ide.autorepair.josm", function () { +describe("ide.autorepair.josm", function() { // repair non-xml output data format: xml query - it("repair non-xml output data format (xml query)", function () { + it("repair non-xml output data format (xml query)", function() { var examples = [ - { // basic case + { + // basic case inp: '', outp: '' }, - { // preserve other osm-script parameters + { + // preserve other osm-script parameters inp: '', outp: '' }, - { // more complex real world example + { + // more complex real world example inp: '\n \n \n \n \n \n', outp: '\n \n \n \n \n \n' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i.foo;\n.foo out meta qt;', outp: '/*bla*/\n[out:xml];/*fixed by auto repair*/\nway\n ["amenity"]\n ({{bbox}})\n->.foo;\n.foo out meta qt;' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("OverpassQL"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i', + { + // trivial case + inp: "", outp: '' }, - { // trivial case 2 + { + // trivial case 2 inp: '', outp: '' }, - { // more complex real world example + { + // more complex real world example inp: '\n \n \n \n \n \n', outp: '\n \n \n \n \n \n' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i.foo;\n.foo out qt;', outp: '/*bla*/\n[out:xml];\nway\n ["amenity"]\n ({{bbox}})\n->.foo;\n.foo out meta qt;/*fixed by auto repair*/' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("OverpassQL"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i', outp: '' }, - { // bounds geometry + { + // bounds geometry inp: '', outp: '' }, - { // full geometry + { + // full geometry inp: '', outp: '' }, - { // full geometry with from output mode + { + // full geometry with from output mode inp: '', outp: '' }, - { // full geometry with named input set + { + // full geometry with named input set inp: '', outp: '' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i;); out meta;/*fixed by auto repair*/' + { + // center geometry + inp: "out meta center;", + outp: "(._;>;); out meta;/*fixed by auto repair*/" }, - { // bounds geometry - inp: 'out meta bb;', - outp: '(._;>;); out meta;/*fixed by auto repair*/' + { + // bounds geometry + inp: "out meta bb;", + outp: "(._;>;); out meta;/*fixed by auto repair*/" }, - { // full geometry - inp: 'out meta geom;', - outp: '(._;>;); out meta;/*fixed by auto repair*/' + { + // full geometry + inp: "out meta geom;", + outp: "(._;>;); out meta;/*fixed by auto repair*/" }, - { // combined with wrong output mode - inp: 'out body geom;', - outp: '(._;>;); out meta;/*fixed by auto repair*/' + { + // combined with wrong output mode + inp: "out body geom;", + outp: "(._;>;); out meta;/*fixed by auto repair*/" }, - { // named input set - inp: '.foo out meta geom;', - outp: '(.foo;.foo >;)->.foo; .foo out meta;/*fixed by auto repair*/' - }, - { // stuff in comment before out statement - inp: '//asd fasd;\nout meta geom;', - outp: '//asd fasd;\n(._;>;); out meta;/*fixed by auto repair*/' + { + // named input set + inp: ".foo out meta geom;", + outp: "(.foo;.foo >;)->.foo; .foo out meta;/*fixed by auto repair*/" }, + { + // stuff in comment before out statement + inp: "//asd fasd;\nout meta geom;", + outp: "//asd fasd;\n(._;>;); out meta;/*fixed by auto repair*/" + } ]; - sinon.stub(ide,"getQueryLang").returns("OverpassQL"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i in xml comment - inp: '', - outp: '' - }, - { // in xml comment - inp: '', - outp: '' + { + // in xml comment + inp: "", + outp: "" }, + { + // in xml comment + inp: "", + outp: "" + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i', + { + // trivial case + inp: "", outp: '\n\n\n \n \n\n' }, - { // simple real life example - inp: '\n \n \n \n \n \n', + { + // simple real life example + inp: '\n \n \n \n \n \n', outp: '\n \n \n \n \n \n \n \n \n \n \n \n' }, - { // complex example with multiple prints and named input sets - inp: '\n \n \n\n\n \n \n\n\n', + { + // complex example with multiple prints and named input sets + inp: '\n \n \n\n\n \n \n\n\n', outp: '\n \n \n\n\n \n \n\n\n\n \n \n\n\n\n\n\n \n \n\n\n' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i;);/*end of auto repair*/out;' + { + // trivial case + inp: "out;", + outp: "/*added by auto repair*/(._;>;);/*end of auto repair*/out;" }, - { // simple real life example - inp: '/*This is the drinking water example in OverpassQL.*/\n(\n way["name"]({{bbox}})\n);\nout;', + { + // simple real life example + inp: '/*This is the drinking water example in OverpassQL.*/\n(\n way["name"]({{bbox}})\n);\nout;', outp: '/*This is the drinking water example in OverpassQL.*/\n(\n way["name"]({{bbox}})\n);\n/*added by auto repair*/\n(._;>;);\n/*end of auto repair*/\nout;' }, - { // simple query with coordinates + { + // simple query with coordinates inp: '(way["name"](50.75267740365953,7.085511088371277,50.755728421888925,7.0877212285995475));out;', outp: '(way["name"](50.75267740365953,7.085511088371277,50.755728421888925,7.0877212285995475));/*added by auto repair*/(._;>;);/*end of auto repair*/out;' }, - { // complex example with multiple prints and named input sets + { + // complex example with multiple prints and named input sets inp: 'way\n ["amenity"]\n ({{bbox}})\n->.foo;\nway\n ["building"]\n ({{bbox}})\n->.bar;\n.foo out;\n.bar out meta;', outp: 'way\n ["amenity"]\n ({{bbox}})\n->.foo;\nway\n ["building"]\n ({{bbox}})\n->.bar;\n/*added by auto repair*/\n(.foo;.foo >;)->.foo;\n/*end of auto repair*/\n.foo out;\n/*added by auto repair*/\n(.bar;.bar >;)->.bar;\n/*end of auto repair*/\n.bar out meta;' }, - { // example with the term "...out..." in string parameters + { + // example with the term "...out..." in string parameters inp: 'way({{bbox}})[junction=roundabout][name!="out"];out;', outp: 'way({{bbox}})[junction=roundabout][name!="out"];/*added by auto repair*/(._;>;);/*end of auto repair*/out;' - }, + } ]; - sinon.stub(ide,"getQueryLang").returns("OverpassQL"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i-->', - outp: '' - }, + inp: "", + outp: "" + } ]; - sinon.stub(ide,"getQueryLang").returns("xml"); - var setQuery = sinon.stub(ide,"setQuery"); - for (var i=0; i.searchArea;"+ - "("+ - "node(area.searchArea);"+ - ");"+ - out_str + "area(foobar)->.searchArea;" + + "(" + + "node(area.searchArea);" + + ");" + + out_str ); }); }); // around - it("around", function () { + it("around", function() { var search = "type:node around foobar"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "node(around:,coords:foobar);"+ - ");"+ - out_str + "(" + "node(around:,coords:foobar);" + ");" + out_str ); }); }); - }); // free form - describe("free form", function () { - + describe("free form", function() { before(function() { var fake_ajax = { success: function(cb) { cb({ "amenity/hospital": { - "name": "Hospital", - "terms": [], - "geometry": ["point","area"], - "tags": {"amenity": "hospital"} + name: "Hospital", + terms: [], + geometry: ["point", "area"], + tags: {amenity: "hospital"} }, "amenity/shelter": { - "name": "Shelter", - "terms": [], - "geometry": ["point"], - "tags": {"amenity": "shelter"} + name: "Shelter", + terms: [], + geometry: ["point"], + tags: {amenity: "shelter"} }, - "highway": { - "name": "Highway", - "terms": [], - "geometry": ["line"], - "tags": {"highway": "*"} + highway: { + name: "Highway", + terms: [], + geometry: ["line"], + tags: {highway: "*"} } }); return fake_ajax; }, error: function(cb) {} }; - sinon.stub($,"ajax").returns(fake_ajax); + sinon.stub($, "ajax").returns(fake_ajax); }); after(function() { $.ajax.restore(); @@ -577,10 +536,7 @@ describe("ide.ffs", function () { ffs.construct_query(search, undefined, function(err, result) { expect(result).to.not.equal(false); expect(compact(result)).to.equal( - "("+ - "node[\"amenity\"=\"shelter\"](bbox);"+ - ");"+ - out_str + "(" + 'node["amenity"="shelter"](bbox);' + ");" + out_str ); }); // preset (points, areas, key-value) @@ -588,12 +544,12 @@ describe("ide.ffs", function () { ffs.construct_query(search, undefined, function(err, result) { expect(result).to.not.equal(false); expect(compact(result)).to.equal( - "("+ - "node[\"amenity\"=\"hospital\"](bbox);"+ - "way[\"amenity\"=\"hospital\"](bbox);"+ - "relation[\"amenity\"=\"hospital\"](bbox);"+ - ");"+ - out_str + "(" + + 'node["amenity"="hospital"](bbox);' + + 'way["amenity"="hospital"](bbox);' + + 'relation["amenity"="hospital"](bbox);' + + ");" + + out_str ); }); // preset (lines, key=*) @@ -601,79 +557,63 @@ describe("ide.ffs", function () { ffs.construct_query(search, undefined, function(err, result) { expect(result).to.not.equal(false); expect(compact(result)).to.equal( - "("+ - "way[\"highway\"](bbox);"+ - ");"+ - out_str + "(" + 'way["highway"](bbox);' + ");" + out_str ); }); - }); - }); // sanity conversions for special conditions - describe("special cases", function () { + describe("special cases", function() { // empty value - it("empty value", function () { + it("empty value", function() { var search = "foo='' and type:way"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "way[\"foo\"~\"^$\"](bbox);"+ - ");"+ - out_str + "(" + 'way["foo"~"^$"](bbox);' + ");" + out_str ); }); }); // empty key - it("empty key", function () { + it("empty key", function() { var search = "''=bar and type:way"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "way[~\"^$\"~\"^bar$\"](bbox);"+ - ");"+ - out_str + "(" + 'way[~"^$"~"^bar$"](bbox);' + ");" + out_str ); }); // make sure stuff in the value section gets escaped properly search = "''='*' and type:way"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "way[~\"^$\"~\"^\\\\*$\"](bbox);"+ - ");"+ - out_str + "(" + 'way[~"^$"~"^\\\\*$"](bbox);' + ");" + out_str ); }); // does also work for =*, ~ and : searches search = "(''=* or ''~/.../) and type:way"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "way[~\"^$\"~\".*\"](bbox);"+ - "way[~\"^$\"~\"...\"](bbox);"+ - ");"+ - out_str + "(" + + 'way[~"^$"~".*"](bbox);' + + 'way[~"^$"~"..."](bbox);' + + ");" + + out_str ); }); }); // newlines, tabs - it("newlines, tabs", function () { + it("newlines, tabs", function() { var search = "(foo='\t' or foo='\n' or asd='\\t') and type:way"; ffs.construct_query(search, undefined, function(err, result) { expect(compact(result)).to.equal( - "("+ - "way[\"foo\"=\"\\t\"](bbox);"+ - "way[\"foo\"=\"\\n\"](bbox);"+ - "way[\"asd\"=\"\\t\"](bbox);"+ - ");"+ - out_str + "(" + + 'way["foo"="\\t"](bbox);' + + 'way["foo"="\\n"](bbox);' + + 'way["asd"="\\t"](bbox);' + + ");" + + out_str ); }); }); - }); - }); diff --git a/tests/test.mapcss.eval.js b/tests/test.mapcss.eval.js index 4e56a824..18834f8f 100644 --- a/tests/test.mapcss.eval.js +++ b/tests/test.mapcss.eval.js @@ -1,193 +1,195 @@ -import chai from 'chai'; +import chai from "chai"; var expect = chai.expect; -import evalparser from '../js/jsmapcss/eval.pegjs'; +import evalparser from "../js/jsmapcss/eval.pegjs"; -describe("mapcss.eval", function () { - it("strings", function () { - var q = '"foo"' - var p = evalparser.parse(q) +describe("mapcss.eval", function() { + it("strings", function() { + var q = '"foo"'; + var p = evalparser.parse(q); expect(p).to.equal("foo"); - var q = "'foo'" - var p = evalparser.parse(q) + var q = "'foo'"; + var p = evalparser.parse(q); expect(p).to.equal("foo"); }); - it("num()", function () { - var q = 'num("12.3")' - var p = evalparser.parse(q) + it("num()", function() { + var q = 'num("12.3")'; + var p = evalparser.parse(q); expect(p).to.equal("12.3"); - var q = 'num("foo")' - var p = evalparser.parse(q) + var q = 'num("foo")'; + var p = evalparser.parse(q); expect(p).to.equal(""); - var q = 'num("-12.3E-1")' - var p = evalparser.parse(q) + var q = 'num("-12.3E-1")'; + var p = evalparser.parse(q); expect(p).to.equal("-1.23"); }); - it("str()", function () { - var q = 'str(12.3)' - var p = evalparser.parse(q) + it("str()", function() { + var q = "str(12.3)"; + var p = evalparser.parse(q); expect(p).to.equal("12.3"); }); - it("number arithmetic", function () { - var q = '(1+2*3-4/2-1)*2' - var p = evalparser.parse(q) + it("number arithmetic", function() { + var q = "(1+2*3-4/2-1)*2"; + var p = evalparser.parse(q); expect(p).to.equal("8"); }); - it("int", function () { - var q = 'int(3.1)' - var p = evalparser.parse(q) + it("int", function() { + var q = "int(3.1)"; + var p = evalparser.parse(q); expect(p).to.equal("3"); - var q = 'int(3.9)' - var p = evalparser.parse(q) + var q = "int(3.9)"; + var p = evalparser.parse(q); expect(p).to.equal("3"); - var q = 'int(-3.1)' - var p = evalparser.parse(q) + var q = "int(-3.1)"; + var p = evalparser.parse(q); expect(p).to.equal("-3"); - var q = 'int(-3.9)' - var p = evalparser.parse(q) + var q = "int(-3.9)"; + var p = evalparser.parse(q); expect(p).to.equal("-3"); }); - it("number EIAS", function () { - var q = '"2" + 4' - var p = evalparser.parse(q) + it("number EIAS", function() { + var q = '"2" + 4'; + var p = evalparser.parse(q); expect(p).to.equal("6"); - var q = '"2" == 2' - var p = evalparser.parse(q) + var q = '"2" == 2'; + var p = evalparser.parse(q); expect(p).to.equal("true"); }); - it("none", function () { - var q = 'none' - var p = evalparser.parse(q) + it("none", function() { + var q = "none"; + var p = evalparser.parse(q); expect(p).to.equal(""); }); - it("none aithmetic", function () { - var q = '2 + none' - var p = evalparser.parse(q) + it("none aithmetic", function() { + var q = "2 + none"; + var p = evalparser.parse(q); expect(p).to.equal("2"); - var q = '2 * none' - var p = evalparser.parse(q) + var q = "2 * none"; + var p = evalparser.parse(q); expect(p).to.equal("0"); }); - it("none EIAS", function () { - var q = '2."" == 2' - var p = evalparser.parse(q) + it("none EIAS", function() { + var q = '2."" == 2'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '2+"" == 2' - var p = evalparser.parse(q) + var q = '2+"" == 2'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = 'none == ""' - var p = evalparser.parse(q) + var q = 'none == ""'; + var p = evalparser.parse(q); expect(p).to.equal("true"); }); - it("boolean", function () { - var q = 'boolean(0)' - var p = evalparser.parse(q) + it("boolean", function() { + var q = "boolean(0)"; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean("0")' - var p = evalparser.parse(q) + var q = 'boolean("0")'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean("no")' - var p = evalparser.parse(q) + var q = 'boolean("no")'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean("false")' - var p = evalparser.parse(q) + var q = 'boolean("false")'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean("")' - var p = evalparser.parse(q) + var q = 'boolean("")'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean(none)' - var p = evalparser.parse(q) + var q = "boolean(none)"; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = 'boolean("foobar")' - var p = evalparser.parse(q) + var q = 'boolean("foobar")'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = 'boolean("yes") == boolean("true")' - var p = evalparser.parse(q) + var q = 'boolean("yes") == boolean("true")'; + var p = evalparser.parse(q); expect(p).to.equal("true"); }); - it("boolean arithmetic", function () { - var q = '"true" && "false"' - var p = evalparser.parse(q) + it("boolean arithmetic", function() { + var q = '"true" && "false"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"true" || "false"' - var p = evalparser.parse(q) + var q = '"true" || "false"'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '!"true"' - var p = evalparser.parse(q) + var q = '!"true"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); }); - it("comparison operators", function () { - var q = '2.3 > 01.2' - var p = evalparser.parse(q) + it("comparison operators", function() { + var q = "2.3 > 01.2"; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '2 >= 2' - var p = evalparser.parse(q) + var q = "2 >= 2"; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '2 < 2' - var p = evalparser.parse(q) + var q = "2 < 2"; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '1 <= 2' - var p = evalparser.parse(q) + var q = "1 <= 2"; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '"2" == "2"' - var p = evalparser.parse(q) + var q = '"2" == "2"'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '"2" == "02"' - var p = evalparser.parse(q) + var q = '"2" == "02"'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '"2" == "3"' - var p = evalparser.parse(q) + var q = '"2" == "3"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"2" eq "2"' - var p = evalparser.parse(q) + var q = '"2" eq "2"'; + var p = evalparser.parse(q); expect(p).to.equal("true"); - var q = '"2" eq "02"' - var p = evalparser.parse(q) + var q = '"2" eq "02"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"2" != "02"' - var p = evalparser.parse(q) + var q = '"2" != "02"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"2" <> "02"' - var p = evalparser.parse(q) + var q = '"2" <> "02"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"2" ne "2"' - var p = evalparser.parse(q) + var q = '"2" ne "2"'; + var p = evalparser.parse(q); expect(p).to.equal("false"); - var q = '"2" ne "02"' - var p = evalparser.parse(q) + var q = '"2" ne "02"'; + var p = evalparser.parse(q); expect(p).to.equal("true"); }); - it("general functions", function () { - var q = 'tag("_")' - evalparser.tag = function(_) { return "foo" } - var p = evalparser.parse(q) + it("general functions", function() { + var q = 'tag("_")'; + evalparser.tag = function(_) { + return "foo"; + }; + var p = evalparser.parse(q); expect(p).to.equal("foo"); - var q = 'cond("true", "a", "b")' - var p = evalparser.parse(q) + var q = 'cond("true", "a", "b")'; + var p = evalparser.parse(q); expect(p).to.equal("a"); - var q = 'cond("false", "a", "b")' - var p = evalparser.parse(q) + var q = 'cond("false", "a", "b")'; + var p = evalparser.parse(q); expect(p).to.equal("b"); - var q = 'any(none, "", "foo", "bar")' - var p = evalparser.parse(q) + var q = 'any(none, "", "foo", "bar")'; + var p = evalparser.parse(q); expect(p).to.equal("foo"); }); - it("numeric functions", function () { - var q = 'max(1,2,3)' - var p = evalparser.parse(q) + it("numeric functions", function() { + var q = "max(1,2,3)"; + var p = evalparser.parse(q); expect(p).to.equal("3"); - var q = 'min(1,2,-3)' - var p = evalparser.parse(q) + var q = "min(1,2,-3)"; + var p = evalparser.parse(q); expect(p).to.equal("-3"); - var q = 'sqrt(16)' - var p = evalparser.parse(q) + var q = "sqrt(16)"; + var p = evalparser.parse(q); expect(p).to.equal("4"); }); - it("string functions", function () { - var q = 'concat("foo","bar","asd","fasd")' - var p = evalparser.parse(q) + it("string functions", function() { + var q = 'concat("foo","bar","asd","fasd")'; + var p = evalparser.parse(q); expect(p).to.equal("foobarasdfasd"); - var q = '"foo" . 123 . "bar"' - var p = evalparser.parse(q) + var q = '"foo" . 123 . "bar"'; + var p = evalparser.parse(q); expect(p).to.equal("foo123bar"); }); }); diff --git a/tests/test.permalink.js b/tests/test.permalink.js index 6997b815..c40ea1ca 100644 --- a/tests/test.permalink.js +++ b/tests/test.permalink.js @@ -1,46 +1,55 @@ -import chai from 'chai'; +import chai from "chai"; var expect = chai.expect; -import ide from '../js/ide'; +import ide from "../js/ide"; -describe("ide.permalink", function () { +describe("ide.permalink", function() { // check share links - it("share uncompressed", function () { - var q = '\n\n \n \n\n'; - var p = ide.compose_share_link(q,false,false,false); - expect(p).to.have.string("?Q=%3C!--%0AThis%20is%20an%20example%20Overpass%20query.%0ATry%20it%20out%20by%20pressing%20the%20Run%20button%20above!%0AYou%20can%20find%20more%20examples%20with%20the%20Load%20tool.%0A--%3E%0A%3Cquery%20type%3D%22node%22%3E%0A%20%20%3Chas-kv%20k%3D%22amenity%22%20v%3D%22drinking_water%22%2F%3E%0A%20%20%3Cbbox-query%20%7B%7Bbbox%7D%7D%2F%3E%3C!--this%20is%20auto-completed%20with%20the%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20current%20map%20view%20coordinates.--%3E%0A%3C%2Fquery%3E%0A%3Cprint%2F%3E"); + it("share uncompressed", function() { + var q = + '\n\n \n \n\n'; + var p = ide.compose_share_link(q, false, false, false); + expect(p).to.have.string( + "?Q=%3C!--%0AThis%20is%20an%20example%20Overpass%20query.%0ATry%20it%20out%20by%20pressing%20the%20Run%20button%20above!%0AYou%20can%20find%20more%20examples%20with%20the%20Load%20tool.%0A--%3E%0A%3Cquery%20type%3D%22node%22%3E%0A%20%20%3Chas-kv%20k%3D%22amenity%22%20v%3D%22drinking_water%22%2F%3E%0A%20%20%3Cbbox-query%20%7B%7Bbbox%7D%7D%2F%3E%3C!--this%20is%20auto-completed%20with%20the%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20current%20map%20view%20coordinates.--%3E%0A%3C%2Fquery%3E%0A%3Cprint%2F%3E" + ); expect(p).not.to.have.string("?q="); expect(p).not.to.have.string("&C="); expect(p).not.to.have.string("&c="); expect(p).not.to.have.string("&R"); }); - it("share compressed", function () { - var q = '\n\n \n \n\n'; - var p = ide.compose_share_link(q,true,false,false); + it("share compressed", function() { + var q = + '\n\n \n \n\n'; + var p = ide.compose_share_link(q, true, false, false); expect(p).not.to.have.string("?Q="); - expect(p).to.have.string("?q=PCEtLQpUaGlzIMSHIGFuIGV4YW1wbGUgT3ZlcnBhc8SIcXXEmXkuxIRyecSJdCBvdcSoYsSmcHJlxJ1pbmcgdGjElVJ1xI1ixKt0b8SNYWJvxJghClnEqiBjxIwgZsSzZCBtb8SwxI7EkMSSxJTEiHdpxLfEtsS4IExvYcWQxL9vbMSjxII-CjzEn8ShxLZ5cGU9Im5vZGUixakgIDxoxJwta3Yga8WyxJFlbsWbeSIgdsWyZHLEs2vEs2dfd2F0xJkiL8W5xbtixYN4LcWsxKUge3vGnW94fX3GmsSAxILEt8SKxIphxKtvLWNvxZdlxpfFkMWaxZzEt2UKxbrHgMeBx4LHgmN1csSwbsSobWFwxotpZXfFim_Fk2TEs8aWxLEuxajFqi_GoXnFqTzEr8SzdMaa"); + expect(p).to.have.string( + "?q=PCEtLQpUaGlzIMSHIGFuIGV4YW1wbGUgT3ZlcnBhc8SIcXXEmXkuxIRyecSJdCBvdcSoYsSmcHJlxJ1pbmcgdGjElVJ1xI1ixKt0b8SNYWJvxJghClnEqiBjxIwgZsSzZCBtb8SwxI7EkMSSxJTEiHdpxLfEtsS4IExvYcWQxL9vbMSjxII-CjzEn8ShxLZ5cGU9Im5vZGUixakgIDxoxJwta3Yga8WyxJFlbsWbeSIgdsWyZHLEs2vEs2dfd2F0xJkiL8W5xbtixYN4LcWsxKUge3vGnW94fX3GmsSAxILEt8SKxIphxKtvLWNvxZdlxpfFkMWaxZzEt2UKxbrHgMeBx4LHgmN1csSwbsSobWFwxotpZXfFim_Fk2TEs8aWxLEuxajFqi_GoXnFqTzEr8SzdMaa" + ); expect(p).not.to.have.string("&C="); expect(p).not.to.have.string("&c="); expect(p).not.to.have.string("&R"); }); - it("share coordinates", function () { - var q = ' '; + it("share coordinates", function() { + var q = " "; ide.map = { - getCenter: function() {return {lat:12.3456, lng:-65.4321}; }, - getZoom: function() {return 8}, + getCenter: function() { + return {lat: 12.3456, lng: -65.4321}; + }, + getZoom: function() { + return 8; + } }; var p; - p = ide.compose_share_link(q,false,true,false); + p = ide.compose_share_link(q, false, true, false); expect(p).to.have.string("&C=12.3456;-65.4321;8"); expect(p).not.to.have.string("&c="); - p = ide.compose_share_link(q,true,true,false); + p = ide.compose_share_link(q, true, true, false); expect(p).not.to.have.string("&C="); expect(p).to.have.string("&c=Au47axyXAI"); }); - it("share autorun", function () { - var q = ' '; + it("share autorun", function() { + var q = " "; var p; - p = ide.compose_share_link(q,false,false,true); + p = ide.compose_share_link(q, false, false, true); expect(p).to.have.string("&R"); }); - }); diff --git a/tests/test.query.js b/tests/test.query.js index 831ac302..3909be96 100644 --- a/tests/test.query.js +++ b/tests/test.query.js @@ -1,21 +1,28 @@ -import chai from 'chai'; +import chai from "chai"; var expect = chai.expect; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; +import sinon from "sinon"; +import sinonChai from "sinon-chai"; chai.use(sinonChai); -import ide from '../js/ide'; - -describe("ide.query", function () { +import ide from "../js/ide"; +describe("ide.query", function() { var orig_codeEditor, orig_map; before(function() { orig_codeEditor = ide.codeEditor; ide.codeEditor = {}; orig_map = ide.map; ide.map = { - bboxfilter: { isEnabled: function() { return false; } }, - getBounds: function() { return L.latLngBounds([1,2],[3,4]); }, - getCenter: function() { return L.latLng([5,6]); } + bboxfilter: { + isEnabled: function() { + return false; + } + }, + getBounds: function() { + return L.latLngBounds([1, 2], [3, 4]); + }, + getCenter: function() { + return L.latLng([5, 6]); + } }; }); after(function() { @@ -24,100 +31,118 @@ describe("ide.query", function () { }); // expand {{parameters}} in ql query - it("expand {{parameters}} in ql query", function () { - sinon.stub(ide,"setQuery"); + it("expand {{parameters}} in ql query", function() { + sinon.stub(ide, "setQuery"); var examples = [ - { // simple expansion - inp: '{{parameter=foo}};{{parameter}}', - outp: ';foo' - }, - { // simple non-expansion - inp: '{{parameter1=foo}};{{parameter2}}', - outp: ';' + { + // simple expansion + inp: "{{parameter=foo}};{{parameter}}", + outp: ";foo" }, - { // multiple expansion - inp: '{{parameter=foo}};{{parameter}}{{parameter}}', - outp: ';foofoo' + { + // simple non-expansion + inp: "{{parameter1=foo}};{{parameter2}}", + outp: ";" }, + { + // multiple expansion + inp: "{{parameter=foo}};{{parameter}}{{parameter}}", + outp: ";foofoo" + } ]; var callback = sinon.spy(); - for (var i=0; i{{parameter}}', - outp: 'foo' + { + // simple expansion + inp: "{{parameter=foo}}{{parameter}}", + outp: "foo" }, - { // simple non-expansion - inp: '{{parameter1=foo}}{{parameter2}}', - outp: '' - }, - { // multiple expansion - inp: '{{parameter=foo}}{{parameter}}{{parameter}}', - outp: 'foofoo' + { + // simple non-expansion + inp: "{{parameter1=foo}}{{parameter2}}", + outp: "" }, + { + // multiple expansion + inp: "{{parameter=foo}}{{parameter}}{{parameter}}", + outp: "foofoo" + } ]; var callback = sinon.spy(); - for (var i=0; i', + { + // xml query + inp: "", outp: '' }, - { // xml query global bbox + { + // xml query global bbox inp: '', outp: '' - }, + } ]; var callback = sinon.spy(); - for (var i=0; i', + { + // xml query + inp: "", outp: '' - }, + } ]; var callback = sinon.spy(); - for (var i=0; i