Waze Edit Count Monitor

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

目前為 2018-07-21 提交的版本,檢視 最新版本

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