WME Tab Preferences

Adjust the tabs in the Waze Map Editor to your liking by adjusting their size, hiding tabs or even renaming tabs completely.

目前为 2016-03-14 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name WME Tab Preferences
  3. // @namespace http://www.tomputtemans.com/
  4. // @description Adjust the tabs in the Waze Map Editor to your liking by adjusting their size, hiding tabs or even renaming tabs completely.
  5. // @include https://www.waze.com/*/editor/*
  6. // @include https://www.waze.com/editor/*
  7. // @include https://editor-beta.waze.com/*
  8. // @exclude https://www.waze.com/user/*editor/*
  9. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAA3CAYAAACo29JGAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wwCEzYBoD6dGgAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAACfUlEQVRo3u3aTUgUYRjA8b/bjKyziyyTH2VpKYoHDxLkaTFvRSJCeBHxpFBHCULoWgcpqL3VqZaQIIKULlKSBoqIGJjQQTE2T8YqbpCzrwuz63Zwxy5+pLTtzvY8txle5n1+PO/XDFP0c8tKU6DhoYBDcIITnOAE99/jtKMa2LaNUnGSts3Ozk5+VMTjQdN1jBIDvbj4wHZFh51QtpXCsrbyujo+nx/D5zte5Wzb3oOZponf70fTtLwAJZNJLMsiFosRj1vour5vBQ+cc0rF92CBQCBvYACaphEIBDBNczfXbXW8BSWVSgFgGEbeDkknNyfXP8clkwAUHzJhcx1Obk6uss8JTnCCy93x6+/FJgvvp1hVBhevXOPS6UKo3NoUI++WSDDHyMMQodBTJpbAmn/D6EIiq10feLbcWI8CUFdXd/KnJxZ4cusOr76BYZxCqQzGa2CkFIpaeh+/4GbzybuIRCIAlFdU/uPKeSs5X1UC2L9hAAmFsoGzLbQ0unJYWnz5MMemx7t7WRrk9vA4U2PPGQiWZpDf+Twxw1fLdbhJXt4LEZ5eB6CmvZsbF7zgr6eru50agPVpwg/u8mzSdbgKquvLMA19d63ciOIMzLXIKpsAuoFZdo7yUjcuKMBKuJ/+8AqgYzZeptmMsfhpmZgNtAww9qgLP25cUJhh9O2K8/pLbHmWj7MZGMD8ME9mXLvPBenta+NM7XUGh3poyNxt6Bli8Go15W199AZdfEKp6rzP606ARaJN4/yIVtHaGqSjKUhHlvvO+pzLduRwzslbgeAEJzjBCS6331CczdrtsZ+joCtXlE6n5Q8iwQlOcIITnOAEJzjBCe6I+AVAjNynsKm5WAAAAABJRU5ErkJggg==
  10. // @version 1.1
  11. // @grant none
  12. // ==/UserScript==
  13. (function() {
  14. var tabReopened = false, // have we reopened the tab from last time?
  15. timesRan = 0, // variable for sanity check
  16. tabsSecured = -1, // Up until which index have we fully rearranged the tabs?
  17. versions = ['0.1', '0.2', '1.0', '1.0.1', '1.0.2', '1.0.3', '1.0.4', '1.0.5', '1.1'],
  18. Storage = (function() {
  19. var hashes = (localStorage.tabprefs_hidden ? localStorage.tabprefs_hidden.split(',') : []),
  20. tabConfigs = (localStorage.tabprefs_configs ? JSON.parse(localStorage.tabprefs_configs) : {});
  21. return {
  22. setTabVisibility: function(hash, visible) {
  23. var hashIndex = hashes.indexOf(hash);
  24. if (hashIndex !== -1 && visible) {
  25. hashes.splice(hashIndex, 1);
  26. localStorage.tabprefs_hidden = hashes.join();
  27. } else if (hashIndex === -1 && !visible) {
  28. hashes.push(hash);
  29. localStorage.tabprefs_hidden = hashes.join();
  30. }
  31. },
  32. isTabVisible: function(hash) {
  33. return hashes.indexOf(hash) === -1;
  34. },
  35. setTabConfig: function(hash, config) {
  36. tabConfigs[hash] = config;
  37. localStorage.tabprefs_configs = JSON.stringify(tabConfigs);
  38. },
  39. removeTabConfig: function(hash) {
  40. delete tabConfigs[hash];
  41. localStorage.tabprefs_configs = JSON.stringify(tabConfigs);
  42. },
  43. getTabConfig: function(hash) {
  44. return tabConfigs[hash] || {};
  45. },
  46. exportConfig: function() {
  47. return JSON.stringify({
  48. reopenTab: localStorage.tabprefs_reopenTab,
  49. tabprefs_tabwidth: localStorage.tabprefs_tabwidth,
  50. tabprefs_tabheight: localStorage.tabprefs_tabheight,
  51. tabprefs_preserveOrder: localStorage.tabprefs_preserveOrder,
  52. tabprefs_hidden: localStorage.tabprefs_hidden,
  53. tabprefs_configs: localStorage.tabprefs_configs
  54. });
  55. },
  56. importConfig: function(toImport) {
  57. var config = JSON.parse(toImport);
  58. var iterator = config.entries();
  59. for (var entry of iterator) {
  60. if (entry[0].startsWith('tabprefs_')) {
  61. localStorage[entry[0]] = entry[1];
  62. }
  63. }
  64. }
  65. };
  66. })();
  67.  
  68. function init(e) {
  69. if (e && e.user == null) {
  70. return;
  71. }
  72. if (typeof I18n === 'undefined') {
  73. log('No internationalisation object found yet, snoozing');
  74. setTimeout(init, 300);
  75. return;
  76. }
  77. if (typeof Waze === 'undefined' ||
  78. typeof Waze.loginManager === 'undefined') {
  79. setTimeout(init, 100);
  80. return;
  81. }
  82. if (!Waze.loginManager.hasUser()) {
  83. Waze.loginManager.events.register("login", null, init);
  84. Waze.loginManager.events.register("loginStatus", null, init);
  85. return;
  86. }
  87.  
  88. var om_strings = {
  89. en: {
  90. prefs: {
  91. title: 'Tab Preferences',
  92. tab_width: 'Tab width',
  93. tab_height: 'Tab height',
  94. reset: 'reset',
  95. preserve_tab: 'Preserve opened tab over sessions',
  96. preserve_order: 'Preserve tab order and change names',
  97. remove_tab: 'Tab no longer available. Remove entry?',
  98. hide_tab: 'Change tab visibility',
  99. move_up_tab: 'Move up',
  100. move_down_tab: 'Move down',
  101. edit_tab: 'Edit tab',
  102. change_icon: 'Change icon',
  103. set_icon: 'Set icon',
  104. reset_tab: 'Reset tab',
  105. icon: 'Icon',
  106. icon_none: 'None set',
  107. close: 'Close'
  108. },
  109. update: {
  110. first_run: 'Thanks for using Tab Preferences!\nThe settings tab on the left contains additional options now.\nThis message will only appear one time.',
  111. message: 'New version installed! Changelog:',
  112. v0_1: '- Initial version with tab memory and order preservation',
  113. v0_2: '- Improvements to order preservation algorithm\n- Addition of version change messages',
  114. v1_0: '- Ability to hide a tab added\n- Ability to replace tab with symbol added\n- Ability to resize tabs added\n- Metadata icon added to userscript',
  115. v1_0_1: '- Fixed the script for Google Chrome',
  116. v1_0_2: '- Fixed tab size reset buttons in Google Chrome',
  117. v1_0_3: '- Tab styling is applied with higher specificity, but plays nicer with other scripts as well',
  118. v1_0_4: '- Tab alignment issues with renamed tabs in Google Chrome fixed',
  119. v1_0_5: '- Removed strict mode that was necessary for Google Chrome\n- Improved initalisation of script',
  120. v1_1: '- The list with tab names can now be dragged around\n- Quick tooltips for tabs with replaced names\n- Bug fix concerning the order of the last tabs with some missing tabs'
  121. }
  122. },
  123. nl: {
  124. prefs: {
  125. title: 'Tabvoorkeuren',
  126. tab_width: 'Tabbreedte',
  127. tab_height: 'Tabhoogte',
  128. reset: 'reset',
  129. preserve_tab: 'Geopende tab bijhouden tussen sessies',
  130. preserve_order: 'Volgorde van tabs bijhouden en namen veranderen',
  131. remove_tab: 'Tab niet langer beschikbaar. Verwijderen?',
  132. hide_tab: 'Tabzichtbaarheid veranderen',
  133. move_up_tab: 'Eerder zetten',
  134. move_down_tab: 'Verder zetten',
  135. edit_tab: 'Tab aanpassen',
  136. change_icon: 'Icoon veranderen',
  137. set_icon: 'Icoon instellen',
  138. reset_tab: 'Tab resetten',
  139. icon: 'Icoon',
  140. icon_none: 'Niet ingesteld',
  141. close: 'Sluiten'
  142. }
  143. }
  144. };
  145. om_strings['en_GB'] = om_strings.en;
  146. for(var i = 0; i < I18n.availableLocales.length; i++) {
  147. var locale = I18n.availableLocales[i];
  148. if (I18n.translations[locale]) {
  149. I18n.translations[locale].tabpreferences = om_strings[locale];
  150. }
  151. }
  152.  
  153. checkVersion();
  154.  
  155. // Always add scrollbar to sidepanel to keep the width constant
  156. document.getElementById('sidebar').style.overflowY = 'scroll';
  157.  
  158. initTabListener();
  159. initSettings();
  160. }
  161.  
  162. function initTabListener() {
  163. var tabs = document.querySelector('#user-tabs .nav-tabs');
  164. if (!tabs) {
  165. log('No tabs found yet, snoozing');
  166. setTimeout(initTabListener, 400);
  167. return;
  168. }
  169. log('Tabs found, enabling observers');
  170. var selectionObserver = new MutationObserver(function(mutationRecords) {
  171. mutationRecords.forEach(function(mutationRecord) {
  172. // Store last opened tab when one of the tabs receives focus
  173. if (localStorage.tabprefs_reopenTab && mutationRecord.target.className === 'active') {
  174. localStorage.tabprefs_reopenTab = mutationRecord.target.querySelector('a').hash;
  175. }
  176. });
  177. });
  178. for (var i = 0; i < tabs.children.length; i++) {
  179. selectionObserver.observe(tabs.children[i], { attributes: true, attributeFilter: ['class'] });
  180. if (tabs.children[i].title) {
  181. $(tabs.children[i]).tooltip();
  182. }
  183. if (!Storage.isTabVisible(tabs.children[i].querySelector('a').hash)) {
  184. tabs.children[i].style.display = 'none';
  185. }
  186. }
  187.  
  188. var tabObserver = new MutationObserver(function(mutationRecords) {
  189. if (!tabReopened) {
  190. reopenTab();
  191. }
  192. mutationRecords.forEach(function(mutationRecord) {
  193. // Reorder tabs when new ones get added
  194. if (mutationRecord.addedNodes.length > 0) {
  195. for (var i = 0; i < mutationRecord.addedNodes.length; i++) {
  196. var node = mutationRecord.addedNodes[i],
  197. anchor = node.querySelector('a'),
  198. hash = anchor.hash;
  199. // Also start observing here for changes to tab selection
  200. selectionObserver.observe(node, { attributes: true, attributeFilter: ['class'] });
  201. if (anchor.title) {
  202. $(anchor).tooltip();
  203. }
  204. // Tab visibility
  205. if (!Storage.isTabVisible(hash)) {
  206. node.style.display = 'none';
  207. }
  208. }
  209. renameTabs();
  210. reorderTabs();
  211. if (localStorage.tabprefs_tabwidth || localStorage.tabprefs_tabheight) {
  212. resizeTabs();
  213. }
  214. }
  215. });
  216. });
  217. tabObserver.observe(tabs, { childList: true });
  218. reopenTab();
  219. renameTabs();
  220. reorderTabs();
  221. if (localStorage.tabprefs_tabwidth || localStorage.tabprefs_tabheight) {
  222. resizeTabs();
  223. }
  224. }
  225.  
  226. function initSettings() {
  227. var prefsTab = document.querySelector('#sidepanel-prefs');
  228. if (!prefsTab) {
  229. log('No settings tab found yet, snoozing');
  230. setTimeout(initSettings, 400);
  231. return;
  232. }
  233. var section = prefsTab.querySelector('.side-panel-section'),
  234. heading = document.createElement('h4'),
  235. tabOrderPanel = document.createElement('ul'),
  236. formGroup = document.createElement('div');
  237. heading.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.title')));
  238. formGroup.className = 'form-group';
  239. formGroup.style.marginBottom = '15px';
  240. formGroup.appendChild(createSlider('tabWidth', I18n.t('tabpreferences.prefs.tab_width'), 'tabprefs_tabwidth', 15, resizeTabs));
  241. formGroup.appendChild(createSlider('tabHeight', I18n.t('tabpreferences.prefs.tab_height'), 'tabprefs_tabheight', 5, resizeTabs));
  242. formGroup.appendChild(createOption('reopenTab', I18n.t('tabpreferences.prefs.preserve_tab'), (localStorage.tabprefs_reopenTab ? true : false), function() {
  243. if (this.checked) {
  244. localStorage.tabprefs_reopenTab = document.querySelector('#user-tabs .nav-tabs li.active a').hash;
  245. } else {
  246. localStorage.removeItem('tabprefs_reopenTab');
  247. }
  248. }));
  249. formGroup.appendChild(createOption('preserveOrder', I18n.t('tabpreferences.prefs.preserve_order'), (localStorage.tabprefs_preserveOrder ? true : false), function() {
  250. if (this.checked) {
  251. var tabs = document.querySelectorAll('#user-tabs .nav-tabs li a'),
  252. hashes = [];
  253. for (var i = 0; i < tabs.length; i++) {
  254. hashes.push(tabs[i].hash);
  255. }
  256. localStorage.tabprefs_preserveOrder = hashes.join();
  257. refreshTabPanel();
  258. document.getElementById('tabPreferencesOrder').style.display = 'block';
  259. } else {
  260. localStorage.removeItem('tabprefs_preserveOrder');
  261. document.getElementById('tabPreferencesOrder').style.display = 'none';
  262. }
  263. }));
  264. tabOrderPanel.className = 'result-list';
  265. tabOrderPanel.id = 'tabPreferencesOrder';
  266. tabOrderPanel.style.display = (localStorage.tabprefs_preserveOrder ? '' : 'none');
  267. formGroup.appendChild(tabOrderPanel);
  268. $(tabOrderPanel).sortable({
  269. forcePlaceholderSize: true,
  270. placeholderClass: 'result'
  271. }).bind('sortupdate', function(e, ui) {
  272. var order = localStorage.tabprefs_preserveOrder.split(',');
  273. order.splice(ui.elementIndex, 0, order.splice(ui.oldElementIndex, 1)[0]);
  274. localStorage.tabprefs_preserveOrder = order.join();
  275. reorderTabs(true);
  276. refreshTabPanel();
  277. });
  278.  
  279. // Obsolete option, remove from localStorage
  280. if (localStorage.tabprefs_hidePermissions) {
  281. localStorage.removeItem('tabprefs_hidePermissions');
  282. }
  283.  
  284. section.appendChild(heading);
  285. section.appendChild(formGroup);
  286.  
  287. refreshTabPanel();
  288. }
  289.  
  290. // Add options to the preferences tab
  291. function createOption(name, description, checked, eventHandler) {
  292. var input = document.createElement('input');
  293. input.type = 'checkbox';
  294. input.name = name;
  295. input.id = name + '-on';
  296. input.checked = checked;
  297. input.addEventListener('click', eventHandler);
  298. var label = document.createElement('label');
  299. label.htmlFor = name + '-on';
  300. label.appendChild(document.createTextNode(description));
  301. var container = document.createElement('div');
  302. container.className = 'controls-container';
  303. container.appendChild(input);
  304. container.appendChild(label);
  305. return container;
  306. }
  307.  
  308. // Add sliders to the preferences tab
  309. function createSlider(name, description, storageKey, defaultValue, eventHandler) {
  310. var input = document.createElement('input');
  311. input.type = 'range';
  312. input.name = name;
  313. input.id = name + '-on';
  314. input.min = 0;
  315. input.max = 30;
  316. input.value = (localStorage[storageKey] ? localStorage[storageKey] : defaultValue);
  317. input.addEventListener('input', function() {
  318. localStorage[storageKey] = this.value;
  319. eventHandler();
  320. });
  321. input.style.verticalAlign = 'middle';
  322. var label = document.createElement('label');
  323. label.htmlFor = name + '-on';
  324. label.appendChild(document.createTextNode(description));
  325. label.style.marginRight = '4px';
  326. var reset = document.createElement('button');
  327. reset.className = 'btn-link';
  328. reset.style.paddingRight = '0';
  329. reset.addEventListener('click', function() {
  330. input.value = defaultValue;
  331. localStorage.removeItem(storageKey);
  332. eventHandler();
  333. });
  334. reset.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.reset')));
  335. var container = document.createElement('div');
  336. container.className = 'controls-container';
  337. container.appendChild(label);
  338. container.appendChild(input);
  339. container.appendChild(reset);
  340. return container;
  341. }
  342.  
  343. // Attempt to reopen the tab opened during the previous session
  344. function reopenTab() {
  345. if (!localStorage.tabprefs_reopenTab) {
  346. return;
  347. }
  348. var tab = document.querySelector('#user-tabs .nav-tabs li a[href$="'+localStorage.tabprefs_reopenTab+'"]');
  349. if (!tabReopened && tab) {
  350. var clickEvent = new MouseEvent('click', {
  351. bubbles: true,
  352. cancelable: true,
  353. view: window
  354. });
  355. tab.dispatchEvent(clickEvent);
  356. tabReopened = true;
  357. }
  358. }
  359.  
  360. // If necessary, start reordering the tags as they were saved
  361. function reorderTabs(force) {
  362. if (force) {
  363. tabsSecured = -1;
  364. }
  365. // Protection against possible infinite loop if certain scripts don't follow the standards
  366. if (timesRan > 1000) {
  367. return;
  368. }
  369. if (timesRan === 1000) {
  370. log('Sanity limit reached! Tab Preferences is most likely conflicting with another script. Backing off from now on.');
  371. // run one last time to increase counter
  372. }
  373. timesRan++;
  374. if (localStorage.tabprefs_preserveOrder) {
  375. var hashes = localStorage.tabprefs_preserveOrder.split(','),
  376. navTabs = document.querySelector('#user-tabs ul.nav-tabs'),
  377. navAnchors = navTabs.querySelectorAll('li a');
  378. // First we check whether we know all tabs we currently have
  379. for (var i = 0; i < navAnchors.length; i++) {
  380. if (hashes.indexOf(navAnchors[i].hash) === -1) {
  381. // Add unknown tags to the end of the list
  382. hashes.push(navAnchors[i].hash);
  383. }
  384. }
  385. localStorage.tabprefs_preserveOrder = hashes.join();
  386. refreshTabPanel();
  387. // Then we put them in the order we have stored
  388. var tabsMissing = 0;
  389. for (var i = tabsSecured+1; i < hashes.length; i++) {
  390. var tabAnchor = navTabs.querySelector('a[href$="'+hashes[i]+'"]');
  391. if (!tabAnchor) {
  392. tabsMissing++;
  393. continue;
  394. }
  395. var tabIndex = Array.prototype.indexOf.call(navTabs.children, tabAnchor.parentNode);
  396. if (tabIndex === i && tabsSecured === tabIndex-1) {
  397. tabsSecured++;
  398. }
  399. if (tabAnchor && tabIndex !== i - tabsMissing && i - tabsMissing < navTabs.children.length) {
  400. navTabs.insertBefore(tabAnchor.parentNode, navTabs.children[i - tabsMissing]);
  401. if (tabsSecured === i-1) {
  402. tabsSecured++;
  403. }
  404. }
  405. }
  406. }
  407. }
  408.  
  409. function resizeTabs() {
  410. var width = (localStorage.tabprefs_tabwidth ? localStorage.tabprefs_tabwidth : 15),
  411. height = (localStorage.tabprefs_tabheight ? localStorage.tabprefs_tabheight : 5),
  412. tabAnchors = document.querySelectorAll('#user-tabs .nav-tabs li a');
  413. for (var i = 0; i < tabAnchors.length; i++) {
  414. tabAnchors[i].style.cssText += ';padding:' + height + 'px ' + width + 'px !important';
  415. }
  416. }
  417.  
  418. function renameTabs(force) {
  419. var tabs = document.querySelectorAll('#user-tabs .nav-tabs li a');
  420. for (var i = 0; i < tabs.length; i++) {
  421. var anchor = tabs[i],
  422. config = Storage.getTabConfig(anchor.hash);
  423. if (config && config.icon && (!anchor.originalChildren || force)) {
  424. if (!anchor.originalChildren) {
  425. var children = [];
  426. for (var j = 0; j < anchor.childNodes.length; j++) { // Bleh, nodelist doesn't support for each
  427. children.push(anchor.childNodes[j]);
  428. }
  429. anchor.originalTitle = anchor.title || anchor.dataset.originalTitle || anchor.parentNode.title || anchor.parentNode.dataset.originalTitle;
  430. if (!anchor.title) {
  431. anchor.title = anchor.textContent.trim();
  432. }
  433. $(anchor).tooltip();
  434. anchor.originalChildren = children;
  435. }
  436. if (config.icon) {
  437. var span = document.createElement('span');
  438. switch (config.icon.fontFamily) {
  439. case 'FontAwesome':
  440. span.className = 'fa';
  441. break;
  442. default:
  443. log('Unsupported fontFamily found: ' + config.icon.fontFamily);
  444. }
  445. span.appendChild(document.createTextNode(String.fromCharCode(config.icon.charCode)));
  446. while (anchor.firstChild) {
  447. anchor.removeChild(anchor.firstChild);
  448. }
  449. anchor.appendChild(span);
  450. }
  451. }
  452. // Icon replacement has been removed
  453. if (!config.icon && anchor.originalChildren) {
  454. while (anchor.firstChild) {
  455. anchor.removeChild(anchor.firstChild);
  456. }
  457. anchor.originalChildren.forEach(function(node) {
  458. anchor.appendChild(node);
  459. });
  460. delete anchor.originalChildren;
  461. if (anchor.originalTitle) {
  462. anchor.title = anchor.originalTitle;
  463. $(anchor).tooltip();
  464. } else {
  465. anchor.removeAttribute('title');
  466. $(anchor).tooltip('destroy');
  467. delete anchor.dataset.originalTitle;
  468. }
  469. delete anchor.originalTitle;
  470. }
  471. }
  472. }
  473.  
  474. function refreshTabPanel() {
  475. var tabPanel = document.getElementById('tabPreferencesOrder'),
  476. eyeSlashIcon = String.fromCharCode(61552),
  477. eyeIcon = String.fromCharCode(61550);
  478. if (!localStorage.tabprefs_preserveOrder || !tabPanel) {
  479. return;
  480. }
  481. var order = localStorage.tabprefs_preserveOrder.split(',');
  482. while (tabPanel.firstChild) { // clear out tabPanel's children
  483. tabPanel.removeChild(tabPanel.firstChild);
  484. }
  485. order.forEach(function(hash, index, obj) {
  486. var item = document.createElement('li'),
  487. name = document.createElement('span'),
  488. buttons = document.createElement('div'),
  489. moveUp = createIconButton('', I18n.t('tabpreferences.prefs.move_up_tab')),
  490. moveDown = createIconButton('', I18n.t('tabpreferences.prefs.move_down_tab')),
  491. remove = createIconButton('', I18n.t('tabpreferences.prefs.remove_tab')),
  492. hide = createIconButton((Storage.isTabVisible(hash) ? eyeIcon : eyeSlashIcon), I18n.t('tabpreferences.prefs.hide_tab')),
  493. edit = createIconButton('', I18n.t('tabpreferences.prefs.edit_tab')),
  494. anchor = document.querySelector('#user-tabs .nav-tabs li a[href$="'+hash+'"]'),
  495. tabConfig = {};
  496. if (anchor) {
  497. tabConfig = Storage.getTabConfig(hash);
  498. }
  499. // Add action buttons
  500. if (!anchor) {
  501. remove.addEventListener('click', function() {
  502. obj.splice(index, 1);
  503. localStorage.tabprefs_preserveOrder = obj.join();
  504. refreshTabPanel();
  505. });
  506. buttons.appendChild(remove);
  507. } else {
  508. if (hash !== '#sidepanel-prefs') { // Prevent the preferences tab from being hidden and getting locked out
  509. hide.addEventListener('click', function() {
  510. if (Storage.isTabVisible(hash)) {
  511. this.innerHTML = eyeSlashIcon;
  512. anchor.parentNode.style.display = 'none';
  513. Storage.setTabVisibility(hash, false);
  514. } else {
  515. this.innerHTML = eyeIcon;
  516. anchor.parentNode.style.display = 'block';
  517. Storage.setTabVisibility(hash, true);
  518. }
  519. refreshTabPanel();
  520. });
  521. hide.addEventListener('mouseenter', function() {
  522. this.innerHTML = (Storage.isTabVisible(hash) ? eyeSlashIcon : eyeIcon);
  523. });
  524. hide.addEventListener('mouseleave', function() {
  525. this.innerHTML = (Storage.isTabVisible(hash) ? eyeIcon : eyeSlashIcon);
  526. });
  527. buttons.appendChild(hide);
  528. }
  529. edit.addEventListener('click', function() {
  530. if (!item.details) {
  531. createTabConfigPane(item, hash);
  532. } else {
  533. item.removeChild(item.details);
  534. item.details = false;
  535. }
  536. });
  537. buttons.appendChild(edit);
  538. }
  539. if (index === order.length - 1) {
  540. moveDown.style.cursor = 'default';
  541. moveDown.style.color = '#aaa';
  542. } else {
  543. moveDown.addEventListener('click', function() {
  544. obj.splice(index+1, 0, obj.splice(index, 1)[0]);
  545. localStorage.tabprefs_preserveOrder = obj.join();
  546. reorderTabs(true);
  547. refreshTabPanel();
  548. });
  549. }
  550. buttons.appendChild(moveDown);
  551. if (index === 0) {
  552. moveUp.style.color = '#aaa';
  553. moveUp.style.cursor = 'default';
  554. } else {
  555. moveUp.addEventListener('click', function() {
  556. obj.splice(index-1, 0, obj.splice(index, 1)[0]);
  557. localStorage.tabprefs_preserveOrder = obj.join();
  558. reorderTabs(true);
  559. refreshTabPanel();
  560. });
  561. }
  562. buttons.appendChild(moveUp);
  563. buttons.style.float = 'right';
  564. item.className = 'result';
  565. item.appendChild(buttons);
  566. var handle = document.createElement('span');
  567. handle.style.fontFamily = 'FontAwesome';
  568. handle.style.letterSpacing = '1px';
  569. handle.style.color = '#c2c2c2';
  570. handle.style.cursor = 'move';
  571. handle.style.fontSize = '11px';
  572. handle.appendChild(document.createTextNode(' '));
  573. item.appendChild(handle);
  574. name.style.cursor = 'default';
  575. // Add name and replacement
  576. if (anchor) {
  577. if (anchor.originalChildren) {
  578. var title = anchor.originalTitle;
  579. var arrow = document.createElement('span');
  580. arrow.style.fontFamily = 'FontAwesome';
  581. arrow.style.color = '#888';
  582. arrow.style.margin = '0 6px';
  583. arrow.appendChild(document.createTextNode(String.fromCharCode(61537)));
  584. anchor.originalChildren.forEach(function(node) {
  585. name.appendChild(node.cloneNode(true));
  586. });
  587. if (title) {
  588. name.appendChild(document.createTextNode(' (' + title + ')'));
  589. }
  590. name.appendChild(arrow);
  591. for (var i = 0; i < anchor.childNodes.length; i++) {
  592. name.appendChild(anchor.childNodes[i].cloneNode(true));
  593. }
  594. } else {
  595. name.innerHTML = anchor.innerHTML;
  596. var title = anchor.title || anchor.dataset.originalTitle || anchor.parentNode.title || anchor.parentNode.dataset.originalTitle;
  597. if (title) {
  598. name.appendChild(document.createTextNode(' (' + title + ')'));
  599. }
  600. }
  601. if (!Storage.isTabVisible(hash)) {
  602. name.style.color = '#888';
  603. }
  604. } else {
  605. name.style.color = '#888';
  606. name.style.fontStyle = 'italic';
  607. name.appendChild(document.createTextNode(hash));
  608. }
  609. item.appendChild(name);
  610. tabPanel.appendChild(item);
  611. });
  612. $(tabPanel).sortable();
  613. }
  614. function createIconButton(icon, title) {
  615. var button = document.createElement('button');
  616. button.style.fontFamily = 'FontAwesome';
  617. button.style.border = 'none';
  618. button.style.background = 'none';
  619. button.style.padding = '0 2px';
  620. button.style.cursor = 'pointer';
  621. button.style.height = 'auto';
  622. button.style.outline = 'none';
  623. button.appendChild(document.createTextNode(icon));
  624. if (title) {
  625. button.title = title;
  626. $(button).tooltip();
  627. }
  628. return button;
  629. }
  630.  
  631. function checkVersion() {
  632. var version = localStorage.tabprefs_version,
  633. scriptVersion = GM_info.script.version;
  634. if (!version) {
  635. showMessage(I18n.t('tabpreferences.update.first_run'));
  636. localStorage.tabprefs_version = scriptVersion;
  637. } else if (version !== scriptVersion) {
  638. if (versions.indexOf(version) === -1) {
  639. // There's tampering happening if we arrive here, just set to current version and ignore issue
  640. localStorage.tabprefs_version = scriptVersion;
  641. return;
  642. }
  643. var message = I18n.t('tabpreferences.update.message');
  644. for (var i = versions.indexOf(version)+1; i < versions.length; i++) {
  645. message += '\nv' + versions[i] + ':\n' + I18n.t('tabpreferences.update.v' + versions[i].replace(/\./g, '_'));
  646. }
  647. showMessage(message);
  648. localStorage.tabprefs_version = scriptVersion;
  649. }
  650. }
  651.  
  652. function createTabConfigPane(container, hash) {
  653. var tabConfig = Storage.getTabConfig(hash),
  654. details = document.createElement('div'),
  655. iconDiv = document.createElement('div'),
  656. iconBtn = document.createElement('button'),
  657. icon = document.createElement('span'),
  658. nameDiv = document.createElement('div'),
  659. nameBtn = document.createElement('button'),
  660. titleDiv = document.createElement('div'),
  661. titleBtn = document.createElement('button'),
  662. reset = document.createElement('button'),
  663. close = document.createElement('button');
  664. iconBtn.appendChild(document.createTextNode((tabConfig.icon ? I18n.t('tabpreferences.prefs.change_icon') : I18n.t('tabpreferences.prefs.set_icon'))));
  665. iconBtn.style.margin = '0 10px';
  666. iconBtn.className = 'btn-link';
  667. iconBtn.addEventListener('click', function() {
  668. if (!iconBtn.icons) {
  669. var icons = document.createElement('div');
  670. icons.style.height = '10em';
  671. icons.style.overflow = 'auto';
  672. icons.style.letterSpacing = '5px';
  673. icons.style.fontSize = '12px';
  674. icons.style.wordWrap = 'break-word';
  675. // FontAwesome: symbols start at 'F000' (= 61440) and end at 'F295' in version 4.5 of FontAwesome (Waze uses 4.4)
  676. for (var i = 61440; i <= 62101; i++) {
  677. var icon = document.createElement('span');
  678. icon.style.fontFamily = 'FontAwesome';
  679. icon.appendChild(document.createTextNode(String.fromCharCode(i)));
  680. icon.style.cursor = 'pointer';
  681. icon.dataset.charCode = i;
  682. icon.addEventListener('click', function() {
  683. tabConfig.icon = {
  684. fontFamily: 'FontAwesome',
  685. charCode: this.dataset.charCode
  686. };
  687. Storage.setTabConfig(hash, tabConfig);
  688. container.removeChild(details);
  689. renameTabs(true);
  690. refreshTabPanel();
  691. });
  692. icons.appendChild(icon);
  693. }
  694. details.insertBefore(icons, reset);
  695. iconBtn.icons = icons;
  696. } else {
  697. details.removeChild(iconBtn.icons);
  698. iconBtn.icons = false;
  699. }
  700. });
  701. iconDiv.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.icon') + ': '));
  702. if (tabConfig.icon) {
  703. icon.style.fontFamily = tabConfig.icon.fontFamily;
  704. icon.appendChild(document.createTextNode(String.fromCharCode(tabConfig.icon.charCode)));
  705. } else {
  706. icon.style.fontStyle = 'italic';
  707. icon.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.icon_none')));
  708. }
  709. iconDiv.appendChild(icon);
  710. iconDiv.appendChild(document.createElement('br'));
  711. iconDiv.appendChild(iconBtn);
  712. details.appendChild(iconDiv);
  713. reset.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.reset_tab')));
  714. reset.style.margin = '0 5px';
  715. reset.className = 'btn btn-danger';
  716. reset.addEventListener('click', function() {
  717. Storage.removeTabConfig(hash);
  718. container.removeChild(details);
  719. container.details = false;
  720. renameTabs();
  721. refreshTabPanel();
  722. });
  723. details.appendChild(reset);
  724. close.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.close')));
  725. close.style.margin = '0 5px';
  726. close.className = 'btn btn-default';
  727. close.addEventListener('click', function() {
  728. container.removeChild(details);
  729. container.details = false;
  730. });
  731. details.appendChild(close);
  732. container.details = details;
  733. container.appendChild(details);
  734. }
  735.  
  736. function showMessage(message) {
  737. alert('Tab Preferences\n=============\n' + message);
  738. }
  739.  
  740. function log(message) {
  741. if (console.log) {
  742. console.log('%cWME Tab Preferences: %c' + message, 'color:black', 'color:#d97e00');
  743. }
  744. }
  745.  
  746. log('version - ' + GM_info.script.version);
  747. init();
  748. })();