WME Route Checker

Allows editors to check the route between two segments

当前为 2015-04-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME Route Checker
  3. // @namespace http://userscripts.org/users/419370
  4. // @description Allows editors to check the route between two segments
  5. // @include https://www.waze.com/editor/*
  6. // @include https://www.waze.com/*/editor/*
  7. // @include https://editor-beta.waze.com/*
  8. // @version 1.12
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // globals
  13. var wmerc_version = "1.12";
  14.  
  15. var AVOID_TOLLS = 1;
  16. var AVOID_FREEWAYS = 2;
  17. var AVOID_DIRT = 4;
  18. var ALLOW_UTURNS = 16;
  19. var ROUTE_DIST = 32;
  20.  
  21. var route_options = ALLOW_UTURNS; // default
  22.  
  23. var routeZIndex = 0;
  24.  
  25. function showRouteOptions() {
  26. if (Waze.selectionManager.selectedItems.length != 2) {
  27. WMERC_lineLayer_route.destroyFeatures();
  28. WMERC_lineLayer_route.setZIndex(-999);
  29. return;
  30. }
  31. if (getId('routeOptions') != null) {
  32. return;
  33. }
  34. // hook into edit panel on the left
  35. var userTabs = getId('edit-panel');
  36. var segmentBox = getElementsByClassName('segment', userTabs)[0];
  37. var tabContent = getElementsByClassName('tab-content', segmentBox)[0];
  38.  
  39. // add new edit tab to left of the map
  40. var addon = document.createElement('div');
  41. addon.id = "routeOptions";
  42. addon.style.borderTop = "solid 2px #E9E9E9";
  43. addon.style.borderBottom = "solid 2px #E9E9E9";
  44. if (location.hostname.match(/editor.*.waze.com/)) {
  45. coords1 = getCoords(Waze.selectionManager.selectedItems[0]);
  46. coords2 = getCoords(Waze.selectionManager.selectedItems[1]);
  47. var url = getLivemap()
  48. + "&from_lon="+coords1.lon + "&from_lat="+coords1.lat
  49. + "&to_lon="+coords2.lon + "&to_lat="+coords2.lat;
  50. addon.innerHTML = '<p><b><a href="'+url+'" title="Opens in new tab" target="LiveMap" style="color:#8309e1">Show routes in LiveMap</a> &raquo;</b></p>';
  51. segmentBox.insertBefore(addon, tabContent);
  52. } else {
  53. addon.innerHTML = '<p><b><a href="#" id="goroutes" title="WME Route Checker v'+wmerc_version+'" style="color:#8309e1">'
  54. + 'Show routes between these two segments</a> &raquo;</b><br>'
  55. + '<b>Route:</b>'
  56. + ' <input type="radio" name="_routeType" id="_routeType_fast" value="0" checked> Fastest'
  57. + ' <input type="radio" name="_routeType" id="_routeType_short" value="'+ROUTE_DIST+'"> Shortest<br>'
  58. + '<b>Avoid:</b>'
  59. + ' <input type="checkbox" id="_avoidTolls" /> Tolls'
  60. + ' <input type="checkbox" id="_avoidFreeways" /> Freeways'
  61. + ' <input type="checkbox" id="_avoidDirt" /> Dirt Trails<br>'
  62. + '<b>Allow:</b>'
  63. + ' <input type="checkbox" id="_allowUTurns" /> U-Turns</p>';
  64. segmentBox.insertBefore(addon, tabContent);
  65. getId('_avoidTolls').checked = route_options & AVOID_TOLLS;
  66. getId('_avoidFreeways').checked = route_options & AVOID_FREEWAYS;
  67. getId('_avoidDirt').checked = route_options & AVOID_DIRT;
  68. getId('_allowUTurns').checked = route_options & ALLOW_UTURNS;
  69. getId('_routeType_short').checked = route_options & ROUTE_DIST;
  70. }
  71.  
  72. // create empty div ready for instructions
  73. var routeTest = document.createElement('div');
  74. routeTest.id = "routeTest";
  75. segmentBox.insertBefore(routeTest, tabContent);
  76. // automatically start getting route when user clicks on link
  77. getId('goroutes').onclick = fetchRoute;
  78. return;
  79. }
  80.  
  81. function saveOptions() {
  82. route_options = (getId('_avoidTolls').checked ? AVOID_TOLLS : 0)
  83. + (getId('_avoidFreeways').checked ? AVOID_FREEWAYS : 0)
  84. + (getId('_avoidDirt').checked ? AVOID_DIRT : 0)
  85. + (getId('_allowUTurns').checked ? ALLOW_UTURNS : 0)
  86. + (getId('_routeType_short').checked ? ROUTE_DIST : 0);
  87.  
  88. console.log("WME Route Checker: saving options: " + route_options);
  89. localStorage.WMERouteChecker = JSON.stringify(route_options);
  90. }
  91.  
  92. function getOptions() {
  93. var list = 'AVOID_TOLL_ROADS' + (route_options & AVOID_TOLLS ? ':t' : ':f') + ','
  94. + 'AVOID_PRIMARIES' + (route_options & AVOID_FREEWAYS ? ':t' : ':f') + ','
  95. + 'AVOID_TRAILS' + (route_options & AVOID_DIRT ? ':t' : ':f') + ','
  96. + 'ALLOW_UTURNS' + (route_options & ALLOW_UTURNS ? ':t' : ':f');
  97. return list;
  98. }
  99.  
  100. function getCoords(segment) {
  101. var numpoints = segment.geometry.components.length;
  102. var middle = Math.floor(numpoints / 2);
  103. if (numpoints % 2 == 1 || numpoints < 2) { // odd number, middle point
  104. seglat = segment.geometry.components[middle].y;
  105. seglon = segment.geometry.components[middle].x;
  106. }
  107. else { // even number - take average of middle two points
  108. seglat = (segment.geometry.components[middle].y
  109. + segment.geometry.components[middle-1].y) / 2.0;
  110. seglon = (segment.geometry.components[middle].x
  111. + segment.geometry.components[middle-1].x) / 2.0;
  112. }
  113. return OpenLayers.Layer.SphericalMercator.inverseMercator(seglon,seglat);
  114. }
  115. function fetchRoute(reverse) {
  116. saveOptions();
  117. var coords1, coord2;
  118. reverse = (reverse != false);
  119. if (reverse) {
  120. coords1 = getCoords(Waze.selectionManager.selectedItems[0]);
  121. coords2 = getCoords(Waze.selectionManager.selectedItems[1]);
  122. } else {
  123. coords1 = getCoords(Waze.selectionManager.selectedItems[1]);
  124. coords2 = getCoords(Waze.selectionManager.selectedItems[0]);
  125. }
  126. var img = '<img src="https://www.waze.com/images/search_indicator.gif" hspace="4">';
  127.  
  128. // get the route, fix and parse the json
  129. getId('routeTest').innerHTML = "<p><b>Fetching route from LiveMap " + img + "</b></p>";
  130. var url = getRoutingManager();
  131. var data = {
  132. from: "x:" + coords1.lon + " y:" + coords1.lat + " bd:true",
  133. to: "x:" + coords2.lon + " y:" + coords2.lat + " bd:true",
  134. returnJSON: true,
  135. returnGeometries: true,
  136. returnInstructions: true,
  137. type: (route_options & ROUTE_DIST ? 'DISTANCE' : 'HISTORIC_TIME'),
  138. clientVersion: '4.0.0',
  139. timeout: 60000,
  140. nPaths: 3,
  141. options: getOptions()};
  142.  
  143. $.ajax({
  144. dataType: "json",
  145. url: url,
  146. data: data,
  147. dataFilter: function(data, dataType) {
  148. return data.replace(/NaN/g, '0');
  149. },
  150. success: function(json) {
  151. showNavigation(json, reverse);
  152. }
  153. });
  154. return false;
  155. }
  156.  
  157. function getLivemap() {
  158. var center_lonlat=new OpenLayers.LonLat(Waze.map.center.lon,Waze.map.center.lat);
  159. center_lonlat.transform(new OpenLayers.Projection ("EPSG:900913"),new OpenLayers.Projection("EPSG:4326"));
  160. var coords = '?lon='+center_lonlat.lon+'&lat='+center_lonlat.lat;
  161.  
  162. return 'https://www.waze.com/livemap'+coords;
  163. }
  164. function getRoutingManager() {
  165. if (Waze.model.countries.get(235) || Waze.model.countries.get(40)) { // US & Canada
  166. return '/RoutingManager/routingRequest';
  167. } else if (Waze.model.countries.get(106)) { // Israel
  168. return '/il-RoutingManager/routingRequest';
  169. } else {
  170. return '/row-RoutingManager/routingRequest';
  171. }
  172. }
  173. function plotRoute(coords, index) {
  174. var points = [];
  175. for (var i in coords) {
  176. if (i > 0) {
  177. var point = OpenLayers.Layer.SphericalMercator.forwardMercator(coords[i].x, coords[i].y);
  178. points.push(new OL.Geometry.Point(point.lon,point.lat));
  179. }
  180. }
  181. var newline = new OL.Geometry.LineString(points);
  182. var style = {
  183. strokeColor: routeColors[index],
  184. strokeOpacity: 0.7,
  185. strokeWidth: 8 - index * 2,
  186. };
  187. var lineFeature = new OL.Feature.Vector(newline, {type: "routeArrow"}, style);
  188. // Display new segment
  189. WMERC_lineLayer_route.addFeatures([lineFeature]);
  190. }
  191.  
  192. function showNavigation(nav_json, reverse) {
  193. // plot the route
  194. routeColors = ["#8309e1", "#52BAD9", "#888800", ];
  195. WMERC_lineLayer_route.destroyFeatures();
  196. if (typeof nav_json.alternatives !== "undefined"){
  197. for (var r = nav_json.alternatives.length-1; r >= 0; r--) {
  198. plotRoute(nav_json.alternatives[r].coords, r);
  199. }
  200. } else {
  201. plotRoute(nav_json.coords, 0);
  202. }
  203. WMERC_lineLayer_route.setVisibility(true);
  204. WMERC_lineLayer_route.setZIndex(routeZIndex);
  205.  
  206. // hide segment details
  207. var userTabs = getId('edit-panel');
  208. var segmentBox = getElementsByClassName('segment', userTabs)[0];
  209. var tabContent = getElementsByClassName('tab-content', segmentBox)[0];
  210. tabContent.style.display = 'none';
  211. // write instructions
  212. instructions = getId('routeTest');
  213. instructions.innerHTML = '';
  214. instructions.style.display = 'block';
  215. instructions.style.height = document.getElementById('map').style.height;
  216.  
  217. if (typeof nav_json.alternatives !== "undefined") {
  218. for (var r = 0; r < nav_json.alternatives.length; r++) {
  219. showInstructions(nav_json.alternatives[r], r);
  220. }
  221. nav_coords = nav_json.alternatives[0].coords;
  222. } else {
  223. showInstructions(nav_json, 0);
  224. nav_coords = nav_json.coords;
  225. }
  226. var lon1 = nav_coords[0].x;
  227. var lat1 = nav_coords[0].y;
  228.  
  229. var end = nav_coords.length - 1;
  230. var lon2 = nav_coords[end].x;
  231. var lat2 = nav_coords[end].y;
  232.  
  233. var rerouteArgs = '{lon:'+lon1+',lat:'+lat1+'},{lon:'+lon2+',lat:'+lat2+'}';
  234.  
  235. // footer for extra links
  236. var footer = document.createElement('div');
  237. footer.className = 'routes_footer';
  238. // create link to reverse the route
  239. var reverseLink = document.createElement('a');
  240. reverseLink.innerHTML = '&laquo; Reverse Route';
  241. reverseLink.href = '#';
  242. reverseLink.setAttribute('onClick', 'fetchRoute('+!reverse+');');
  243. reverseLink.addEventListener('click', function() { fetchRoute(!reverse); }, false);
  244. footer.appendChild(reverseLink);
  245.  
  246. footer.appendChild(document.createTextNode(' | '));
  247.  
  248. var url = getLivemap()
  249. + "&from_lon="+lon1 + "&from_lat="+lat1
  250. + "&to_lon="+lon2 + "&to_lat="+lat2;
  251.  
  252. // create link to view the navigation instructions
  253. var livemapLink = document.createElement('a');
  254. livemapLink.innerHTML = 'View in LiveMap &raquo;';
  255. livemapLink.href = url;
  256. livemapLink.target="LiveMap";
  257. footer.appendChild(livemapLink);
  258.  
  259. footer.appendChild(document.createElement('br'));
  260.  
  261. // add link to script homepage and version
  262. var scriptLink = document.createElement('a');
  263. scriptLink.innerHTML = 'WME Route Checker v' + wmerc_version;
  264. scriptLink.href = 'https://www.waze.com/forum/viewtopic.php?t=64777';
  265. scriptLink.style.fontStyle = 'italic';
  266. scriptLink.target="_blank";
  267. footer.appendChild(scriptLink);
  268.  
  269. instructions.appendChild(footer);
  270.  
  271. return false;
  272. }
  273.  
  274. function showInstructions(nav_json, r) {
  275. var imgRoot = '/assets-editor';
  276. continueImage = '<img src="'+imgRoot+'/images/vectors/routeInstructions/big_direction_forward.png" style="float: left; top: 0; padding-right: 4px" width="16" height="16" />';
  277. beginImage = continueImage;
  278.  
  279. // for each route returned by Waze...
  280. var route = nav_json.response;
  281. var streetNames = route.streetNames;
  282.  
  283. if (r > 0) {
  284. instructions.appendChild(document.createElement('p'));
  285. }
  286.  
  287. // name of the route, with coloured icon
  288. route_name = document.createElement('p');
  289. route_name.className = 'route';
  290. route_name.style.borderColor = routeColors[r];
  291. route_name.innerHTML = '<b style="color:'+routeColors[r]+'">Via ' + route.routeName + '</b>';
  292. instructions.appendChild(route_name);
  293. if (route.tollMeters > 0) {
  294. route_name.innerHTML = '<span style="float: right">TOLL</span>' + route_name.innerHTML;
  295. }
  296.  
  297. var optail = '';
  298. var prevStreet = '';
  299. var currentItem = null;
  300. var totalDist = 0;
  301. var totalTime = 0;
  302. var prevDist = 0;
  303. var prevTime = 0;
  304. var lastSegment = route.results[0].path.segmentId;
  305.  
  306. // iterate over all the steps in the list
  307. for (var i = 0; i < route.results.length; i++) {
  308. prevDist = totalDist;
  309. prevTime = totalTime;
  310. totalDist += route.results[i].length;
  311. totalTime += route.results[i].crossTime;
  312.  
  313. if (!route.results[i].instruction)
  314. continue;
  315. var opcode = route.results[i].instruction.opcode;
  316. if (!opcode)
  317. continue;
  318.  
  319. if (opcode.match(/ROUNDABOUT_EXIT/))
  320. continue;
  321.  
  322. switch (opcode) {
  323. case "CONTINUE":
  324. case "NONE": dirImage = "big_direction_forward"; break;
  325. case "TURN_LEFT": dirImage = "big_direction_left"; break;
  326. case "TURN_RIGHT": dirImage = "big_direction_right"; break;
  327. case "KEEP_LEFT":
  328. case "EXIT_LEFT": dirImage = "big_direction_exit_left"; break;
  329. case "KEEP_RIGHT":
  330. case "EXIT_RIGHT": dirImage = "big_direction_exit_right"; break;
  331. case "UTURN": dirImage = "big_directions_roundabout_u"; break;
  332. case "APPROACHING_DESTINATION": dirImage = "big_direction_end"; break;
  333. case "ROUNDABOUT_LEFT":
  334. case "ROUNDABOUT_EXIT_LEFT": dirImage = "big_directions_roundabout_l"; break;
  335. case "ROUNDABOUT_RIGHT":
  336. case "ROUNDABOUT_EXIT_RIGHT": dirImage = "big_directions_roundabout_r"; break;
  337. case "ROUNDABOUT_STRAIGHT":
  338. case "ROUNDABOUT_EXIT_STRAIGHT": dirImage = "big_directions_roundabout_s"; break;
  339. case "ROUNDABOUT_ENTER":
  340. case "ROUNDABOUT_EXIT": dirImage = "big_directions_roundabout"; break;
  341. case "ROUNDABOUT_U": dirImage = "big_directions_roundabout_u"; break;
  342. default: dirImage = '';
  343. }
  344. if (dirImage != '')
  345. dirImageSrc = '<img src="'+imgRoot+'/images/vectors/routeInstructions/'+dirImage+'.png" style="float: left; top: 0; padding-right: 4px" width="16" height="16" />';
  346.  
  347. var streetName = route.streetNames[route.results[i].street];
  348. if (!streetName || streetName == null)
  349. streetName = '';
  350. else
  351. streetName = '<span style="color: blue">' + streetName + '</span>';
  352.  
  353. if (streetName.match(/^to/i))
  354. optail = ' ';
  355.  
  356. // display new, non-blank street names
  357. if ((streetName != prevStreet && streetName != '') || i == 0) {
  358. if (currentItem == null) {
  359. // new street that doesn't follow turn instruction, i.e. continue
  360. if (optail == '')
  361. optail = continueImage + 'continue on ';
  362.  
  363. if (i == 0) { // from location
  364. var lon = nav_json.coords[0].x;
  365. var lat = nav_json.coords[0].y;
  366. optail = beginImage + 'depart from ';
  367. } else { // start of street location
  368. var lon = route.results[i].path.x;
  369. var lat = route.results[i].path.y;
  370. }
  371.  
  372. addTurnImage(lat, lon, dirImage);
  373. currentItem = document.createElement('a');
  374. currentItem.className = 'step';
  375. currentItem.innerHTML = optail + streetName;
  376. } else {
  377. // these will be appended to previous turn instruction
  378. currentItem.innerHTML += optail + streetName;
  379. }
  380.  
  381. instructions.appendChild(currentItem);
  382. prevStreet = streetName;
  383. currentItem = null;
  384. optail = '';
  385. }
  386.  
  387. if (opcode == 'CONTINUE' || opcode == 'NONE') {
  388. continue;
  389. }
  390.  
  391. // roundabouts with nth exit instructions
  392. if (opcode == 'ROUNDABOUT_ENTER') {
  393. opcode += route.results[i].instruction.arg + 'th exit';
  394. opcode = opcode.replace(/1th/, '1st');
  395. opcode = opcode.replace(/2th/, '2nd');
  396. opcode = opcode.replace(/3th/, '3rd');
  397. opcode = opcode.replace(/4th/, '4th');
  398. opcode = opcode.replace(/0th/, '<span style="color: red">0th</span>');
  399. }
  400.  
  401. // convert opcode to pretty text
  402. opcode = opcode.replace(/APPROACHING_DESTINATION/, 'arrive at destination');
  403. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?LEFT/, 'at the roundabout, turn left');
  404. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?RIGHT/, 'at the roundabout, turn right');
  405. opcode = opcode.replace(/ROUNDABOUT_(EXIT_)?STRAIGHT/, 'at the roundabout, continue straight');
  406. opcode = opcode.replace(/ROUNDABOUT_(ENTER|EXIT)/, 'at the roundabout, take ');
  407. opcode = opcode.toLowerCase().replace(/_/, ' ');
  408. opcode = opcode.replace(/uturn/, 'make a U-turn');
  409. opcode = opcode.replace(/roundabout u/, 'at the roundabout, make a U-turn');
  410.  
  411. // flush previous instruction to list
  412. if (currentItem != null) {
  413. instructions.appendChild(currentItem);
  414. currentItem = null;
  415. }
  416.  
  417. // get coordinates of instruction
  418. if (i+1 < route.results.length) { // location of turn
  419. var lon = route.results[i+1].path.x;
  420. var lat = route.results[i+1].path.y;
  421. } else { // destination location
  422. var end = nav_json.coords.length - 1;
  423. var lon = nav_json.coords[end].x;
  424. var lat = nav_json.coords[end].y;
  425. }
  426. addTurnImage(lat, lon, dirImage);
  427. lastSegment = route.results[i].path.segmentId;
  428.  
  429. // create new instruction
  430. currentItem = document.createElement('a');
  431. currentItem.className = 'step';
  432. currentItem.innerHTML = dirImageSrc + opcode;
  433.  
  434. // connecting word in case we have a street name later
  435. if (opcode.match(/continue/))
  436. optail = ' on ';
  437. else
  438. optail = ' onto ';
  439.  
  440. // force 'turn left' and 'turn right' to show the street name
  441. if (opcode.match(/turn left|turn right/))
  442. prevStreet = '';
  443. }
  444.  
  445. // flush last instruction to list
  446. if (currentItem != null) {
  447. currentItem.title = (totalDist/1609).toFixed(3) + " miles";
  448. currentItem.innerHTML += ' - ' + totalDist/1000 + ' km';
  449. currentItem.innerHTML += ' - ' + timeFromSecs(totalTime);
  450. instructions.appendChild(currentItem);
  451. currentItem = null;
  452. }
  453. };
  454.  
  455. function timeFromSecs(seconds)
  456. {
  457. hh = '00'+Math.floor(((seconds/86400)%1)*24);
  458. mm = '00'+Math.floor(((seconds/3600)%1)*60);
  459. ss = '00'+Math.round(((seconds/60)%1)*60);
  460. return hh.slice(-2) + ':' + mm.slice(-2) + ':' + ss.slice(-2);
  461. }
  462.  
  463. function addTurnImage(lat, lon, image, title) {
  464. var coords = OpenLayers.Layer.SphericalMercator.forwardMercator(lon, lat);
  465. var point = new OL.Geometry.Point(coords.lon,coords.lat);
  466. var imgRoot = '/assets-editor';
  467. var style = {
  468. externalGraphic: imgRoot + "/images/vectors/routeInstructions/"+image+".png",
  469. graphicWidth: 30,
  470. graphicHeight: 32,
  471. graphicZIndex: 9999,
  472. title: title
  473. };
  474. var imageFeature = new OL.Feature.Vector(point, null, style);
  475. // Display new segment
  476. WMERC_lineLayer_route.addFeatures([imageFeature]);
  477. }
  478.  
  479. /* helper function */
  480. function getElementsByClassName(classname, node) {
  481. if(!node) node = document.getElementsByTagName("body")[0];
  482. var a = [];
  483. var re = new RegExp('\\b' + classname + '\\b');
  484. var els = node.getElementsByTagName("*");
  485. for (var i=0,j=els.length; i<j; i++)
  486. if (re.test(els[i].className)) a.push(els[i]);
  487. return a;
  488. }
  489.  
  490. function getId(node) {
  491. return document.getElementById(node);
  492. }
  493.  
  494. function initialiseRouteChecker() {
  495. console.log("WME Route Checker: initialising v" + wmerc_version);
  496. if (localStorage.WMERouteChecker) {
  497. route_options = JSON.parse(localStorage.WMERouteChecker);
  498. console.log("WME Route Checker: loaded options: " + route_options);
  499. }
  500.  
  501. /* dirty hack to inject stylesheet in to the DOM */
  502. var style = document.createElement('style');
  503. style.innerHTML = "#routeTest {padding: 0 4px 0 0; overflow-y: auto;}\n"
  504. + "#routeTest p.route {margin: 0; padding: 4px 8px; border-bottom: silver solid 3px; background: #eee}\n"
  505. + "#routeTest a.step {display: block; margin: 0; padding: 3px 8px; text-decoration: none; color:black;border-bottom: silver solid 1px;}\n"
  506. + "#routeTest a.step:hover {background: #ffd;}\n"
  507. + "#routeTest a.step:active {background: #dfd;}\n"
  508. + "#routeTest div.routes_footer {text-align: center; margin-bottom: 25px;}\n";
  509. (document.body || document.head || document.documentElement).appendChild(style);
  510. // add a new layer for routes
  511. WMERC_lineLayer_route = new OL.Layer.Vector("Route Checker Script",
  512. { rendererOptions: { zIndexing: true },
  513. shortcutKey: "S+t",
  514. uniqueName: 'route_checker' }
  515. );
  516. Waze.map.addLayer(WMERC_lineLayer_route);
  517. Waze.map.addControl(new OL.Control.DrawFeature(WMERC_lineLayer_route, OL.Handler.Path));
  518. // find best ZIndex
  519. for (var i = 0; i < Waze.map.layers.length; i++) {
  520. l = Waze.map.layers[i];
  521. if (/Waze.Control.SelectHighlightFeature/.test(l.name)) {
  522. routeZIndex = l.getZIndex()+1;
  523. }
  524. }
  525. // hack in translation:
  526. I18n.translations[I18n.locale].layers.name.route_checker = "Route Checker Script";
  527. // ...and then hide it
  528. $("label:contains('Route Checker Script')").parent().remove();
  529. // add listener for whenever selection changes
  530. Waze.selectionManager.events.register("selectionchanged", null, showRouteOptions);
  531. showRouteOptions(); // for permalinks
  532. }
  533.  
  534. // bootstrap!
  535. (function()
  536. {
  537. setTimeout(initialiseRouteChecker, 1003);
  538. })();
  539.  
  540. /* end ======================================================================= */