WME E95

Setup road properties in one click

目前为 2019-08-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name WME E95
  3. // @version 0.4.12
  4. // @description Setup road properties in one click
  5. // @author Anton Shevchuk
  6. // @license MIT License
  7. // @include https://www.waze.com/editor*
  8. // @include https://www.waze.com/*/editor*
  9. // @include https://beta.waze.com/editor*
  10. // @include https://beta.waze.com/*/editor*
  11. // @exclude https://www.waze.com/user/editor*
  12. // @exclude https://beta.waze.com/user/editor*
  13. // @icon data:image/png;base64,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
  14. // @grant none
  15. // @supportURL https://github.com/AntonShevchuk/wme-e95/issues
  16. // @namespace https://greasyfork.org/uk/scripts/382614-wme-e95
  17. // ==/UserScript==
  18. /* jshint esversion: 6 */
  19. /* global require, window */
  20.  
  21. (function ($, WazeApi, I18n) {
  22. 'use strict';
  23.  
  24. // Script name, uses as unique index
  25. const NAME = 'E95';
  26.  
  27. // Translations
  28. const LOCALE = I18n.currentLocale();
  29. const translation = {
  30. 'en': {
  31. title: 'Quick Properties'
  32. },
  33. 'uk': {
  34. title: 'Швидкі налаштування',
  35. },
  36. 'ru': {
  37. title: 'Быстрые настройки'
  38. }
  39. };
  40. // Road Types
  41. // I18n.translations.uk.segment.road_types
  42. const types = {
  43. street: 1,
  44. primary: 2,
  45. // ...
  46. offroad: 8,
  47. // ...
  48. private: 17,
  49. // ...
  50. parking: 20,
  51. };
  52. // Road colors by type
  53. const colors = {
  54. '1': '#ffffeb',
  55. '2': '#f0ea58',
  56. // ...
  57. '8': '#867342',
  58. // ...
  59. '17': '#beba6c',
  60. // ...
  61. '20': '#ababab'
  62. };
  63. // Road Flags
  64. // for setup flags use binary operators
  65. // e.g. flags.tunnel | flags.headlights
  66. const flags = {
  67. tunnel: 0b00000001,
  68. // ??? : 0b00000010,
  69. // ??? : 0b00000100,
  70. // ??? : 0b00001000,
  71. unpaved: 0b00010000,
  72. headlights: 0b00100000,
  73. };
  74. // Buttons:
  75. // title - for buttons
  76. // keyCode - key for shortcuts (Alt + 1..9)
  77. // detectCity - try to detect city name by closures segments
  78. // clearCity - clear city name
  79. // attributes - native settings for model object
  80. // TODO:
  81. // – check permissions for user level lower than 2
  82. const buttons = {
  83. A: {
  84. title: 'PLR',
  85. shortcut: 'A+49',
  86. detectCity: true,
  87. attributes: {
  88. fwdMaxSpeed: 5,
  89. revMaxSpeed: 5,
  90. fwdMaxSpeedUnverified: false,
  91. revMaxSpeedUnverified: false,
  92. roadType: types.parking,
  93. flags: 0,
  94. lockRank: 0,
  95. }
  96. },
  97. B: {
  98. title: 'Pr20',
  99. shortcut: 'A+50',
  100. detectCity: true,
  101. attributes: {
  102. fwdMaxSpeed: 20,
  103. revMaxSpeed: 20,
  104. fwdMaxSpeedUnverified: false,
  105. revMaxSpeedUnverified: false,
  106. roadType: types.private,
  107. flags: 0,
  108. lockRank: 0,
  109. }
  110. },
  111. C: {
  112. title: 'Pr50',
  113. shortcut: 'A+51',
  114. detectCity: true,
  115. attributes: {
  116. fwdMaxSpeed: 50,
  117. revMaxSpeed: 50,
  118. fwdMaxSpeedUnverified: false,
  119. revMaxSpeedUnverified: false,
  120. roadType: types.private,
  121. flags: 0,
  122. lockRank: 0,
  123. }
  124. },
  125. D: {
  126. title: 'St50',
  127. shortcut: 'A+52',
  128. detectCity: true,
  129. attributes: {
  130. fwdMaxSpeed: 50,
  131. revMaxSpeed: 50,
  132. roadType: types.street,
  133. flags: 0,
  134. lockRank: 0,
  135. }
  136. },
  137. E: {
  138. title: 'PS50',
  139. shortcut: 'A+53',
  140. detectCity: true,
  141. attributes: {
  142. fwdMaxSpeed: 50,
  143. revMaxSpeed: 50,
  144. fwdMaxSpeedUnverified: false,
  145. revMaxSpeedUnverified: false,
  146. roadType: types.primary,
  147. flags: 0,
  148. lockRank: 1,
  149. }
  150. },
  151. F: {
  152. title: 'OR',
  153. shortcut: 'A+54',
  154. clearCity: true,
  155. attributes: {
  156. fwdMaxSpeed: 90,
  157. revMaxSpeed: 90,
  158. fwdMaxSpeedUnverified: false,
  159. revMaxSpeedUnverified: false,
  160. roadType: types.offroad,
  161. lockRank: 0,
  162. }
  163. },
  164. G: {
  165. title: 'Pr90',
  166. shortcut: 'A+55',
  167. clearCity: true,
  168. attributes: {
  169. fwdMaxSpeed: 90,
  170. revMaxSpeed: 90,
  171. fwdMaxSpeedUnverified: false,
  172. revMaxSpeedUnverified: false,
  173. roadType: types.private,
  174. lockRank: 0,
  175. }
  176. },
  177. H: {
  178. title: 'St90',
  179. shortcut: 'A+56',
  180. clearCity: true,
  181. attributes: {
  182. fwdMaxSpeed: 90,
  183. revMaxSpeed: 90,
  184. fwdMaxSpeedUnverified: false,
  185. revMaxSpeedUnverified: false,
  186. roadType: types.street,
  187. lockRank: 0,
  188. }
  189. },
  190. I: {
  191. title: 'PS90',
  192. shortcut: 'A+57',
  193. clearCity: true,
  194. attributes: {
  195. fwdMaxSpeed: 90,
  196. revMaxSpeed: 90,
  197. fwdMaxSpeedUnverified: false,
  198. revMaxSpeedUnverified: false,
  199. roadType: types.primary,
  200. lockRank: 1,
  201. }
  202. },
  203. };
  204. // Regions settings, will be merged with default values
  205. // Default values is actual for Ukraine
  206. const speed = {
  207. '20': {
  208. fwdMaxSpeed: 20,
  209. revMaxSpeed: 20,
  210. },
  211. '60': {
  212. fwdMaxSpeed: 60,
  213. revMaxSpeed: 60,
  214. }
  215. };
  216. const preset = {
  217. headlights: {
  218. attributes: {
  219. flags: flags.headlights
  220. }
  221. },
  222. pr60: {
  223. title: 'Pr60',
  224. attributes: speed["60"]
  225. },
  226. st60: {
  227. title: 'St60',
  228. attributes: speed["60"]
  229. },
  230. ps60: {
  231. title: 'PS60',
  232. attributes: speed["60"]
  233. },
  234. };
  235. const region = {
  236. // Belarus
  237. 'BO': {
  238. A: {
  239. attributes: speed["20"]
  240. },
  241. C: preset.pr60,
  242. D: preset.st60,
  243. E: preset.ps60,
  244. F: {
  245. title: 'SUP',
  246. attributes: {
  247. roadType: types.street,
  248. flags: flags.unpaved,
  249. }
  250. }
  251. },
  252. // Russian Federation
  253. 'RS': {
  254. C: preset.pr60,
  255. D: preset.st60,
  256. E: preset.ps60,
  257. },
  258. // Ukraine
  259. 'UP': {
  260. F: preset.headlights,
  261. G: preset.headlights,
  262. H: preset.headlights,
  263. I: preset.headlights,
  264. }
  265. };
  266.  
  267. // Require Waze API
  268. let WazeActionUpdateObject = require('Waze/Action/UpdateObject');
  269. let WazeActionUpdateFeatureAddress = require('Waze/Action/UpdateFeatureAddress');
  270.  
  271. // Get Button settings
  272. function getButtonConfig(index) {
  273. let btn = {};
  274. let abbr = WazeApi.model.getTopCountry().getAttributes().abbr;
  275. if (region[abbr] && region[abbr][index]) {
  276. // Merge default settings with region settings
  277. $.extend(true, btn, buttons[index], region[abbr][index]);
  278. } else {
  279. btn = buttons[index];
  280. }
  281. return btn;
  282. }
  283.  
  284. // Update segment attributes
  285. function setupRoad(segment, settings, options = []) {
  286. let addr = segment.getAddress().attributes;
  287. // Change address
  288. let address = {
  289. countryID: addr.country ? addr.country.id : WazeApi.model.getTopCountry().getID(),
  290. stateID: addr.state ? addr.state.id : WazeApi.model.getTopState().getID(),
  291. cityName: addr.city ? addr.city.attributes.name : null,
  292. streetName: addr.street ? addr.street.name : null,
  293. };
  294. // Settings: Clear city
  295. if (settings.clearCity) {
  296. address.cityName = null;
  297. }
  298. // Settings: Detect city
  299. if (settings.detectCity && options.cityName) {
  300. address.cityName = options.cityName;
  301. }
  302. // Check city
  303. address.emptyCity = (address.cityName === null);
  304. // Check street
  305. address.emptyStreet = (address.streetName === null) || (address.streetName === '');
  306. // Update segment properties
  307. WazeApi.model.actionManager.add(
  308. new WazeActionUpdateObject(
  309. segment,
  310. settings.attributes
  311. )
  312. );
  313. // Update segment address
  314. WazeApi.model.actionManager.add(
  315. new WazeActionUpdateFeatureAddress(
  316. segment,
  317. address,
  318. {
  319. streetIDField: 'primaryStreetID'
  320. }
  321. )
  322. );
  323. }
  324.  
  325. // Update street handler
  326. function processHandler() {
  327. process(this.dataset.e95);
  328. }
  329.  
  330. function process(index) {
  331. // Get all selected segments
  332. let selected = WazeApi.selectionManager.getSelectedFeatures();
  333. let segments = [];
  334. let options = {};
  335. // Fill segments array
  336. for (let i = 0, total = selected.length; i < total; i++) {
  337. segments.push(WazeApi.model.segments.getObjectById(selected[i].model.attributes.id))
  338. }
  339. // Filter segments array
  340. segments = segments.filter(segment => segment && segment.getPermissions());
  341. // Try to detect city
  342. if (getButtonConfig(index).detectCity) {
  343. let cityName = null;
  344. for (let i = 0, total = segments.length; i < total; i++) {
  345. cityName = detectCity(segments[i]);
  346. if (cityName) {
  347. options.cityName = cityName;
  348. break;
  349. }
  350. }
  351. log('detected city ' + cityName);
  352. }
  353.  
  354. for (let i = 0, total = segments.length; i < total; i++) {
  355. setupRoad(segments[i], getButtonConfig(index), options);
  356. }
  357. }
  358.  
  359. // Detect city name by connected segments
  360. function detectCity(segment) {
  361. // Check cityName of the segment
  362. if (segment.getAddress().getCity() && !segment.getAddress().getCity().isEmpty()) {
  363. return segment.getAddress().getCity().getName();
  364. }
  365. let cityName = null;
  366. // TODO: replace follow magic with
  367. // segment.getConnectedSegments() and segment.getConnectedSegmentsByDirection() when it will work
  368. // last check - 30.07.19
  369. let connected = WazeApi.model.nodes.getObjectById(segment.getAttributes().fromNodeID).getSegmentIds(); // segments from point A
  370. connected = connected.concat(WazeApi.model.nodes.getObjectById(segment.getAttributes().toNodeID).getSegmentIds()); // segments from point B
  371. connected.filter(id => id !== segment.getID());
  372.  
  373. for (let i = 0, total = connected.length; i < total; i++) {
  374. let city = WazeApi.model.segments.getObjectById(connected[i]).getAddress().getCity();
  375. // skip segments with empty cities
  376. if (city && !city.isEmpty()) {
  377. cityName = city.getName();
  378. break;
  379. }
  380. }
  381. return cityName;
  382. }
  383.  
  384. // Create UI controls everytime when updated DOM of sidebar
  385. // Uses native JS function for better performance
  386. function createUI() {
  387. // Container for buttons
  388. let controls = document.createElement('div');
  389. controls.className = 'controls';
  390. // Create buttons
  391. for (let btn in buttons) {
  392. let config = getButtonConfig(btn);
  393. let button = document.createElement('button');
  394. button.className = 'waze-btn waze-btn-small e95 e95-' + btn;
  395. button.style.backgroundColor = colors[config.attributes.roadType];
  396. button.innerHTML = config.title;
  397. button.title = I18n.translate('segment.road_types')[config.attributes.roadType];
  398. button.dataset.e95 = btn;
  399. controls.appendChild(button);
  400. }
  401.  
  402. let label = document.createElement('label');
  403. label.className = 'control-label';
  404. label.innerHTML = I18n.translate(NAME)['title'];
  405.  
  406. let group = document.createElement('div');
  407. group.className = 'form-group ' + NAME;
  408. group.appendChild(label);
  409. group.appendChild(controls);
  410.  
  411. document.getElementById('segment-edit-general').prepend(group);
  412. }
  413.  
  414. // Apply CSS styles
  415. function appendStyle(css) {
  416. let style = document.createElement('style');
  417. style.type = 'text/css';
  418. style.innerHTML = css;
  419. document.getElementsByTagName('head')[0].appendChild(style);
  420. }
  421.  
  422. // Simple console.log wrapper
  423. function log(message) {
  424. console.log(NAME + ': ' + message);
  425. }
  426.  
  427. // Initial Translation for UI and Shortcuts
  428. function initTranslation() {
  429. I18n.translations[LOCALE][NAME] = translation[LOCALE] || translation['en'];
  430.  
  431. // Translation for Shortcuts
  432. I18n.translations[LOCALE].keyboard_shortcuts.groups[NAME] = [];
  433. I18n.translations[LOCALE].keyboard_shortcuts.groups[NAME].description = NAME;
  434. I18n.translations[LOCALE].keyboard_shortcuts.groups[NAME].members = [];
  435.  
  436. // Create description for every button
  437. for (let btn in buttons) {
  438. let name = NAME + 'Button' + buttons[btn].title;
  439. // Build description
  440. I18n.translations[LOCALE].keyboard_shortcuts.groups[NAME].members[name] =
  441. buttons[btn].title + ' - ' +
  442. I18n.translate('segment.road_types')[buttons[btn].attributes.roadType] + '; ' +
  443. I18n.translate('edit.segment.fields.speed_limit') + ' ' +
  444. I18n.translate('measurements.speed.km', {speed: buttons[btn].attributes.fwdMaxSpeed})
  445. ;
  446. }
  447. }
  448.  
  449. // Initial Mutation Observer
  450. // #segment-edit-general - for segment tab
  451. // #landmark-edit-general - for POI tab
  452. function initObserver() {
  453. // Check for changes in the edit-panel
  454. // TODO: try to find solutions to handle native event
  455. let speedLimitsObserver = new MutationObserver(function (mutations) {
  456. mutations.forEach(function (mutation) {
  457. for (let i = 0, total = mutation.addedNodes.length; i < total; i++) {
  458. let node = mutation.addedNodes[i];
  459. // Only fire up if it's a node
  460. if (node.nodeType === Node.ELEMENT_NODE &&
  461. node.querySelector('div.selection') &&
  462. node.querySelector('#segment-edit-general') && // segment tab
  463. node.querySelector('div.hide-walking-trail').style.display !== 'none' && // skip for walking trails
  464. !node.querySelector('div.form-group.' + NAME)) {
  465. createUI();
  466. }
  467. }
  468. });
  469. });
  470.  
  471. speedLimitsObserver.observe(document.getElementById('edit-panel'), {childList: true, subtree: true});
  472. log('observer was run');
  473. }
  474.  
  475. function initButtons() {
  476. $('#edit-panel').on('click', 'button.e95', processHandler);
  477. }
  478.  
  479. function initShortcuts() {
  480. WazeApi.accelerators.Groups[NAME] = [];
  481. WazeApi.accelerators.Groups[NAME].members = [];
  482.  
  483. for (let btn in buttons) {
  484. let name = NAME + 'Button' + buttons[btn].title;
  485. WazeApi.accelerators.addAction(name, { group: NAME });
  486. WazeApi.accelerators.events.register(name, null, () => process(btn));
  487. WazeApi.accelerators.registerShortcut(buttons[btn].shortcut, name);
  488. }
  489. }
  490.  
  491. function init() {
  492. // Initial Translation
  493. initTranslation();
  494.  
  495. // Initial Mutation Observer
  496. initObserver();
  497.  
  498. // Handler for all buttons
  499. initButtons();
  500.  
  501. // Handler for button shortcuts
  502. initShortcuts();
  503.  
  504. // Apply CSS styles
  505. appendStyle(
  506. 'button.waze-btn.e95 { margin: 0 4px 4px 0; padding: 2px; width: 42px; } ' +
  507. 'button.waze-btn.e95:hover { box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1), inset 0 0 100px 100px rgba(255, 255, 255, 0.3); } ' +
  508. 'button.waze-btn.e95-E { margin-right: 42px; }' +
  509. 'button.waze-btn.e95-F { margin-right: 50px; }'
  510. );
  511. }
  512.  
  513. // Bootstrap plugin
  514. function bootstrap(tries = 1) {
  515. log('attempt ' + tries);
  516. if (WazeApi &&
  517. WazeApi.map &&
  518. WazeApi.model &&
  519. WazeApi.loginManager.user) {
  520. log('was initialized');
  521. init();
  522. } else if (tries < 100) {
  523. tries++;
  524. setTimeout(() => bootstrap(tries), 500);
  525. } else {
  526. console.error('initialization failed');
  527. }
  528. }
  529.  
  530. log('initialization');
  531. bootstrap();
  532. })(window.jQuery, window.W, window.I18n);