WME Bookmarklets

Put bookmarklets in a tab and provide a better code execution environment

目前为 2021-02-18 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name WME Bookmarklets
  3. // @author Tom 'Glodenox' Puttemans
  4. // @namespace http://www.tomputtemans.com/
  5. // @description Put bookmarklets in a tab and provide a better code execution environment
  6. // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
  7. // @version 1.0.7
  8. // @grant none
  9. // ==/UserScript==
  10. (function() {
  11. var bookmarklets = [];
  12. if (localStorage.WME_Bookmarklets) {
  13. importBookmarklets(JSON.parse(localStorage.WME_Bookmarklets).bookmarklets);
  14. }
  15.  
  16. function init(e) {
  17. if (e && e.user == null) {
  18. return;
  19. }
  20. if (document.getElementById('user-info') == null) {
  21. setTimeout(init, 500);
  22. log('user-info element not yet available, page still loading');
  23. return;
  24. }
  25. if (typeof W.loginManager === 'undefined') {
  26. setTimeout(init, 300);
  27. return;
  28. }
  29. if (!W.loginManager.user) {
  30. W.loginManager.events.register('login', null, init);
  31. W.loginManager.events.register('loginStatus', null, init);
  32. // Double check as event might have triggered already
  33. if (!W.loginManager.user) {
  34. return;
  35. }
  36. }
  37.  
  38. var bookmarklets_strings = {
  39. en: {
  40. tab_title: 'Bookmarklets',
  41. add_bookmarklet: 'Add bookmarklet',
  42. empty_list: 'No bookmarklets added yet',
  43. bookmarklet_name: 'Name',
  44. bookmarklet_script: 'Script',
  45. bookmarklet_error: 'Bookmarklet threw an error',
  46. message: 'Activate a bookmarklet or add a new one:',
  47. bookmarklet_sources: 'Many Wazers have created bookmarklets to perform small tasks within the WME. Most of these can be found on <a href="https://wiki.waze.com/wiki/Bookmarklets" target="_blank">the wiki</a> or by <a href="https://www.waze.com/forum/search.php?keywords=Bookmarklet&terms=all&sv=0&sc=1&sf=all&sr=posts&sk=t&sd=d&st=0&ch=300&t=0&submit=Search" target="_blank">seaching the forums</a>.',
  48. bookmarklet_remove: 'Remove bookmarklet'
  49. }
  50. };
  51. setTranslations(bookmarklets_strings);
  52.  
  53. var tab = addTab();
  54. var message = document.createElement('p');
  55. message.appendChild(document.createTextNode(I18n.t('bookmarklets.message')));
  56. tab.appendChild(message);
  57.  
  58. var emptyList = document.createElement('span');
  59. emptyList.id = 'emptyBookmarklets';
  60. emptyList.style.fontStyle = 'italic';
  61. emptyList.style.display = (bookmarklets.length == 0 ? 'block' : 'none');
  62. emptyList.appendChild(document.createTextNode(I18n.t('bookmarklets.empty_list')));
  63. tab.appendChild(emptyList);
  64.  
  65. var bookmarkletList = document.createElement('div');
  66. bookmarkletList.className = 'result-list';
  67. bookmarkletList.style.marginBottom = '1em';
  68. bookmarklets.forEach(function(bookmarklet) {
  69. addBookmarklet(bookmarklet);
  70. });
  71. tab.appendChild(bookmarkletList);
  72.  
  73. var addBookmarkletForm = document.createElement('form');
  74. var addBookmarkletTitle = document.createElement('h4');
  75. addBookmarkletTitle.appendChild(document.createTextNode(I18n.t('bookmarklets.add_bookmarklet')));
  76. addBookmarkletForm.appendChild(addBookmarkletTitle);
  77. addBookmarkletForm.style.display = 'none';
  78. var nameGroup = document.createElement('div');
  79. nameGroup.className = 'form-group';
  80. var nameFieldLabel = document.createElement('label');
  81. nameFieldLabel.htmlFor = 'name';
  82. nameFieldLabel.appendChild(document.createTextNode(I18n.t('bookmarklets.bookmarklet_name')));
  83. nameGroup.appendChild(nameFieldLabel);
  84. var nameField = document.createElement('input');
  85. nameField.type = 'text';
  86. nameField.autocomplete = 'off';
  87. nameField.style.width = '100%';
  88. nameField.name = 'name';
  89. nameField.className = 'from-control';
  90. nameGroup.appendChild(nameField);
  91. addBookmarkletForm.appendChild(nameGroup);
  92. var scriptGroup = document.createElement('div');
  93. scriptGroup.className = 'form-group';
  94. var scriptFieldLabel = document.createElement('label');
  95. scriptFieldLabel.htmlFor = 'script';
  96. scriptFieldLabel.appendChild(document.createTextNode(I18n.t('bookmarklets.bookmarklet_script')));
  97. scriptGroup.appendChild(scriptFieldLabel);
  98. var scriptField = document.createElement('textarea');
  99. scriptField.name = 'script';
  100. scriptField.className = 'from-control';
  101. scriptField.style.width = '100%';
  102. scriptField.style.height = 'auto';
  103. scriptGroup.appendChild(scriptField);
  104. addBookmarkletForm.appendChild(scriptGroup);
  105. var createBookmarklet = document.createElement('input');
  106. createBookmarklet.type = 'submit';
  107. createBookmarklet.className = 'btn btn-default';
  108. createBookmarklet.value = I18n.t('bookmarklets.add_bookmarklet');
  109. addBookmarkletForm.appendChild(createBookmarklet);
  110.  
  111. var addBookmarkletButton = document.createElement('button');
  112. addBookmarkletButton.className = 'btn btn-default';
  113. var addSpan = document.createElement('span');
  114. addSpan.appendChild(document.createTextNode(''));
  115. addSpan.className = 'fa';
  116. addSpan.style.marginRight = '5px';
  117. addBookmarkletButton.appendChild(addSpan);
  118. addBookmarkletButton.appendChild(document.createTextNode(I18n.t('bookmarklets.add_bookmarklet')));
  119. addBookmarkletButton.addEventListener('click', function() {
  120. addBookmarkletButton.style.display = 'none';
  121. addBookmarkletForm.style.display = 'block';
  122. });
  123. tab.appendChild(addBookmarkletButton);
  124. tab.appendChild(addBookmarkletForm);
  125. addBookmarkletForm.addEventListener('submit', function(e) {
  126. e.preventDefault();
  127. addBookmarkletForm.style.display = 'none';
  128. addBookmarkletButton.style.display = 'block';
  129. var newBookmarklet = {};
  130. newBookmarklet.name = nameField.value;
  131. newBookmarklet.script = scriptField.value;
  132. addBookmarklet(newBookmarklet);
  133. bookmarklets.push(newBookmarklet);
  134. saveBookmarklets();
  135. return false;
  136. }, true);
  137.  
  138. var bookmarkletSources = document.createElement('p');
  139. bookmarkletSources.innerHTML = I18n.t('bookmarklets.bookmarklet_sources');
  140. bookmarkletSources.style.marginTop = '1em';
  141. tab.appendChild(bookmarkletSources);
  142. var versionInfo = document.createElement('a');
  143. versionInfo.appendChild(document.createTextNode(GM_info.script.name + ' (v' + GM_info.script.version + ')'));
  144. versionInfo.href = 'https://greasyfork.org/nl/scripts/20379-wme-bookmarklets/';
  145. versionInfo.target = '_blank';
  146. tab.appendChild(versionInfo);
  147.  
  148. // Create a tab and possibly receive a previous tab to restore (usually in case of a mode change)
  149. function addTab(recoveredTab) {
  150. var userInfo = document.getElementById('user-info'),
  151. tabHandles = userInfo.querySelector('.nav-tabs'),
  152. tabs = userInfo.querySelector('.tab-content'),
  153. tabHandle = document.createElement('li'),
  154. tab = document.createElement('div');
  155. tabHandle.innerHTML = '<a href="#sidepanel-bookmarklets" data-toggle="tab" title="' + I18n.t('bookmarklets.tab_title') + '"><span class="fa"></span></a>';
  156. if (recoveredTab) {
  157. tab = recoveredTab;
  158. } else {
  159. tab.id = 'sidepanel-bookmarklets';
  160. tab.className = 'tab-pane';
  161. }
  162. tabHandles.appendChild(tabHandle);
  163. $(tabHandle.childNodes[0]).tooltip();
  164. tabs.appendChild(tab);
  165. return tab;
  166. }
  167.  
  168. function addBookmarklet(bookmarklet) {
  169. document.getElementById('emptyBookmarklets').style.display = 'none';
  170. var bookmarkletContainer = document.createElement('div');
  171. bookmarkletContainer.className = 'result session-available';
  172. var bookmarkletRemove = document.createElement('button');
  173. bookmarkletRemove.style.position = 'absolute';
  174. bookmarkletRemove.style.display = 'none';
  175. bookmarkletRemove.style.fontSize = '14px';
  176. bookmarkletRemove.style.top = '4px';
  177. bookmarkletRemove.style.right = '4px'
  178. bookmarkletRemove.className = 'btn btn-default fa';
  179. bookmarkletRemove.addEventListener('click', function(e) {
  180. e.stopPropagation();
  181. removeBookmarklet(bookmarklet);
  182. });
  183. bookmarkletRemove.appendChild(document.createTextNode(''));
  184. bookmarkletRemove.title = I18n.t('bookmarklets.bookmarklet_remove');
  185. $(bookmarkletRemove).tooltip();
  186. bookmarkletContainer.appendChild(bookmarkletRemove);
  187. bookmarkletContainer.addEventListener('mouseenter', function() {
  188. bookmarkletRemove.style.display = 'block';
  189. });
  190. bookmarkletContainer.addEventListener('mouseleave', function() {
  191. bookmarkletRemove.style.display = 'none';
  192. });
  193. var bookmarkletName = document.createElement('div');
  194. bookmarkletName.appendChild(document.createTextNode(bookmarklet.name));
  195. bookmarkletContainer.appendChild(bookmarkletName);
  196. var bookmarkletError = document.createElement('div');
  197. bookmarkletError.style.display = 'none';
  198. bookmarkletError.style.backgroundColor = 'red';
  199. bookmarkletError.style.color = '#f2dede';
  200. bookmarkletError.style.border = '2px solid #f2dede';
  201. bookmarkletError.style.padding = '4px';
  202. bookmarkletError.style.margin = '4px 0 4px 0';
  203. bookmarkletContainer.appendChild(bookmarkletError);
  204. var bookmarkletErrorClose = document.createElement('button');
  205. bookmarkletErrorClose.style.fontFamily = 'FontAwesome';
  206. bookmarkletErrorClose.style.color = '#f2dede';
  207. bookmarkletErrorClose.style.border = 'none';
  208. bookmarkletErrorClose.style.background = 'none';
  209. bookmarkletErrorClose.style.padding = '3px';
  210. bookmarkletErrorClose.style.float = 'right';
  211. bookmarkletErrorClose.style.cursor = 'pointer';
  212. bookmarkletErrorClose.style.height = 'auto';
  213. bookmarkletErrorClose.style.outline = 'none';
  214. bookmarkletErrorClose.appendChild(document.createTextNode(''));
  215. bookmarkletError.addEventListener('click', function(e) {
  216. e.stopPropagation();
  217. bookmarkletError.style.display = 'none';
  218. }, true);
  219. bookmarkletError.appendChild(bookmarkletErrorClose);
  220. var bookmarkletErrorTitle = document.createElement('strong');
  221. bookmarkletErrorTitle.appendChild(document.createTextNode(I18n.t('bookmarklets.bookmarklet_error')));
  222. bookmarkletError.appendChild(bookmarkletErrorTitle);
  223. bookmarkletError.appendChild(document.createElement('br'));
  224. bookmarkletContainer.addEventListener('click', function() {
  225. try {
  226. var cleanedUpScript = decodeURI(bookmarklet.script);
  227. cleanedUpScript = cleanedUpScript.replace('javascript:', '');
  228. eval(cleanedUpScript);
  229. } catch (e) {
  230. log(e);
  231. if (bookmarkletError.childNodes[3]) { // Remove previous error message
  232. bookmarkletError.removeChild(bookmarkletError.childNodes[3]);
  233. }
  234. bookmarkletError.appendChild(document.createTextNode((e && e.message ? e.message : e)));
  235. bookmarkletError.style.display = 'block';
  236. }
  237. });
  238. bookmarkletList.appendChild(bookmarkletContainer);
  239. bookmarklet.container = bookmarkletContainer;
  240. }
  241.  
  242. function removeBookmarklet(bookmarklet) {
  243. var idx = bookmarklets.indexOf(bookmarklet);
  244. if (idx > -1) {
  245. bookmarklets.splice(idx, 1);
  246. }
  247. saveBookmarklets();
  248. bookmarkletList.removeChild(bookmarklet.container);
  249. if (bookmarklets.length == 0) {
  250. document.getElementById('emptyBookmarklets').style.display = 'block';
  251. }
  252. }
  253. }
  254.  
  255. function setTranslations(translations) {
  256. I18n.translations[I18n.currentLocale()].bookmarklets = translations.en;
  257. for (var i = 0; i < Object.keys(translations).length; i++) {
  258. var locale = Object.keys(translations)[i];
  259. if (I18n.currentLocale() == locale) {
  260. I18n.translations[locale].bookmarklets = translations[locale];
  261. return;
  262. }
  263. }
  264. }
  265.  
  266. function importBookmarklets(data) {
  267. data.forEach(function(bookmarklet) {
  268. bookmarklets.push(bookmarklet);
  269. });
  270. }
  271.  
  272. function saveBookmarklets() {
  273. var storage;
  274. if (typeof localStorage.WME_Bookmarklets == 'undefined') {
  275. storage = {};
  276. } else {
  277. storage = JSON.parse(localStorage.WME_Bookmarklets);
  278. }
  279. storage.bookmarklets = bookmarklets.map(function(bookmarklet) {
  280. return {
  281. 'name': bookmarklet.name,
  282. 'script': bookmarklet.script
  283. };
  284. });
  285. localStorage.WME_Bookmarklets = JSON.stringify(storage);
  286. }
  287.  
  288. function log(message) {
  289. if (typeof message === 'string') {
  290. console.log('%c' + GM_info.script.name + ' (v' + GM_info.script.version + '): %c' + message, 'color:black', 'color:#d97e00');
  291. } else {
  292. console.log('%c' + GM_info.script.name + ' (v' + GM_info.script.version + ')', 'color:black', message);
  293. }
  294. }
  295.  
  296. init();
  297. })();