Waze Edit Count Monitor

Displays your daily edit count in the WME toolbar. Warns if you might be throttled.

当前为 2018-07-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Waze Edit Count Monitor
  3. // @namespace https://greasyfork.org/en/users/45389-mapomatic
  4. // @version 2018.07.24.001
  5. // @description Displays your daily edit count in the WME toolbar. Warns if you might be throttled.
  6. // @author MapOMatic
  7. // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
  8. // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
  9. // @license GNU GPLv3
  10. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  11. // @grant GM_xmlhttpRequest
  12. // @connect www.waze.com
  13.  
  14. // ==/UserScript==
  15.  
  16. /* global W */
  17. /* global GM_info */
  18. /* global toastr */
  19.  
  20. // This function is injected into the page to allow it to run in the page's context.
  21. function WECM_Injected() {
  22. 'use strict';
  23.  
  24. let _toastrSettings = {
  25. remindAtEditCount: 100,
  26. warnAtEditCount: 150,
  27. wasReminded: false,
  28. wasWarned: false
  29. };
  30.  
  31. var debugLevel = 0;
  32. var $outputElem = null;
  33. var $outputElemContainer = null;
  34. var lastEditCount = null;
  35. var userName = null;
  36. var savesWithoutIncrease = 0;
  37. var lastURCount = null;
  38. var lastMPCount = null;
  39. var tooltipText = 'Your daily edit count from your profile. Click to open your profile.';
  40.  
  41. function log(message, level) {
  42. if (message && level <= debugLevel) {
  43. console.log('Edit Count Monitor: ' + message);
  44. }
  45. }
  46.  
  47. function checkEditCount() {
  48. window.postMessage(JSON.stringify(['wecmGetCounts',userName]),'*');
  49. _toastrSettings.wasReminded = false;
  50. _toastrSettings.wasWarned = false;
  51. toastr.remove();
  52. }
  53.  
  54. function getChangedObjectCount() {
  55. let count = 0;
  56. let changed = W.model._getModifiedObjects();
  57. Object.keys(changed).forEach(key => {
  58. let obj = changed[key];
  59. count += obj.Insert.length + obj.Update.length + obj.Delete.length;
  60. });
  61. return count;
  62. }
  63.  
  64. function updateEditCount(editCount, urCount, mpCount, noIncrement) {
  65. var textColor;
  66. var bgColor;
  67. var tooltipTextColor;
  68.  
  69. // Add the counter div if it doesn't exist.
  70. if ($('#wecm-count').length === 0) {
  71. $outputElemContainer = $('<div>', {style:'position:relative; border-radius:23px; text-color:#354148; height:24px; padding-top:1px; padding-left:10px; padding-right:10px; display:block; float:right; margin-top:11px; font-weight:bold; font-size:medium;'}); //margin:9px 5px 8px 5px; display:inline;
  72. $outputElem = $('<a>', {id: 'wecm-count',
  73. href:'https://www.waze.com/user/editor/' + userName.toLowerCase(),
  74. target: '_blank',
  75. style:'text-decoration:none',
  76. 'data-original-title': tooltipText});
  77. $outputElemContainer.append($outputElem);
  78. $('#edit-buttons').children().first().append($outputElemContainer);
  79. $outputElem.tooltip({
  80. placement: 'auto top',
  81. delay: {show: 100, hide: 100},
  82. html: true,
  83. template: '<div class="tooltip" role="tooltip" style="opacity:0.95"><div class="tooltip-arrow"></div><div class="my-tooltip-header" style="display:block;"><b></b></div><div class="my-tooltip-body tooltip-inner" style="display:block; font-weight:600; !important"></div></div>'
  84. });
  85. }
  86.  
  87. log('edit count = ' + editCount + ', UR count = ' + urCount.count, 1);
  88. if (lastEditCount !== editCount || lastURCount.count !== urCount.count || lastMPCount !== mpCount.count) {
  89. savesWithoutIncrease = 0;
  90. } else {
  91. if (!noIncrement) savesWithoutIncrease += 1;
  92. }
  93.  
  94. switch (savesWithoutIncrease) {
  95. case 0:
  96. case 1:
  97. textColor = '#354148';
  98. bgColor = '';
  99. tooltipTextColor = 'white';
  100. break;
  101. case 2:
  102. textColor = '#354148';
  103. bgColor = 'yellow';
  104. tooltipTextColor = 'black';
  105. break;
  106. default:
  107. textColor = 'white';
  108. bgColor = 'red';
  109. tooltipTextColor = 'white';
  110. }
  111. $outputElemContainer.css('background-color', bgColor);
  112. $outputElem.css('color', textColor).html(editCount);
  113. var urCountText = '<div style="margin-top:8px;padding:3px;">UR\'s&nbsp;Closed:&nbsp;' + urCount.count + '&nbsp;&nbsp;(since&nbsp;' + (new Date(urCount.since)).toLocaleDateString() + ')</div>';
  114. var mpCountText = '<div style="margin-top:0px;padding:0px 3px;">MP\'s&nbsp;Closed:&nbsp;' + mpCount.count + '&nbsp;&nbsp;(since&nbsp;' + (new Date(mpCount.since)).toLocaleDateString() + ')</div>';
  115. var warningText = (savesWithoutIncrease > 0) ? '<div style="border-radius:8px;padding:3px;margin-top:8px;margin-bottom:5px;color:' + tooltipTextColor + ';background-color:' + bgColor + ';">' + savesWithoutIncrease + ' consecutive saves without an increase. (Are you throttled?)</div>' : '';
  116. $outputElem.attr('data-original-title', tooltipText + urCountText + mpCountText + warningText);
  117. lastEditCount = editCount;
  118. lastURCount = urCount;
  119. }
  120.  
  121. function receiveMessage(event) {
  122. var msg;
  123. try {
  124. msg = JSON.parse(event.data);
  125. } catch (err) {
  126. // Do nothing
  127. }
  128.  
  129. if (msg && msg[0] === 'wecmUpdateUi') {
  130. var editCount = msg[1][0];
  131. var urCount = msg[1][1];
  132. var mpCount = msg[1][2];
  133. updateEditCount(editCount, urCount, mpCount);
  134. }
  135. }
  136.  
  137. function checkChangedObjectCount() {
  138. let objectEditCount = getChangedObjectCount();
  139. if (objectEditCount >= _toastrSettings.warnAtEditCount && !_toastrSettings.wasWarned) {
  140. toastr.remove();
  141. toastr.warning('You have edited at least ' + _toastrSettings.warnAtEditCount + ' objects. You should consider saving soon. ' +
  142. 'If you get an error while saving, you may need to undo some actions and try again.', 'Reminder from Edit Count Monitor:');
  143. _toastrSettings.wasWarned = true;
  144. //_toastrSettings.wasReminded = true;
  145. } else if (objectEditCount >= _toastrSettings.remindAtEditCount && !_toastrSettings.wasReminded) {
  146. toastr.remove();
  147. toastr.info('You have edited at least ' + _toastrSettings.remindAtEditCount + ' objects. You should consider saving soon.', 'Reminder from Edit Count Monitor:');
  148. _toastrSettings.wasReminded = true;
  149. } else if (objectEditCount < _toastrSettings.remindAtEditCount) {
  150. _toastrSettings.wasWarned = false;
  151. _toastrSettings.wasReminded = false;
  152. toastr.remove();
  153. }
  154. }
  155.  
  156. function errorHandler(callback) {
  157. try {
  158. callback();
  159. } catch (ex) {
  160. console.error('Waze Edit Count Monitor:', ex);
  161. }
  162. }
  163.  
  164. function init() {
  165. userName = W.loginManager.user.userName;
  166. // Listen for events from sandboxed code.
  167. window.addEventListener('message', receiveMessage);
  168. // Listen for Save events.
  169.  
  170. $('head').append(
  171. $('<link/>', {
  172. rel: 'stylesheet',
  173. type: 'text/css',
  174. href: 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css'
  175. }),
  176. $('<style type="text/css">#toast-container {position: absolute;} #toast-container > div {opacity: 0.95;} .toast-top-center {top: 30px;}</style>')
  177. );
  178. $.getScript('https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.js', function() {
  179. toastr.options = {
  180. target:'#map',
  181. timeOut: 9999999999,
  182. positionClass: 'toast-top-right',
  183. closeOnHover: false,
  184. closeDuration: 0,
  185. showDuration: 0,
  186. closeButton: true
  187. //preventDuplicates: true
  188. };
  189. W.model.actionManager.events.register('afterclearactions', null, () => errorHandler(checkEditCount));
  190. W.model.actionManager.events.register('afteraction', null, () => errorHandler(checkChangedObjectCount));
  191. W.model.actionManager.events.register('afterundoaction', null, () => errorHandler(checkChangedObjectCount));
  192.  
  193. // Update the edit count first time.
  194. checkEditCount();
  195. log('Initialized.',0);
  196. });
  197. }
  198.  
  199. function bootstrap() {
  200. if (W &&
  201. W.loginManager &&
  202. W.loginManager.events &&
  203. W.loginManager.events.register &&
  204. W.map &&
  205. W.loginManager.isLoggedIn()) {
  206. log('Initializing...', 0);
  207. init();
  208. } else {
  209. log('Bootstrap failed. Trying again...', 0);
  210. window.setTimeout(function () {
  211. bootstrap();
  212. }, 1000);
  213. }
  214. }
  215.  
  216. bootstrap();
  217. }
  218.  
  219.  
  220. // Code that is NOT injected into the page.
  221. // Note that jQuery may or may not be available, so don't rely on it in this part of the script.
  222. (function(){
  223. 'use strict';
  224.  
  225. function getEditorProfileFromSource(source) {
  226. var match = source.match(/gon.data=({.*?});gon.env=/i);
  227. return JSON.parse(match[1]);
  228. }
  229.  
  230. function getEditCountFromProfile(profile) {
  231. var editingActivity = profile.editingActivity;
  232. return editingActivity[editingActivity.length-1];
  233. }
  234.  
  235. function getEditCountByTypeFromProfile(profile, type) {
  236. let edits = profile.editsByType.find(edits => edits.key === type);
  237. return edits ? edits.value : -1;
  238. }
  239.  
  240. // Handle messages from the page.
  241. function receiveMessage(event) {
  242. var msg;
  243. try {
  244. msg = JSON.parse(event.data);
  245. }
  246. catch (err) {
  247. // Ignore errors
  248. }
  249.  
  250. if (msg && msg[0] === 'wecmGetCounts') {
  251. var userName = msg[1];
  252. GM_xmlhttpRequest({
  253. method: 'GET',
  254. url: 'https://www.waze.com/user/editor/' + userName,
  255. onload: function(res) {
  256. var profile = getEditorProfileFromSource(res.responseText);
  257. window.postMessage(JSON.stringify(['wecmUpdateUi',[getEditCountFromProfile(profile), getEditCountByTypeFromProfile(profile,'mapUpdateRequest'), getEditCountByTypeFromProfile(profile,'machineMapProblem')]]),'*');
  258. }
  259. });
  260. }
  261. }
  262.  
  263. var WECM_Injected_script = document.createElement('script');
  264. WECM_Injected_script.textContent = '' + WECM_Injected.toString() + ' \nWECM_Injected();';
  265. WECM_Injected_script.setAttribute('type', 'application/javascript');
  266. document.body.appendChild(WECM_Injected_script);
  267. window.addEventListener('message', receiveMessage);
  268.  
  269. // Listen for events coming from the page script.
  270. window.addEventListener('message', receiveMessage);
  271. })();