script.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. var widths = [12, 13, 13, 13, 13, 13, 13, 13];
  2. var opas = [0.8, 0.6, 0.5, 0.5, 0.4];
  3. var mwidths = [1, 1, 1, 1.5, 2, 3, 5, 6, 6, 4, 3, 2];
  4. var stCols = ['#78f378', '#0000c3', 'red'];
  5. var osmUrl = "//www.openstreetmap.org/";
  6. var grIdx, stIdx, selectedRes, prevSearch, delayTimer;
  7. var reqs = {};
  8. var openedGr = -1;
  9. var openedSt = -1;
  10. var sgMvOrNew = "Move into a <span class='grouplink' onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})'>new relation</span> <tt>public_transport=stop_area</tt>.";
  11. var sgMvOrEx = "Move into relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>.";
  12. var sgMvRelNew = "Move from relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> into a <span class='grouplink' onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})'>new relation</span> <tt>public_transport=stop_area</tt>.";
  13. var sgMvRelRel = "Move from relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a> into relation <a onmouseover='grHl(${tid})' onmouseout='grUnHl(${tid})' href=\"" + osmUrl + "relation/${toid}\" target=\"_blank\">${toid}</a>.";
  14. var sgMvOutRel = "Move out of relation <a onmouseover='grHl(${oid})' onmouseout='grUnHl(${oid})' href=\"" + osmUrl + "relation/${ooid}\" target=\"_blank\">${ooid}</a>";
  15. var sgFixAttr = "Fix attribute <tt>${attr}</tt>.";
  16. var sgAddName = "Consider adding a <tt><a target='_blank' href='https://wiki.openstreetmap.org/wiki/Key:name'>name</a></tt> attribute.";
  17. var sgAttrTr = "Attribute <tt>${attr}</tt> seems to be a track number. Use <tt>ref</tt> for this and set <tt>${attr}</tt> to the station name.";
  18. var suggsMsg = [sgMvOrNew, sgMvOrEx, sgMvRelNew, sgMvRelRel, sgMvOutRel, sgFixAttr, sgAddName, sgAttrTr];
  19. function $(a){return a[0] == "#" ? document.getElementById(a.substr(1)) : a[0] == "." ? document.getElementsByClassName(a.substr(1)) : document.getElementsByTagName(a)}
  20. function $$(t){return document.createElement(t) }
  21. function ll(g){return {"lat" : g[0], "lng" : g[1]}}
  22. function hasCl(e, c){return e.className.split(" ").indexOf(c) != -1}
  23. function addCl(e, c){if (!hasCl(e, c)) e.className += " " + c;e.className = e.className.trim()}
  24. function delCl(e, c){var a = e.className.split(" "); delete a[a.indexOf(c)]; e.className = a.join(" ").trim()}
  25. function stCol(s){return s.e ? stCols[2] : s.s ? stCols[1] : stCols[0]}
  26. function tmpl(s, r){for (var p in r) s = s.replace(new RegExp("\\${" + p + "}", "g"), r[p]); return s}
  27. function req(id, u, cb) {
  28. if (reqs[id]) reqs[id].abort();
  29. reqs[id] = new XMLHttpRequest();
  30. reqs[id].onreadystatechange = function() { if (this.readyState == 4 && this.status == 200 && this == reqs[id]) cb(JSON.parse(this.responseText))};
  31. reqs[id].open("GET", u, 1);
  32. reqs[id].send();
  33. }
  34. function marker(stat, z) {
  35. if (stat.g.length == 1) {
  36. if (z > 15) {
  37. return L.circle(
  38. stat.g[0], {
  39. color: '#000',
  40. fillColor: stCol(stat),
  41. radius: mwidths[23 - z],
  42. fillOpacity: 1,
  43. weight: z > 17 ? 1.5 : 1,
  44. id: stat.i
  45. }
  46. );
  47. } else {
  48. return L.polyline(
  49. [stat.g[0], stat.g[0]], {
  50. color: stCol(stat),
  51. fillColor: stCol(stat),
  52. weight: widths[15 - z],
  53. opacity: opas[15 - z],
  54. id: stat.i
  55. }
  56. );
  57. }
  58. } else {
  59. return L.polygon(
  60. stat.g, {
  61. color: z > 15 ? '#000': stCol(stat),
  62. fillColor: stCol(stat),
  63. smoothFactor: 0,
  64. fillOpacity: 0.75,
  65. weight: z > 17 ? 1.5 : 1,
  66. id: stat.i
  67. }
  68. );
  69. }
  70. }
  71. function poly(group, z) {
  72. var col = group.e ? 'red' : group.s ? '#0000c3' : '#85f385';
  73. var style = {
  74. color: col,
  75. fillColor: col,
  76. smoothFactor: 0.4,
  77. fillOpacity: 0.2,
  78. id: group.i
  79. };
  80. if (z < 16) {
  81. style.weight = 11;
  82. style.opacity = 0.5;
  83. style.fillOpacity = 0.5;
  84. }
  85. return L.polygon(group.g, style)
  86. }
  87. function sugArr(sug, z) {
  88. return L.polyline(sug.a, {
  89. id: sug.i,
  90. color: '#0000c3',
  91. smoothFactor: 0.1,
  92. weight: 4,
  93. opacity: 0.5
  94. });
  95. }
  96. function rndrSt(stat) {
  97. openedSt = stat.id;
  98. stHl(stat.id);
  99. var attrrows = {};
  100. var way = stat.osmid < 0;
  101. var osmid = Math.abs(stat.osmid);
  102. var ident = way ? "Way" : "Node";
  103. var con = $$('div');
  104. con.setAttribute("id", "nav")
  105. var suggD = $$('div');
  106. suggD.setAttribute("id", "sugg")
  107. con.innerHTML = ident + " <a target='_blank' href='" + osmUrl + ident.toLowerCase()+"/" + osmid + "'>" + osmid + "</a>";
  108. if (stat.attrs.name) con.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
  109. con.innerHTML += "<a class='ebut' target='_blank' href='" + osmUrl + ident.toLowerCase() + "=" + osmid +"'>&#9998;</a>";
  110. var attrTbl = $$('table');
  111. attrTbl.setAttribute("id", "attr-tbl")
  112. con.appendChild(attrTbl);
  113. con.appendChild(suggD);
  114. var tbody = $$('tbody');
  115. attrTbl.appendChild(tbody);
  116. for (var key in stat.attrs) {
  117. var row = $$('tr');
  118. var col1 = $$('td');
  119. var col2 = $$('td');
  120. addCl(col2, "err-wrap");
  121. tbody.appendChild(row);
  122. row.appendChild(col1);
  123. row.appendChild(col2);
  124. col1.innerHTML = "<a href=\"https://wiki.openstreetmap.org/wiki/Key:" + key + "\" target=\"_blank\"><tt>" + key + "</tt></a>";
  125. for (var i = 0; i < stat.attrs[key].length; i++) col2.innerHTML += "<span class='attrval'>" + stat.attrs[key][i] + "</span>" + "<br>";
  126. attrrows[key] = row;
  127. }
  128. for (var i = 0; i < stat.attrerrs.length; i++) {
  129. var err = stat.attrerrs[i];
  130. var row = attrrows[err.attr[0]];
  131. addCl(row, "err-" + Math.round(err.conf * 10));
  132. var info = $$('div');
  133. if (err.other_grp) {
  134. // the mismatch was with a group name
  135. if (err.other_osmid > 1) info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <a onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")' target=\"_blank\" href=\"" + osmUrl + "relation/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  136. else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <span onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")'>" + Math.abs(err.other_osmid) + "</span>";
  137. } else {
  138. // the mismatch was with another station
  139. if (err.other_osmid != stat.osmid) {
  140. var lident = err.other_osmid < 0 ? "way" : "node";
  141. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in " + lident + " <a onmouseover='stHl( " + err.other + ")' onmouseout='stUnHl( " + err.other + ")' target=\"_blank\" href=\"" + osmUrl + lident+"/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  142. } else {
  143. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> = '" + err.other_attr[1] + "'";
  144. }
  145. }
  146. addCl(info, 'attr-err-info');
  147. row.childNodes[1].appendChild(info);
  148. }
  149. var suggList = $$('ul');
  150. if (stat.su.length) {
  151. var a = $$('span');
  152. addCl(a, "sugtit");
  153. a.innerHTML = "Suggestions";
  154. suggD.appendChild(a);
  155. }
  156. suggD.appendChild(suggList);
  157. for (var i = 0; i < stat.su.length; i++) {
  158. var sg = stat.su[i];
  159. var sgDiv = $$('li');
  160. sgDiv.innerHTML = tmpl(suggsMsg[sg.type - 1], {"attr" : sg.attr, "tid" : sg.target_gid, "ooid" : sg.orig_osm_rel_id, "toid" : sg.target_osm_rel_id, "oid" : sg.orig_gid});
  161. suggList.appendChild(sgDiv);
  162. }
  163. L.popup({opacity: 0.8})
  164. .setLatLng(stat)
  165. .setContent(con)
  166. .openOn(map)
  167. .on('remove', function() {if (openedSt == stat.id) {openedSt = -1; stUnHl(stat.id)}});
  168. }
  169. function openSt(id) {req("s", "/stat?id=" + id, function(c) {rndrSt(c)});}
  170. function rndrGr(grp, ll) {
  171. openedGr = grp.id;
  172. var attrrows = {};
  173. grHl(grp.id);
  174. var con = $$('div');
  175. con.setAttribute("id", "nav");
  176. var newMembers = $$('div');
  177. newMembers.setAttribute("id", "group-stations-new")
  178. newMembers.innerHTML = "<span class='newmemberstit'>New Members</span>";
  179. var oldMembers = $$('div');
  180. oldMembers.setAttribute("id", "group-stations-old")
  181. oldMembers.innerHTML = "<span class='oldmemberstit'>Existing Members</span>";
  182. if (grp.osmid == 1) {
  183. con.innerHTML = "<span class='grouplink'>New relation</span> <tt>public_transport=stop_area</tt>";
  184. } else {
  185. con.innerHTML = "OSM relation <a target='_blank' href='https://www.openstreetmap.org/relation/" + grp.osmid + "'>" + grp.osmid + "</a>";
  186. if (grp.attrs.name) con.innerHTML += " (<b>\"" + grp.attrs.name + "\"</b>)";
  187. con.innerHTML += "<a class='ebut' target='_blank' href='https://www.openstreetmap.org/edit?relation=" + grp.osmid +"'>&#9998;</a>";
  188. }
  189. var attrTbl = $$('table');
  190. attrTbl.setAttribute("id", "attr-tbl")
  191. con.appendChild(attrTbl);
  192. var tbody = $$('tbody');
  193. attrTbl.appendChild(tbody);
  194. var suggD = $$('div');
  195. suggD.setAttribute("id", "sugg")
  196. for (var key in grp.attrs) {
  197. var row = $$('tr');
  198. var col1 = $$('td');
  199. var col2 = $$('td');
  200. addCl(col2, "err-wrap");
  201. tbody.appendChild(row);
  202. row.appendChild(col1);
  203. row.appendChild(col2);
  204. col1.innerHTML = "<a href=\"https://wiki.openstreetmap.org/wiki/Key:" + key + "\" target=\"_blank\"><tt>" + key + "</tt></a>";
  205. for (var i = 0; i < grp.attrs[key].length; i++) col2.innerHTML += "<span class='attrval'>" + grp.attrs[key][i] + "</span>" + "<br>";
  206. attrrows[key] = row;
  207. }
  208. for (var i = 0; i < grp.attrerrs.length; i++) {
  209. var err = grp.attrerrs[i];
  210. var row = attrrows[err.attr[0]];
  211. addCl(row, "err-" + Math.round(err.conf * 10));
  212. var info = $$('div');
  213. if (err.other_grp) {
  214. // the mismatch was with a group name
  215. if (err.other_osmid != grp.osmid) {
  216. if (err.other_osmid > 1) info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <a onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")' target=\"_blank\" href=\"" + osmUrl + "relation/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  217. else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in relation <span onmouseover='grHl( " + err.other_grp + ")' onmouseout='grUnHl( " + err.other_grp + ")'>" + Math.abs(err.other_osmid) + "</span>";
  218. } else info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> = '" + err.other_attr[1] + "'";
  219. } else {
  220. // the mismatch was with another station
  221. var ident = err.other_osmid < 0 ? "way" : "node";
  222. info.innerHTML = "Does not match <tt>" + err.other_attr[0] + "</tt> in " + ident + " <a onmouseover='stHl( " + err.other + ")' onmouseout='stUnHl( " + err.other + ")' target=\"_blank\" href=\"" + osmUrl + ident+"/" + Math.abs(err.other_osmid) + "\">" + Math.abs(err.other_osmid) + "</a>";
  223. }
  224. addCl(info, 'attr-err-info');
  225. row.childNodes[1].appendChild(info);
  226. }
  227. con.appendChild(newMembers);
  228. if (grp.osmid != 1) con.appendChild(oldMembers);
  229. for (var key in grp.stations) {
  230. var stat = grp.stations[key];
  231. var row = $$('div');
  232. var ident = stat.osmid < 0 ? "Way" : "Node";
  233. row.innerHTML = ident + " <a onmouseover='stHl( " + stat.id + ")' onmouseout='stUnHl( " + stat.id + ")' target='_blank' href='" + osmUrl + ident.toLowerCase() + "/" + Math.abs(stat.osmid) + "'>" + Math.abs(stat.osmid) + "</a>";
  234. if (stat.attrs.name) row.innerHTML += " (<b>\"" + stat.attrs.name + "\"</b>)";
  235. row.style.backgroundColor = stat.e ? '#f58d8d' : stat.s ? '#b6b6e4' : '#c0f7c0';
  236. if (grp.osmid == 1 || stat.orig_group != grp.id) newMembers.appendChild(row);
  237. else {
  238. oldMembers.appendChild(row);
  239. if (stat.group != grp.id) addCl(row, "del-stat");
  240. }
  241. }
  242. var suggList = $$('ul');
  243. if (grp.su.length) {
  244. var a = $$('span');
  245. addCl(a, "sugtit");
  246. a.innerHTML = "Suggestions";
  247. suggD.appendChild(a);
  248. }
  249. suggD.appendChild(suggList);
  250. for (var i = 0; i < grp.su.length; i++) {
  251. var sugg = grp.su[i];
  252. var suggDiv = $$('li');
  253. if (sugg.type == 6) suggDiv.innerHTML = "Fix attribute <tt>" + sugg.attr + "</tt>.";
  254. else if (sugg.type == 7) suggDiv.innerHTML = "Consider adding a <tt><a target='_blank' href='https://wiki.openstreetmap.org/wiki/Key:name'>name</a></tt> attribute.";
  255. else if (sugg.type == 8) suggDiv.innerHTML = "Attribute <tt>" + sugg.attr + "</tt> seems to be a track number. Use <tt>ref</tt> for this and set <tt>" + sugg.attr + "</tt> to the station name.";
  256. suggList.appendChild(suggDiv);
  257. }
  258. con.appendChild(suggD);
  259. L.popup({opacity: 0.8})
  260. .setLatLng(ll)
  261. .setContent(con)
  262. .openOn(map)
  263. .on('remove', function() {if (openedGr == grp.id) {openedGr = -1; grUnHl(grp.id)}});
  264. }
  265. function openGr(id, ll) {
  266. req("g", "/group?id=" + id, function(c) {rndrGr(c, ll)});
  267. }
  268. function grHl(id) {
  269. !grIdx[id] || grIdx[id].setStyle({'weight': 6, 'color': "#eecc00"});
  270. }
  271. function grUnHl(id) {
  272. !grIdx[id] || grIdx[id].setStyle({
  273. 'weight': 3,
  274. 'color': grIdx[id].options["fillColor"]
  275. });
  276. }
  277. function stHl(id) {
  278. if (!stIdx[id]) return;
  279. if (map.getZoom() > 15) {
  280. stIdx[id].setStyle({
  281. 'weight': 5,
  282. 'color': "#eecc00"
  283. });
  284. } else {
  285. stIdx[id].setStyle({
  286. 'color': "#eecc00"
  287. });
  288. }
  289. }
  290. function stUnHl(id) {
  291. if (!stIdx[id]) return;
  292. if (map.getZoom() > 15) {
  293. stIdx[id].setStyle({
  294. 'weight': map.getZoom() > 17 ? 1.5 : 1,
  295. 'color': "#000"
  296. });
  297. } else {
  298. stIdx[id].setStyle({
  299. 'color': stIdx[id].options["fillColor"]
  300. });
  301. }
  302. }
  303. var map = L.map('m', {renderer: L.canvas(), attributionControl: false}).setView([47.9965, 7.8469], 13);
  304. map.addControl(L.control.attribution({
  305. position: 'bottomright',
  306. prefix: '&copy; <a href="http://ad.cs.uni-freiburg.de">University of Freiburg, Chair of Algorithms and Data Structures</a>'
  307. }));
  308. map.on('popupopen', function(e) {
  309. var z = Math.max(map.getZoom(), 16);
  310. var px = map.project(e.target._popup._latlng, z);
  311. px.x += e.target._popup._container.clientWidth/2 - 20;
  312. px.y -= e.target._popup._container.clientHeight/2;
  313. map.setView(map.unproject(px, z), z, {animate: true});
  314. s();
  315. });
  316. L.tileLayer('http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png', {
  317. maxZoom: 20,
  318. attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  319. opacity: 0.8
  320. }).addTo(map);
  321. var l = L.featureGroup().addTo(map);
  322. map.on("moveend", function() {render();});
  323. map.on("click", function() {s()});
  324. map.on("zoomend", function() {$("main")[0].className = '';addCl($("main")[0], "z"+map.getZoom())});
  325. function render() {
  326. if (map.getZoom() < 11) {
  327. var b = map.getBounds();
  328. var sw = b.getSouthWest();
  329. var ne = b.getNorthEast();
  330. req("m", "/heatmap?z=" + map.getZoom() + "&bbox=" + [sw.lat, sw.lng, ne.lat, ne.lng].join(","),
  331. function(re) {
  332. l.clearLayers();
  333. var blur = 22 - map.getZoom();
  334. var rad = 25 - map.getZoom();
  335. l.addLayer(L.heatLayer(re.ok, {
  336. max: 500,
  337. gradient: {
  338. 0: '#cbf7cb',
  339. 0.5: '#78f378',
  340. 1: '#29c329'
  341. },
  342. minOpacity: 0.65,
  343. blur: blur,
  344. radius: rad
  345. }));
  346. l.addLayer(L.heatLayer(re.sugg, {
  347. max: 500,
  348. gradient: {
  349. 0: '#7f7fbd',
  350. 0.5: '#4444b3',
  351. 1: '#0606c1'
  352. },
  353. minOpacity: 0.65,
  354. blur: blur - 3,
  355. radius: Math.min(12, rad - 3)
  356. }));
  357. l.addLayer(L.heatLayer(re.err, {
  358. max: 500,
  359. gradient: {
  360. 0: '#f39191',
  361. 0.5: '#ff5656',
  362. 1: '#ff0000'
  363. },
  364. minOpacity: 0.75,
  365. blur: blur - 3,
  366. radius: Math.min(10, rad - 3),
  367. maxZoom: 15
  368. }));
  369. }
  370. )
  371. } else {
  372. req("m", "/map?z=" + map.getZoom() + "&bbox=" + [map.getBounds().getSouthWest().lat, map.getBounds().getSouthWest().lng, map.getBounds().getNorthEast().lat, map.getBounds().getNorthEast().lng].join(","),
  373. function(re) {
  374. l.clearLayers();
  375. grIdx = {};
  376. stIdx = {};
  377. var stats = [];
  378. for (var i = 0; i < re.stats.length; i++) {
  379. stIdx[re.stats[i].i] = stats[stats.push(marker(re.stats[i], map.getZoom())) - 1];
  380. }
  381. var groups = [];
  382. for (var i = 0; i < re.groups.length; i++) {
  383. grIdx[re.groups[i].i] = groups[groups.push(poly(re.groups[i], map.getZoom())) - 1];;
  384. }
  385. var suggs = [];
  386. for (var i = 0; i < re.su.length; i++) {
  387. suggs.push(sugArr(re.su[i], map.getZoom()));
  388. }
  389. if (map.getZoom() > 13) {
  390. l.addLayer(L.featureGroup(groups).on('click', function(a) {
  391. openGr(a.layer.options.id, a.layer.getBounds().getCenter());
  392. }));
  393. }
  394. l.addLayer(L.featureGroup(stats).on('click', function(a) {
  395. openSt(a.layer.options.id);
  396. }));
  397. if (map.getZoom() > 15) {
  398. l.addLayer(L.featureGroup(suggs).on('click', function(a) {
  399. openSt(a.layer.options.id);
  400. }));
  401. }
  402. grHl(openedGr);
  403. stHl(openedSt);
  404. }
  405. )
  406. };
  407. }
  408. function rowClick(row) {
  409. if (!isSearchOpen()) return;
  410. if (row.stat) openSt(row.stat.i, ll(row.stat.g[0]));
  411. else openGr(row.group.i, ll(row.group.g[0]));
  412. }
  413. function select(row) {
  414. if (!row) return;
  415. if (!isSearchOpen()) return;
  416. unselect(selectedRes);
  417. selectedRes = row;
  418. addCl(row, "selres");
  419. if (row.stat) stHl(row.stat.i);
  420. if (row.group) grHl(row.group.i);
  421. }
  422. function unselect(row) {
  423. selectedRes = undefined;
  424. if (!row) return;
  425. delCl(row, "selres");
  426. if (row.stat && row.stat.i != openedSt) stUnHl(row.stat.i);
  427. if (row.group && row.group.i != openedGr) grUnHl(row.group.i);
  428. }
  429. function isSearchOpen() {
  430. return $("#sres").className == "res-open";
  431. }
  432. function s(q) {
  433. var delay = 0;
  434. if (q == prevSearch) return;
  435. clearTimeout(delayTimer);
  436. prevSearch = q;
  437. //unselect(selectedRes);
  438. if (!q) {
  439. $('#si').value = "";
  440. $("#sres").className = "";
  441. $("#sres").innerHTML = "";
  442. return;
  443. }
  444. delayTimer = setTimeout(function() {
  445. req("sr", "/search?q=" + q, function(c) {
  446. var res = $("#sres");
  447. addCl(res, "res-open");
  448. res.innerHTML = "";
  449. for (var i = 0; i < c.length; i++) {
  450. var e = c[i];
  451. var row = $$('span');
  452. addCl(row, "sres");
  453. row.innerHTML = e.n;
  454. if (e.w) addCl(row, "res-way");
  455. if (e.s) {
  456. row.stat = e.s;
  457. addCl(row, "res-stat");
  458. if (e.s.s) addCl(row, "res-sugg");
  459. if (e.s.e) addCl(row, "res-err");
  460. } else {
  461. row.group = e.g;
  462. addCl(row, "res-group");
  463. if (e.g.s) addCl(row, "res-sugg");
  464. if (e.g.e) addCl(row, "res-err");
  465. }
  466. row.onmouseover = function(){select(this)};
  467. row.onclick = function(){rowClick(this)};
  468. var dist = $$('span');
  469. addCl(dist, "dist");
  470. dist.innerHTML = dstr(e.s ? e.s.g : e.g.g);
  471. row.appendChild(dist);
  472. if (e.v && e.v != e.name) {
  473. var via = $$('span');
  474. addCl(via, "via");
  475. via.innerHTML = e.v;
  476. row.appendChild(via);
  477. }
  478. res.appendChild(row);
  479. }
  480. if ($('.sres').length > 0) select($('.sres')[0]);
  481. }
  482. )}, delay);
  483. }
  484. function dstr(s) {
  485. if (map.getBounds().contains(ll(s[0]))) return "";
  486. var d = map.distance(map.getCenter(), ll(s[0]));
  487. if (d < 500) return Math.round(d).toFixed(0) + "m";
  488. if (d < 5000) return (d / 1000.0).toFixed(1) + "km";
  489. return Math.round(d / 1000.0).toFixed(0) + "km";
  490. }
  491. function kp(e) {
  492. if (e.keyCode == 40 || (!e.shiftKey && e.keyCode == 9)) {
  493. var sels = $('.selres')
  494. if (sels.length) select(sels[0].nextSibling);
  495. else select($('.sres')[0]);
  496. e.preventDefault();
  497. } else if (e.keyCode == 38 || (e.shiftKey && e.keyCode == 9)) {
  498. var sels = $('.selres')
  499. if (sels.length) {
  500. if (sels[0].previousSibling) select(sels[0].previousSibling);
  501. else unselect(sels[0]);
  502. e.preventDefault();
  503. }
  504. }
  505. if (e.keyCode == 13) {
  506. var sels = $('.selres');
  507. if (sels.length) rowClick(sels[0]);
  508. }
  509. }
  510. $('#del').onclick = function() {s();$("#si").focus()}
  511. render();