Waze Edit Count Monitor

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

当前为 2018-05-27 提交的版本,查看 最新版本

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