WME Tab Manager

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

// ==UserScript==
// @name        WME Tab Manager
// @namespace   http://www.tomputtemans.com/
// @description Adjust the tabs in the Waze Map Editor to your liking by adjusting their size, hiding tabs or even renaming tabs completely.
// @include     /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor.*$/
// @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==
// @version     1.4.5
// @require     https://bowercdn.net/c/html.sortable-0.4.4/dist/html.sortable.js
// @grant       none
// ==/UserScript==

/* global I18n, _, W, OL, $ */

(function() {
  var tabReopened = false, // have we reopened the tab from last time?
      timesRan = 0, // variable for sanity check
      tabsSecured = -1, // Up until which index have we fully rearranged the tabs?
      versions = ['0.1', '0.2', '1.0', '1.0.1', '1.0.2', '1.0.3', '1.0.4', '1.0.5', '1.1', '1.1.1', '1.1.2', '1.2', '1.2.1', '1.2.2', '1.2.3', '1.2.4', '1.3', '1.3.1', '1.4.0', '1.4.1', '1.4.2', '1.4.3', '1.4.4', '1.4.5'],
      styleElement, // Style element to reuse whenever it gets removed by the WME (user login, for example)
      Storage = (function() {
        var hashes = (localStorage.tabprefs_hidden ? localStorage.tabprefs_hidden.split(',') : []),
            tabConfigs = (localStorage.tabprefs_configs ? JSON.parse(localStorage.tabprefs_configs) : {});
        return {
          setTabVisibility: function(hash, visible) {
            var hashIndex = hashes.indexOf(hash);
            if (hashIndex !== -1 && visible) {
              hashes.splice(hashIndex, 1);
              localStorage.tabprefs_hidden = hashes.join();
            } else if (hashIndex === -1 && !visible) {
              hashes.push(hash);
              localStorage.tabprefs_hidden = hashes.join();
            }
          },
          isTabVisible: function(hash) {
            return hashes.indexOf(hash) === -1;
          },
          setTabConfig: function(hash, config) {
            tabConfigs[hash] = config;
            localStorage.tabprefs_configs = JSON.stringify(tabConfigs);
          },
          removeTabConfig: function(hash) {
            delete tabConfigs[hash];
            localStorage.tabprefs_configs = JSON.stringify(tabConfigs);
          },
          getTabConfig: function(hash) {
            return tabConfigs[hash] || {};
          },
          exportConfig: function() {
            return JSON.stringify({
              reopenTab: localStorage.tabprefs_reopenTab,
              tabwidth: localStorage.tabprefs_tabwidth,
              tabheight: localStorage.tabprefs_tabheight,
              preserveOrder: localStorage.tabprefs_preserveOrder,
              hidden: localStorage.tabprefs_hidden,
              configs: localStorage.tabprefs_configs
            });
          },
          importConfig: function(toImport) {
            var result = JSON.parse(toImport);
            ['configs', 'hidden', 'preserveOrder', 'reopenTab', 'tabheight', 'tabwidth'].forEach(function(key) {
              if (result[key]) {
                localStorage.setItem('tabprefs_' + key, result[key]);
              }
            });
          }
        };
      })(),
      iconKeywords = {
        fontAwesome: {"500px":[62062],address:[62137,62138,62139,62140],book:[62137,62138,61485],card:[62139,62140,61597,62083,62146,62147],adjust:[61506],adn:[61808],align:[61495,61497,61494,61496],center:[61495],justify:[61497],left:[61494,61696,61700,61608,61840,61536,61657,61841,61751,61523,61605,61815,61709,61666,61841],right:[61496,61697,61701,61838,61609,61537,61658,61778,61752,61524,61604,61816,61710,61470,61778],amazon:[62064],ambulance:[61689],american:[62115],sign:[62115,61584,62119,61579],language:[62115,61867,62119],interpreting:[62115,62115],anchor:[61757],android:[61819],angellist:[61961],angle:[61699,61696,61697,61698,61703,61700,61701,61702],double:[61699,61696,61697,61698,61991,61990],down:[61699,61703,61611,61466,61539,61655,61776,61976,61754,61560,61607,61769,61813,61661,61797,61576,61776,61479],up:[61698,61702,61467,61610,61538,61777,61656,61753,61559,61606,61768,61814,61662,61575,61796,61777,61480],apple:[61817],archive:[61831,61894],area:[61950],chart:[61950,61568,61568,61953,61952],arrow:[61611,61608,61466,61840,61838,61467,61609,61610,61539,61536,61537,61538,61976,61732,61813,61815,61816,61814],circle:[61611,61608,61466,61840,61838,61467,61609,61610,61528,61533,61754,61751,61752,61753,61713,61708,61902,61915,61842,61546,62049,62131,61530,61526,62091,62092,61764,61469,61525,61529,62108,62093,62094,61859,61527,61532,62141,62142],arrows:[61511,61618,61566,61565],alt:[61618,62083,61715,61474,61864,61920,61921,61989,61758,62107],asl:[62115],assistive:[62114],listening:[62114],systems:[62114],asterisk:[61545],at:[61946],audio:[62110,61895],description:[62110],automobile:[61881],backward:[61514,61513,61512],balance:[62030],scale:[62030],ban:[61534],bandcamp:[62165],bank:[61852],bar:[61568,61568],barcode:[61482],bars:[61641],bath:[62157],bathtub:[62157],battery:[62016,62020,62019,62018,62017,62016,62020,62016,62018,62019,62017],empty:[62020,61731,62155],full:[62016,61731,62151],half:[62018,62034,61577,61731,61731,61731,62153],quarter:[62019,62154],three:[62017,62152],quarters:[62017,62152],bed:[62006],beer:[61692],behance:[61876,61877],square:[61877,61810,61776,61841,61778,61777,61770,61510,61849,61772,61570,61906,61586,61652,61693,61955,61580,61766,61767,62052,61771,61508,61592,61651,61694,61846,61858,61763,61921,61773,61509,62125,61640,61590,61879,61812,61569,62122,61844,61801,61908,61908,61798],bell:[61683,61602,61942,61943],slash:[61942,61943,61552,61745],bicycle:[61958],binoculars:[61925],birthday:[61949],cake:[61949],bitbucket:[61809,61810],bitcoin:[61786],black:[62078],tie:[62078],blind:[62109],bluetooth:[62099,62100],bold:[61490],bolt:[61671],bomb:[61922],bookmark:[61486,61591],braille:[62113],briefcase:[61617],btc:[61786],bug:[61832],building:[61869,61687],bullhorn:[61601],bullseye:[61760],bus:[61959],buysellads:[61965],cab:[61882],calculator:[61932],calendar:[61555,62068,62066,61747,62065,62067],check:[62068,61452,61528,61533,61770,61510],minus:[62066,61544,61526,61766,61767,61456],plus:[62065,61975,61653,62131,62131,61652,61543,61525,61694,61846,61454,62004],times:[62067,61453,61527,61532,62163,62164,62005],camera:[61488,61571,61501],retro:[61571],car:[61881],caret:[61655,61657,61658,61776,61841,61778,61777,61656],cart:[61976,61975,61562],cc:[61962,61939,62028,61938,62027,61937,61940,61941,61936],amex:[61939],diners:[62028],club:[62028],discover:[61938],jcb:[62027],mastercard:[61937],paypal:[61940,61933],stripe:[61941],visa:[61936],certificate:[61603],chain:[61633,61735],broken:[61735],chevron:[61754,61751,61752,61753,61560,61523,61524,61559],child:[61870],chrome:[62056],notch:[61902],thin:[61915],clipboard:[61674],clock:[61463],clone:[62029],close:[61453,62163,62164],cloud:[61634,61677,61678],download:[61677,61465],upload:[61678,61587],cny:[61783],code:[61729,61734,61897,62149],fork:[61734],codepen:[61899],codiepie:[62084],coffee:[61684],cog:[61459],cogs:[61573],columns:[61659],comment:[61557,61669],commenting:[62074,62075],comments:[61574,61670],compass:[61774],compress:[61542],connectdevelop:[61966],contao:[62061],copy:[61637],copyright:[61945],creative:[62046],commons:[62046],credit:[61597,62083],crop:[61733],crosshairs:[61531],"css3":[61756],cube:[61874],cubes:[61875],cut:[61636],cutlery:[61685],dashboard:[61668],dashcube:[61968],database:[61888],deaf:[62116],deafness:[62116],dedent:[61499],delicious:[61861],desktop:[61704],deviantart:[61885],diamond:[61977],digg:[61862],dollar:[61781],dot:[61842],dribbble:[61821],drivers:[62146,62147],license:[62146,62147],dropbox:[61803],drupal:[61865],edge:[62082],edit:[61508],eercast:[62170],eject:[61522],ellipsis:[61761,61762],empire:[61905],envelope:[61664,61443,62134,62135,61849],open:[62134,62135,61564,61717],envira:[62105],eraser:[61741],etsy:[62167],eur:[61779],euro:[61779],exchange:[61676,61837],exclamation:[61738,61546,61553],triangle:[61553],expand:[61541],expeditedssl:[62014],external:[61582,61772],link:[61582,61772,61633],eye:[61550,61552],eyedropper:[61947],fa:[62132],facebook:[61594,61594,62000,61570],official:[62000,62131],fast:[61513,61520],forward:[61520,61518,61540,61521],fax:[61868],feed:[61598],female:[61826],fighter:[61691],jet:[61691],file:[61787,61894,61895,61897,61891,61893,61896,61462,61889,61893,61893,61892,61895,61788,61686,61896,61890,61894],excel:[61891],image:[61893,61502],movie:[61896],pdf:[61889],photo:[61893,61502],picture:[61893,61502],powerpoint:[61892],sound:[61895],text:[61788,61686,61492,61493],video:[61896,61501],word:[61890],zip:[61894],files:[61637],film:[61448],filter:[61616],fire:[61549,61748],extinguisher:[61748],firefox:[62057],first:[62128],order:[62128],flag:[61476,61726,61725],checkered:[61726],flash:[61671],flask:[61635],flickr:[61806],floppy:[61639],folder:[61563,61716,61564,61717],font:[61489,62132],awesome:[62132,62086],fonticons:[62080],fort:[62086],forumbee:[61969],foursquare:[61824],free:[62149],camp:[62149],frown:[61721],futbol:[61923],gamepad:[61723],gavel:[61667],gbp:[61780],ge:[61905],gear:[61459],gears:[61573],genderless:[61997],get:[62053],pocket:[62053],gg:[62048,62049],gift:[61547],git:[61907,61906],github:[61595,61715,61586],gitlab:[62102],gittip:[61828],glass:[61440],glide:[62117,62118],globe:[61612],google:[61856,61653,62131,62131,61652,61934],wallet:[61934],graduation:[61853],cap:[61853],gratipay:[61828],grav:[62166],group:[61632,62023],hacker:[61908],news:[61908],hand:[62037,62040,61607,61605,61604,61606,62038,62043,62042,62037,62039,62041,62038],grab:[62037],lizard:[62040],paper:[62038,61912,61913],peace:[62043],pointer:[62042,62021],rock:[62037],scissors:[62039,61636],spock:[62041],stop:[62038,61517,62093,62094],handshake:[62133],hard:[62116],of:[62116],hearing:[62116],hashtag:[62098],hdd:[61600],header:[61916],headphones:[61477],heart:[61444,61578],heartbeat:[61982],history:[61914],home:[61461],hospital:[61688],hotel:[62006],hourglass:[62036,62033,62034,62035,62035,62034,62032,62033],end:[62035],start:[62033],houzz:[62076],"html5":[61755],cursor:[62022],id:[62145,62146,62147],badge:[62145],ils:[61963],imdb:[62168],inbox:[61468],indent:[61500],industry:[62069],info:[61737,61530],inr:[61782],instagram:[61805],institution:[61852],internet:[62059],explorer:[62059],intersex:[61988],ioxhost:[61960],italic:[61491],joomla:[61866],jpy:[61783],jsfiddle:[61900],key:[61572],keyboard:[61724],krw:[61785],laptop:[61705],lastfm:[61954,61955],leaf:[61548],leanpub:[61970],legal:[61667],lemon:[61588],level:[61769,61768],life:[61901,61901,61901,61901],bouy:[61901],buoy:[61901],ring:[61901],saver:[61901],lightbulb:[61675],line:[61953],linkedin:[61665,61580],linode:[62136],linux:[61820],list:[61498,61474,61643,61642,61451],ol:[61643],ul:[61642],location:[61732],lock:[61475],long:[61813,61815,61816,61814],low:[62120],vision:[62120],magic:[61648],magnet:[61558],mail:[61540,61714,61730],reply:[61714,61730,61714,61730],all:[61730,61730],male:[61827],map:[62073,61505,62072,62070,62071],marker:[61505],pin:[62070],signs:[62071],mars:[61986,61991,61993,61995,61994,61992],stroke:[61993,61995,61994],maxcdn:[61750],meanpath:[61964],medium:[62010],medkit:[61690],meetup:[62176],meh:[61722],mercury:[61987],microchip:[62171],microphone:[61744,61745],mixcloud:[62089],mobile:[61707,61707],phone:[61707,61589,61592,62112],modx:[62085],money:[61654],moon:[61830],mortar:[61853],board:[61853],motorcycle:[61980],mouse:[62021],music:[61441],navicon:[61641],neuter:[61996],newspaper:[61930],object:[62023,62024],ungroup:[62024],odnoklassniki:[62051,62052],opencart:[62013],openid:[61851],opera:[62058],optin:[62012],monster:[62012],outdent:[61499],pagelines:[61836],paint:[61948],brush:[61948],plane:[61912,61913,61554],paperclip:[61638],paragraph:[61917],paste:[61674],pause:[61516,62091,62092],paw:[61872],pencil:[61504,61771,61508],percent:[62101],pie:[61952],pied:[62126,61864,61863],piper:[62126,61864,61863],pp:[61863],pinterest:[61650,62001,61651],play:[61515,61764,61469,61802],plug:[61926],podcast:[62158],power:[61457],off:[61457,61956,61478],print:[61487],product:[62088],hunt:[62088],puzzle:[61742],piece:[61742],qq:[61910],qrcode:[61481],question:[61736,61529,62108],quora:[62148],quote:[61709,61710],ra:[61904],random:[61556],ravelry:[62169],rebel:[61904],recycle:[61880],reddit:[61857,62081,61858],alien:[62081],refresh:[61473],registered:[62045],remove:[61453],renren:[61835],reorder:[61641],repeat:[61470],resistance:[61904],retweet:[61561],rmb:[61783],road:[61464],rocket:[61749],rotate:[61666,61470],rouble:[61784],rss:[61598,61763],rub:[61784],ruble:[61784],rupee:[61782],"s15":[62157],safari:[62055],save:[61639],scribd:[62090],search:[61442,61456,61454],sellsy:[61971],send:[61912,61913],server:[62003],share:[61540,61920,61921,61773,61509],shekel:[61963],sheqel:[61963],shield:[61746],ship:[61978],shirtsinbulk:[61972],shopping:[62096,62097,61562],bag:[62096],basket:[62097],shower:[62156],in:[61584],out:[61579],signal:[61458],signing:[62119],simplybuilt:[61973],sitemap:[61672],skyatlas:[61974],skype:[61822],slack:[61848],sliders:[61918],slideshare:[61927],smile:[61720],snapchat:[62123,62124,62125],ghost:[62124],snowflake:[62172],soccer:[61923],ball:[61923],sort:[61660,61789,61790,61792,61793,61662,61661,61661,61794,61795,61662],alpha:[61789,61790],asc:[61789,61792,61662,61794],desc:[61790,61793,61661,61795],amount:[61792,61793],numeric:[61794,61795],soundcloud:[61886],space:[61847],shuttle:[61847],spinner:[61712],spoon:[61873],spotify:[61884],stack:[61837,61804],overflow:[61804],star:[61445,61577,61731,61731,61731,61446],steam:[61878,61879],step:[61512,61521],stethoscope:[61681],sticky:[62025,62026],note:[62025,62026],street:[61981],view:[61981],strikethrough:[61644],stumbleupon:[61860,61859],subscript:[61740],subway:[62009],suitcase:[61682],sun:[61829],superpowers:[62173],superscript:[61739],support:[61901],table:[61646],tablet:[61706],tachometer:[61668],tag:[61483],tags:[61484],tasks:[61614],taxi:[61882],telegram:[62150],television:[62060],tencent:[61909],weibo:[61909,61834],terminal:[61728],height:[61492],width:[61493],th:[61450,61449,61451],large:[61449],themeisle:[62130],thermometer:[62151,62155,62154,62153,62152,62151,62155,62151,62153,62154,62152],thumb:[61581],tack:[61581],thumbs:[61797,61576,61575,61796],ticket:[61765],rectangle:[62163,62164],tint:[61507],toggle:[61776,61841,61956,61957,61778,61777],on:[61957],trademark:[62044],train:[62008],transgender:[61988,61989],trash:[61944,61460],tree:[61883],trello:[61825],tripadvisor:[62050],trophy:[61585],truck:[61649],try:[61845],tty:[61924],tumblr:[61811,61812],turkish:[61845],lira:[61845],tv:[62060],twitch:[61928],twitter:[61593,61569],umbrella:[61673],underline:[61645],undo:[61666],universal:[62106],access:[62106],university:[61852],unlink:[61735],unlock:[61596,61758],unsorted:[61660],usb:[62087],usd:[61781],user:[61447,62141,62142,61680,62144,62004,61979,62005],md:[61680],secret:[61979],users:[61632],vcard:[62139,62140],venus:[61985,61990,61992],viacoin:[62007],viadeo:[62121,62122],vimeo:[62077,61844],vine:[61898],vk:[61833],volume:[62112,61479,61478,61480],control:[62112],warning:[61553],wechat:[61911],weixin:[61911],whatsapp:[62002],wheelchair:[61843,62107],wifi:[61931],wikipedia:[62054],window:[62163,62164,62160,62161,62162],maximize:[62160],minimize:[62161],restore:[62162],windows:[61818],won:[61785],wordpress:[61850],wpbeginner:[62103],wpexplorer:[62174],wpforms:[62104],wrench:[61613],xing:[61800,61801],combinator:[62011,61908],yahoo:[61854],yc:[62011,61908],yelp:[61929],yen:[61783],yoast:[62129],youtube:[61799,61802,61798]}
      },
      iconFilterList = document.createElement('datalist');

  function init(e) {
    if (e && e.user == null) {
      return;
    }
    if (typeof I18n === 'undefined') {
      log('No internationalisation object found yet, snoozing');
      setTimeout(init, 300);
      return;
    }
    if (typeof W === 'undefined' ||
        typeof W.loginManager === 'undefined') {
      setTimeout(init, 100);
      return;
    }
    if (!W.loginManager.user) {
      W.loginManager.events.register("login", null, init);
    }

    setTranslations({
      en: {
        prefs: {
          title: 'Tab Manager',
          tab_width: 'Tab width',
          tab_height: 'Tab height',
          tab_order: 'Tab order',
          reset: 'reset',
          preserve_tab: 'Preserve opened tab over sessions',
          remove_tab: 'Tab no longer available. Remove entry?',
          hide_tab: 'Change tab visibility',
          move_up_tab: 'Move up',
          move_down_tab: 'Move down',
          edit_tab: 'Edit tab',
          change: 'change',
          set: 'set',
          reset_tab: 'Reset tab',
          icon: 'Icon',
          background_color: 'Background color',
          text_color: 'Text color',
          none_set: 'None set',
          close: 'Close',
          reload: 'The page needs to reload for the changes to take effect. Do you want to refresh now?'
        },
        update: {
          first_run: 'Thanks for using Tab Manager!\nThe settings tab on the left contains additional options now.\nThis message will only appear one time.',
          message: 'New version installed! Changelog:',
          v0_1: '- Initial version with tab memory and order preservation',
          v0_2: '- Improvements to order preservation algorithm\n- Addition of version change messages',
          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',
          v1_0_1: '- Fixed the script for Google Chrome',
          v1_0_2: '- Fixed tab size reset buttons in Google Chrome',
          v1_0_3: '- Tab styling is applied with higher specificity, but plays nicer with other scripts as well',
          v1_0_4: '- Tab alignment issues with renamed tabs in Google Chrome fixed',
          v1_0_5: '- Removed strict mode that was necessary for Google Chrome\n- Improved initalisation of script',
          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',
          v1_1_1: '- Problems with UTF8 in text editor resulted in missing icons',
          v1_1_2: '- Removed fixed scrollbar\n- Fixed a very rare potential bootstrap issue',
          v1_2: '- Renamed to Tab Manager\n- Translation fixes',
          v1_2_1: '- Added library to allow for sorting on WME beta',
          v1_2_2: '- Fixed minor issue concerning support for other languages',
          v1_2_3: '- Fix script activation on missing trailing slash in URL',
          v1_2_4: '- Internal fix for beta (hasUser function was removed)',
          v1_3: '- Added export/import functionality\n- Make it possible to recolour tabs\n- Recover from imperial/metric unit switches',
          v1_3_1: '- Technical release to deal with an upcoming change',
          v1_4_0: '- Filter icons by text\n- Fix script startup stability\n- Possibility to replace feed while retaining ability to refresh\n- Put all styling in CSS classes',
          v1_4_1: '- Fix for future WME version',
          v1_4_2: '- Fix for new WME sidepanel',
          v1_4_3: '- Restyling with new WME elements',
          v1_4_4: '- Further fixes in icon editing panel',
          v1_4_5: '- Disabled automatic tab reopening due to new layout conflicts'
        }
      },
      nl: {
        prefs: {
          title: 'Tab manager',
          tab_width: 'Tabbreedte',
          tab_height: 'Tabhoogte',
          tab_order: 'Tabvolgorde',
          reset: 'reset',
          preserve_tab: 'Geopende tab bijhouden tussen sessies',
          remove_tab: 'Tab niet langer beschikbaar. Verwijderen?',
          hide_tab: 'Tabzichtbaarheid veranderen',
          move_up_tab: 'Eerder zetten',
          move_down_tab: 'Verder zetten',
          edit_tab: 'Tab aanpassen',
          change: 'veranderen',
          set: 'instellen',
          reset_tab: 'Tab resetten',
          icon: 'Icoon',
          background_color: 'Achtergrondkleur',
          text_color: 'Tekstkleur',
          none_set: 'Niet ingesteld',
          close: 'Sluiten',
          reload: 'De pagina moet opnieuw geladen worden om de wijzigingen door te voeren. Wil je nu de pagina vernieuwen?'
        }
      }
    });

    checkVersion();
    initTabListener();
    initSettings();
    applyStyles();

    setUnitChangeListener();
  }

  function setTranslations(translations) {
    I18n.translations[I18n.currentLocale()].tabpreferences = translations.en;
    for (var i = 0; i < Object.keys(translations).length; i++) {
      var locale = Object.keys(translations)[i];
      if (I18n.currentLocale() == locale) {
        I18n.translations[locale].tabpreferences.prefs = translations[locale].prefs;
        return;
      }
    }
  }

  function setUnitChangeListener() {
    if (W.prefs) {
      W.prefs.on('change:isImperial', function() {setTimeout(reinitialize, 200); });
    } else {
      setTimeout(setUnitChangeListener, 400);
    }
  }

  function reinitialize() {
    tabReopened = false;
    tabsSecured = -1;
    initTabListener();
    initSettings();
    applyStyles();
  }

  function initTabListener() {
    var tabs = document.querySelector('#user-tabs .nav-tabs');
    if (!tabs) {
      setTimeout(initTabListener, 400);
      return;
    }
    log('Tabs found, enabling observers');
    var selectionObserver = new MutationObserver(function(mutationRecords) {
      mutationRecords.forEach(function(mutationRecord) {
        // Store last opened tab when one of the tabs receives focus
        if (localStorage.tabprefs_reopenTab && mutationRecord.target.className === 'active') {
          localStorage.tabprefs_reopenTab = mutationRecord.target.querySelector('a').hash;
        }
        adjustTabColors();
      });
    });
    for (var i = 0; i < tabs.children.length; i++) {
      selectionObserver.observe(tabs.children[i], { attributes: true, attributeFilter: ['class'] });
      if (tabs.children[i].title) {
        $(tabs.children[i]).tooltip({
          trigger: 'hover'
        });
      }
      if (!Storage.isTabVisible(tabs.children[i].querySelector('a').hash)) {
        tabs.children[i].style.display = 'none';
      }
    }

    var tabObserver = new MutationObserver(function(mutationRecords) {
      if (!tabReopened) {
        reopenTab();
      }
      mutationRecords.forEach(function(mutationRecord) {
        // Reorder tabs when new ones get added
        if (mutationRecord.addedNodes.length > 0) {
          for (var i = 0; i < mutationRecord.addedNodes.length; i++) {
            var node = mutationRecord.addedNodes[i],
                anchor = node.querySelector('a'),
                hash = anchor.hash;
            // Also start observing here for changes to tab selection
            selectionObserver.observe(node, { attributes: true, attributeFilter: ['class'] });
            if (anchor.title) {
              $(anchor).tooltip({
                trigger: 'hover'
              });
            }
            // Tab visibility
            if (!Storage.isTabVisible(hash)) {
              node.style.display = 'none';
            }
          }
          updateTabs();
          reorderTabs();
          if (localStorage.tabprefs_tabwidth || localStorage.tabprefs_tabheight) {
            resizeTabs();
          }
        }
      });
    });
    tabObserver.observe(tabs, { childList: true });

    var editPanelObserver = new MutationObserver(function(mutationRecords) {
      resizeTabs();
    });
    editPanelObserver.observe(document.querySelector('#edit-panel .contents'), { childList: true });

    reopenTab();
    updateTabs();
    reorderTabs();
    if (localStorage.tabprefs_tabwidth || localStorage.tabprefs_tabheight) {
      resizeTabs();
    }
  }

  function initSettings() {
    var prefsTab = document.querySelector('#sidepanel-prefs');
    if (!prefsTab) {
      setTimeout(initSettings, 400);
      return;
    }

    // First run
    if (localStorage.tabprefs_preserveOrder == null) {
      var tabs = document.querySelectorAll('#user-tabs .nav-tabs li a'),
          hashes = [];
      for (var i = 0; i < tabs.length; i++) {
        hashes.push(tabs[i].hash);
      }
      localStorage.tabprefs_preserveOrder = hashes.join();
    }

    var section = prefsTab.querySelector('.settings'),
        version = document.createElement('a'),
        headingContainer = document.createElement('div'),
        heading = document.createElement('wz-overline'),
        tabOrderPanel = document.createElement('div'),
        configExportImport = document.createElement('div'),
        configExport = document.createElement('a'),
        configImport = document.createElement('input'),
        configImportLabel = document.createElement('label'),
        formGroup = document.createElement('div');
    version.href = 'https://www.waze.com/forum/viewtopic.php?f=819&t=168863';
    version.target = '_blank';
    version.appendChild(document.createTextNode('v' + GM_info.script.version));
    heading.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.title')));
    headingContainer.style.borderTop = '1px solid #e0e0e0';
    headingContainer.style.marginTop = '1em';
    headingContainer.className = 'panel-header-component';
    headingContainer.appendChild(heading);
    headingContainer.appendChild(version);
    formGroup.className = 'form-group';
    formGroup.style.marginBottom = '15px';
    formGroup.appendChild(createSlider('tabWidth', I18n.t('tabpreferences.prefs.tab_width'), 'tabprefs_tabwidth', 15, resizeTabs));
    formGroup.appendChild(createSlider('tabHeight', I18n.t('tabpreferences.prefs.tab_height'), 'tabprefs_tabheight', 5, resizeTabs));
    formGroup.appendChild(createOption('reopenTab', I18n.t('tabpreferences.prefs.preserve_tab'), (localStorage.tabprefs_reopenTab ? true : false), function() {
      if (this.checked) {
        localStorage.tabprefs_reopenTab = document.querySelector('#user-tabs .nav-tabs li.active a').hash;
      } else {
        localStorage.removeItem('tabprefs_reopenTab');
      }
    }));

    var tabOrderLabel = document.createElement('label');
    tabOrderLabel.textContent = I18n.t('tabpreferences.prefs.tab_order');
    tabOrderLabel.style = 'padding-top: 8px';
    formGroup.appendChild(tabOrderLabel);
    tabOrderPanel.className = 'result-list';
    tabOrderPanel.id = 'tabPreferencesOrder';
    formGroup.appendChild(tabOrderPanel);
    sortable(tabOrderPanel, {
      forcePlaceholderSize: true,
      placeholderClass: 'result'
    })[0].addEventListener('sortupdate', function(e) {
      var order = localStorage.tabprefs_preserveOrder.split(',');
      order.splice(e.detail.elementIndex, 0, order.splice(e.detail.oldElementIndex, 1)[0]);
      localStorage.tabprefs_preserveOrder = order.join();
      reorderTabs(true);
      refreshTabPanel();
    });

    configExportImport.style.textAlign = 'center';
    configExportImport.style.marginBottom = '1em';
    configExport.download = 'tab-manager.conf';
    configExport.style.textDecoration = 'none';
    configExport.href = '#';
    configExport.addEventListener('click', function() {
      configExport.href = 'data:application/octet-stream,' + encodeURIComponent(Storage.exportConfig());
    });
    configExport.innerHTML = '<i class="fa fa-upload"></i> Export</a>';
    configImport.id = 'tab-manager-import';
    configImport.type = 'file';
    configImport.accept = '.conf';
    configImport.style.display = 'none';
    configImportLabel.style.fontWeight = 'normal';
    configImportLabel.style.cursor = 'pointer';
    configImportLabel.style.marginLeft = '20px';
    configImportLabel.innerHTML = '<i class="fa fa-download"></i> Import';
    configImportLabel.htmlFor = configImport.id;
    configImport.addEventListener('change', function() {
      var reader = new FileReader();
      reader.addEventListener('load', function(e) {
        Storage.importConfig(reader.result);
        if (confirm(I18n.t('tabpreferences.prefs.reload'))) {
          window.location.reload();
        }
      });
      reader.readAsText(configImport.files[0]);
    });
    configExportImport.appendChild(configExport);
    configExportImport.appendChild(configImportLabel);
    configExportImport.appendChild(configImport);

    // Obsolete option, remove from localStorage
    if (localStorage.tabprefs_hidePermissions) {
      localStorage.removeItem('tabprefs_hidePermissions');
    }

    section.appendChild(headingContainer);
    section.appendChild(formGroup);
    section.appendChild(configExportImport);

    iconFilterList.id = 'tab-manager-icon-filter';
    section.appendChild(iconFilterList);

    refreshTabPanel();
  }

  // Add options to the preferences tab
  function createOption(name, description, checked, eventHandler) {
    var checkbox = document.createElement('wz-checkbox');
    checkbox.addEventListener('click', eventHandler);
    checkbox.checked = checked;
    checkbox.textContent = description;
    return checkbox;
  }

  // Add sliders to the preferences tab
  function createSlider(name, description, storageKey, defaultValue, eventHandler) {
    var input = document.createElement('input');
    input.type = 'range';
    input.name = name;
    input.id = name + '-on';
    input.min = 0;
    input.max = 30;
    input.value = (localStorage[storageKey] ? localStorage[storageKey] : defaultValue);
    input.addEventListener('input', function() {
      localStorage[storageKey] = this.value;
      eventHandler();
    });
    input.style.verticalAlign = 'middle';
    var label = document.createElement('label');
    label.htmlFor = name + '-on';
    label.appendChild(document.createTextNode(description));
    label.style.marginRight = '4px';
    var reset = document.createElement('button');
    reset.className = 'btn-link pull-right';
    reset.addEventListener('click', function() {
      input.value = defaultValue;
      localStorage.removeItem(storageKey);
      eventHandler();
    });
    reset.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.reset')));
    var container = document.createElement('div');
    container.className = 'controls-container';
    container.appendChild(reset);
    container.appendChild(label);
    container.appendChild(input);
    return container;
  }

  // Attempt to reopen the tab opened during the previous session
  function reopenTab() {
    // Temporarily disable tab reopening due to new version issues
    /*if (!localStorage.tabprefs_reopenTab) {
      return;
    }
    var tab = document.querySelector('#user-tabs .nav-tabs li a[href$="'+localStorage.tabprefs_reopenTab+'"]');
    if (!tabReopened && tab) {
      var clickEvent = new MouseEvent('click', {
        bubbles: true,
        cancelable: true,
        view: window
      });
      tab.dispatchEvent(clickEvent);
      tabReopened = true;
    }*/
  }

  // If necessary, start reordering the tags as they were saved
  function reorderTabs(force) {
    if (force) {
      tabsSecured = -1;
    }
    // Protection against possible infinite loop if certain scripts don't follow the standards
    if (timesRan > 1000) {
      return;
    }
    if (timesRan === 1000) {
      log('Sanity limit reached! Tab Manager is most likely conflicting with another script. Backing off from now on.');
      // run one last time to increase counter
    }
    timesRan++;
    var hashes = localStorage.tabprefs_preserveOrder.split(','),
        navTabs = document.querySelector('#user-tabs ul.nav-tabs'),
        navAnchors = navTabs.querySelectorAll('li a');
    // First we check whether we know all tabs we currently have
    for (var i = 0; i < navAnchors.length; i++) {
      if (hashes.indexOf(navAnchors[i].hash) === -1) {
        // Add unknown tags to the end of the list
        hashes.push(navAnchors[i].hash);
      }
    }
    localStorage.tabprefs_preserveOrder = hashes.join();
    refreshTabPanel();
    // Then we put them in the order we have stored
    var tabsMissing = 0;
    for (var i = tabsSecured+1; i < hashes.length; i++) {
      var tabAnchor = navTabs.querySelector('a[href$="'+hashes[i]+'"]');

      if (!tabAnchor) {
        tabsMissing++;
        continue;
      }
      var tabIndex = Array.prototype.indexOf.call(navTabs.children, tabAnchor.parentNode);
      if (tabIndex === i && tabsSecured === tabIndex-1) {
        tabsSecured++;
      }
      if (tabAnchor && tabIndex !== i - tabsMissing && i - tabsMissing < navTabs.children.length) {
        navTabs.insertBefore(tabAnchor.parentNode, navTabs.children[i - tabsMissing]);
        if (tabsSecured === i-1) {
          tabsSecured++;
        }
      }
    }
  }

  function resizeTabs() {
    var width = (localStorage.tabprefs_tabwidth ? localStorage.tabprefs_tabwidth : 15),
        height = (localStorage.tabprefs_tabheight ? localStorage.tabprefs_tabheight : 5),
        tabAnchors = document.querySelectorAll('#user-tabs .nav-tabs li a');
    for (var i = 0; i < tabAnchors.length; i++) {
      tabAnchors[i].style.cssText += ';padding:' + height + 'px ' + width + 'px !important';
      tabAnchors[i].style.height = 'auto';
    }
    if (!localStorage.tabprefs_adjustSidepanel) { // Work in progress for resizing of other sidepanel tabs
      return;
    }
    var editPanelAnchors = document.querySelectorAll('#edit-panel .nav.nav-tabs > li > a');
    _.each(editPanelAnchors, (editPanelAnchor) => { editPanelAnchor.style.cssText += ';padding:' + height + 'px ' + width + 'px !important' });
  }

  function saveTab(anchor) {
    if (!anchor.originalChildren) { // only do so if it hasn't been done yet
      var children = [];
      _.each(anchor.childNodes, (anchorChild) => children.push(anchorChild));
      anchor.originalTitle = anchor.title || anchor.dataset.originalTitle || anchor.parentNode.title || anchor.parentNode.dataset.originalTitle;
      if (!anchor.title) {
        anchor.title = anchor.textContent.trim();
      }
      $(anchor).tooltip({
        trigger: 'hover'
      });
      anchor.originalChildren = children;
    }
  }

  function restoreTab(anchor) {
    while (anchor.firstChild) {
      anchor.removeChild(anchor.firstChild);
    }
    anchor.originalChildren.forEach(function(node) {
      anchor.appendChild(node);
      if (node.classList) {
        node.classList.remove('hidden');
      }
    });
    delete anchor.originalChildren;
    if (anchor.originalTitle) {
      anchor.title = anchor.originalTitle;
      $(anchor).tooltip({
        trigger: 'hover'
      });
    } else {
      anchor.removeAttribute('title');
      $(anchor).tooltip('destroy');
      delete anchor.dataset.originalTitle;
    }
    delete anchor.originalTitle;
  }

  function renameTabs(hash) {
    _.each(document.querySelectorAll('#user-tabs .nav-tabs li a'), (anchor) => {
      var config = Storage.getTabConfig(anchor.hash);
      if (config && config.icon && (!anchor.originalChildren || hash == anchor.hash)) {
        saveTab(anchor);
        if (config.icon) {
          var span = document.createElement('span');
          switch (config.icon.fontFamily) {
            case 'FontAwesome':
              span.className = 'fa';
              break;
            default:
              log('Unsupported fontFamily found: ' + config.icon.fontFamily);
          }
          span.classList.add('replacement');
          span.appendChild(document.createTextNode(String.fromCharCode(config.icon.charCode)));
          if (anchor.querySelector('span')) {
            span.addEventListener('click', function(e) { e.target.previousElementSibling.dispatchEvent(new Event('click')); });
          }
          cleanTabAnchor(anchor);
          anchor.appendChild(span);
          /*if (anchor.hash === '#sidepanel-feed') {
            var feedObserver = new MutationObserver(function(mutations, observer) {
              renameTabs(anchor.hash);
              observer.disconnect(); // we only need to clean once, otherwise we'll hide our own icon
            });
            feedObserver.observe(anchor, {
              childList: true
            });
          }*/
        }
      }
      // Replacements have been removed, revert
      if (!config.icon && !config.name && anchor.originalChildren) {
        restoreTab(anchor);
      }
    });
  }

  function cleanTabAnchor(anchor) {
    _.each(anchor.childNodes, function(node) {
      if (!node) {
        return;
      }
      if (node.classList && !node.classList.contains('replacement')) {
        node.classList.add('hidden');
      } else {
        anchor.removeChild(node);
      }
    });
  }

  function adjustTabColors() {
    _.each(document.querySelectorAll('#user-tabs .nav-tabs li a'), (anchor) => {
      var config = Storage.getTabConfig(anchor.hash);
      anchor.style.borderBottomWidth = '0';
      if (config && (config.backgroundColor || config.color)) {
        saveTab(anchor);
      }
      anchor.style.backgroundColor = config && config.backgroundColor && !anchor.parentNode.classList.contains('active') ? config.backgroundColor : '';
      anchor.style.color = config && config.color && !anchor.parentNode.classList.contains('active') ? config.color : '';
    });
  }

  function updateTabs() {
    renameTabs();
    adjustTabColors();
  }

  function refreshTabPanel() {
    var tabPanel = document.getElementById('tabPreferencesOrder');
    if (!tabPanel) {
      return;
    }
    var order = localStorage.tabprefs_preserveOrder.split(',');
    while (tabPanel.firstChild) { // clear out tabPanel's children
      tabPanel.removeChild(tabPanel.firstChild);
    }
    order.forEach(function(hash, index, obj) {
      var card = document.createElement('wz-card'),
          item = document.createElement('div'),
          name = document.createElement('div'),
          buttons = document.createElement('div'),
          moveUp = createIconButton('fa-chevron-up', I18n.t('tabpreferences.prefs.move_up_tab')),
          moveDown = createIconButton('fa-chevron-down', I18n.t('tabpreferences.prefs.move_down_tab')),
          remove = createIconButton('fa-remove', I18n.t('tabpreferences.prefs.remove_tab')),
          hide = createIconButton((Storage.isTabVisible(hash) ? 'fa-eye' : 'fa-eye-slash'), I18n.t('tabpreferences.prefs.hide_tab')),
          edit = createIconButton('fa-pencil', I18n.t('tabpreferences.prefs.edit_tab')),
          anchor = document.querySelector('#user-tabs .nav-tabs li a[href$="'+hash+'"]'),
          tabConfig = {};
      if (anchor) {
        tabConfig = Storage.getTabConfig(hash);
      }
      card.className = 'result list-item-card';
      card.style.cursor = 'default';
      item.className = 'tab-manager-list-card';
      card.appendChild(item);
      // Add item handle
      var handle = document.createElement('div');
      handle.className = 'tab-manager-list-handle';
      handle.innerHTML = '<i class="fa fa-reorder"></i>';
      item.appendChild(handle);
      name.className = 'tab-manager-tab-handle-name';
      // Add name and replacement
      if (anchor) {
        var title;
        if (tabConfig.icon || tabConfig.backgroundColor || tabConfig.color) {
          title = anchor.originalTitle;
          var arrow = document.createElement('span');
          arrow.className = 'fa fa-arrow-right';
          arrow.style.color = '#888';
          arrow.style.margin = '0 6px';
          anchor.originalChildren.forEach(function(node) {
            name.appendChild(node.cloneNode(true));
          });
          if (title) {
            name.appendChild(document.createTextNode(' (' + title + ')'));
          }
          name.appendChild(arrow);
          var replacement = document.createElement('span');
          replacement.style.padding = '2px 4px';
          replacement.style.borderRadius = '5px';
          replacement.style.backgroundColor = tabConfig.backgroundColor || '';
          replacement.style.color = tabConfig.color || '';
          for (var i = 0; i < anchor.childNodes.length; i++) {
            if (anchor.childNodes[i].textContent == '\n') {
              continue;
            }
            replacement.appendChild(anchor.childNodes[i].cloneNode(true));
          }
          name.appendChild(replacement);
        } else {
          name.innerHTML = anchor.innerHTML;
          title = anchor.title || anchor.dataset.originalTitle || anchor.parentNode.title || anchor.parentNode.dataset.originalTitle;
          if (title) {
            name.appendChild(document.createTextNode(' (' + title + ')'));
          }
        }
        if (!Storage.isTabVisible(hash)) {
          name.style.color = '#888';
        }
      } else {
        name.style.color = '#888';
        name.style.fontStyle = 'italic';
        name.appendChild(document.createTextNode(hash));
      }
      item.appendChild(name);
      // Add action buttons
      if (!anchor) {
        remove.addEventListener('click', function() {
          obj.splice(index, 1);
          localStorage.tabprefs_preserveOrder = obj.join();
          refreshTabPanel();
        });
        buttons.appendChild(remove);
      } else {
        if (hash !== '#sidepanel-prefs') { // Prevent the preferences tab from being hidden and getting locked out
          hide.addEventListener('click', function() {
            if (Storage.isTabVisible(hash)) {
              this.classList.remove('fa-eye');
              this.classList.add('fa-eye-slash');
              anchor.parentNode.style.display = 'none';
              Storage.setTabVisibility(hash, false);
            } else {
              this.classList.remove('fa-eye-slash');
              this.classList.add('fa-eye');
              anchor.parentNode.style.display = 'block';
              Storage.setTabVisibility(hash, true);
            }
            refreshTabPanel();
          });
          hide.addEventListener('mouseenter', function() {
            this.classList.toggle('fa-eye-slash', Storage.isTabVisible(hash));
            this.classList.toggle('fa-eye', !Storage.isTabVisible(hash));
          });
          hide.addEventListener('mouseleave', function() {
            this.classList.toggle('fa-eye', Storage.isTabVisible(hash));
            this.classList.toggle('fa-eye-slash', !Storage.isTabVisible(hash));
          });
          buttons.appendChild(hide);
        }
        edit.addEventListener('click', function() {
          if (!item.details) {
            createTabConfigPane(item, hash);
          } else {
            item.parentNode.removeChild(item.details);
            item.details = false;
          }
        });
        buttons.appendChild(edit);
      }
      if (index === order.length - 1) {
        moveDown.style.cursor = 'default';
        moveDown.style.color = '#aaa';
      } else {
        moveDown.addEventListener('click', function() {
          obj.splice(index+1, 0, obj.splice(index, 1)[0]);
          localStorage.tabprefs_preserveOrder = obj.join();
          reorderTabs(true);
          refreshTabPanel();
        });
      }
      buttons.appendChild(moveDown);
      if (index === 0) {
        moveUp.style.color = '#aaa';
        moveUp.style.cursor = 'default';
      } else {
        moveUp.addEventListener('click', function() {
          obj.splice(index-1, 0, obj.splice(index, 1)[0]);
          localStorage.tabprefs_preserveOrder = obj.join();
          reorderTabs(true);
          refreshTabPanel();
        });
      }
      buttons.appendChild(moveUp);
      buttons.className = 'tab-manager-card-buttons';
      item.appendChild(buttons);
      tabPanel.appendChild(card);
    });
    sortable(tabPanel);
  }

  function createIconButton(icon, title) {
    var button = document.createElement('button');
    button.className = 'fa ' + icon + ' tab-manager-icon-button';
    if (title) {
      button.title = title;
      $(button).tooltip({
        trigger: 'hover'
      });
    }
    return button;
  }

  function createDetailsLabel(text) {
    var block = document.createElement('div');
    block.textContent = text;
    return block;
  }

  function createColorChooser(propertyName, hash) {
    var div = document.createElement('div'),
        btn = document.createElement('a'),
        input = document.createElement('input'),
        noneSet = document.createElement('span'),
        tabConfig = Storage.getTabConfig(hash);
    input.type = 'color';
    input.classList.toggle('hidden', !tabConfig[propertyName]);
    var eventListener = function() {
      tabConfig[propertyName] = input.value;
      Storage.setTabConfig(hash, tabConfig);
      adjustTabColors();
    };
    input.addEventListener('input', eventListener);
    input.addEventListener('change', eventListener);
    div.appendChild(input);
    noneSet.style.fontStyle = 'italic';
    noneSet.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.none_set')));
    noneSet.classList.toggle('hidden', tabConfig[propertyName] != undefined);
    div.appendChild(noneSet);
    btn.style.marginLeft = '10px';
    btn.style.cursor = 'pointer';
    btn.textContent = tabConfig[propertyName] ? I18n.t('tabpreferences.prefs.reset') : I18n.t('tabpreferences.prefs.set');
    btn.addEventListener('click', function(e) {
      noneSet.classList.toggle('hidden');
      input.classList.toggle('hidden');
      btn.textContent = input.classList.contains('hidden') ? I18n.t('tabpreferences.prefs.set') : I18n.t('tabpreferences.prefs.reset');
      if (!input.classList.contains('hidden')) {
        input.dispatchEvent(new MouseEvent(e.type, e));
      } else {
        delete tabConfig[propertyName];
        Storage.setTabConfig(hash, tabConfig);
        adjustTabColors();
      }
    });
    div.appendChild(btn);
    if (tabConfig[propertyName]) {
      input.value = tabConfig[propertyName];
    }
    return div;
  }

  function createFilterInput(handler) {
    var form = document.createElement('div'),
        input = document.createElement('input');
    form.className = 'form-search tab-manager-icons-filter';
    input.className = 'search-query';
    input.autocomplete = 'off';
    input.placeholder = 'Filter icons';
    form.appendChild(input);
    input.addEventListener('change', handler);
    input.addEventListener('input', handler);
    input.setAttribute('list', 'tab-manager-icon-filter');
    return form;
  }

  function checkVersion() {
    var version = localStorage.tabprefs_version,
        scriptVersion = GM_info.script.version;
    if (!version) {
      showMessage(I18n.t('tabpreferences.update.first_run'));
      localStorage.tabprefs_version = scriptVersion;
    } else if (version !== scriptVersion) {
      if (versions.indexOf(version) === -1) {
        // There's tampering happening if we arrive here, just set to current version and ignore issue
        localStorage.tabprefs_version = scriptVersion;
        return;
      }
      var message = I18n.t('tabpreferences.update.message');
      for (var i = versions.indexOf(version)+1; i < versions.length; i++) {
        message += '\nv' + versions[i] + ':\n' + I18n.t('tabpreferences.update.v' + versions[i].replace(/\./g, '_'));
      }
      showMessage(message);
      localStorage.tabprefs_version = scriptVersion;
    }
  }

  function createTabConfigPane(container, hash) {
    var tabConfig = Storage.getTabConfig(hash),
        details = document.createElement('div'),
        iconDiv = document.createElement('div'),
        iconBtn = document.createElement('a'),
        icon = document.createElement('span'),
        reset = document.createElement('button'),
        close = document.createElement('button');
    details.appendChild(createDetailsLabel(I18n.t('tabpreferences.prefs.icon')));
    iconBtn.appendChild(document.createTextNode((tabConfig.icon ? I18n.t('tabpreferences.prefs.change') : I18n.t('tabpreferences.prefs.set'))));
    iconBtn.className = 'tab-manager-add-icon-button';
    iconBtn.addEventListener('click', function() {
      if (!iconBtn.filterContainer) {
        var iconsFilterContainer = document.createElement('div'),
            icons = document.createElement('div');
        iconsFilterContainer.className = 'tab-manager-icons-filter';
        icons.className = 'tab-manager-icons-container';
        // FontAwesome: symbols start at 'F000' (= 61440) and end at 'F2E0' in version 4.7 of FontAwesome (Waze uses 4.7)
        for (var i = 61440; i <= 62177; i++) {
          var icon = document.createElement('div');
          icon.appendChild(document.createTextNode(String.fromCharCode(i)));
          icon.className = 'tab-manager-select-icon';
          if (tabConfig && tabConfig.icon && tabConfig.icon.charCode == i) {
            icon.classList.add('selected');
          }
          // TODO: add suggested here as well after creating some suggestions
          icon.dataset.charCode = i;
          icon.addEventListener('click', function() {
            tabConfig.icon = {
              fontFamily: 'FontAwesome',
              charCode: this.dataset.charCode
            };
            Storage.setTabConfig(hash, tabConfig);
            container.parentNode.removeChild(details);
            renameTabs(hash);
            refreshTabPanel();
          });
          icons.appendChild(icon);
        }
        iconsFilterContainer.appendChild(icons);
        var iconsFilter = createFilterInput((event) => {
          var filterText = event.target.value;
          while (iconFilterList.firstChild) {
            iconFilterList.removeChild(iconFilterList.firstChild);
          }
          _.each(document.querySelectorAll('.tab-manager-select-icon'), (icon) => icon.classList.add('hidden'));
          Object.keys(iconKeywords).forEach((font) => {
            Object.keys(iconKeywords[font])
              .filter((keyword) => keyword.indexOf(filterText) != -1)
              .forEach((keyword) => {
              iconKeywords[font][keyword].forEach((code) => {
                document.querySelector('.tab-manager-select-icon[data-char-code="' + code + '"]').classList.remove('hidden');
              });
              iconFilterList.appendChild(new Option(keyword));
            });
          });
        });
        iconsFilterContainer.appendChild(iconsFilter);
        details.insertBefore(iconsFilterContainer, reset);
        iconBtn.filterContainer = iconsFilterContainer;
      } else {
        details.removeChild(iconBtn.filterContainer);
        iconBtn.filterContainer = false;
      }
    });
    if (tabConfig.icon) {
      icon.style.fontFamily = tabConfig.icon.fontFamily;
      icon.appendChild(document.createTextNode(String.fromCharCode(tabConfig.icon.charCode)));
    } else {
      icon.style.fontStyle = 'italic';
      icon.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.none_set')));
    }
    iconDiv.appendChild(icon);
    iconDiv.appendChild(iconBtn);
    details.appendChild(iconDiv);
    details.appendChild(createDetailsLabel(I18n.t('tabpreferences.prefs.background_color')));
    details.appendChild(createColorChooser('backgroundColor', hash));
    details.appendChild(createDetailsLabel(I18n.t('tabpreferences.prefs.text_color')));
    details.appendChild(createColorChooser('color', hash));
    reset.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.reset_tab')));
    reset.style.margin = '0 5px';
    reset.className = 'btn btn-danger';
    reset.addEventListener('click', function() {
      Storage.removeTabConfig(hash);
      container.parentNode.removeChild(details);
      container.details = false;
      updateTabs();
      refreshTabPanel();
    });
    details.appendChild(reset);
    close.appendChild(document.createTextNode(I18n.t('tabpreferences.prefs.close')));
    close.style.margin = '0 5px';
    close.className = 'btn btn-default';
    close.addEventListener('click', function() {
      container.parentNode.removeChild(details);
      container.details = false;
      refreshTabPanel();
    });
    details.appendChild(close);
    details.className = 'tab-manager-tab-details';
    container.details = details;
    container.after(details);
  }

  function showMessage(message) {
    alert('Tab Manager\n=============\n' + message);
  }

  function log(message) {
    if (console.log) {
      console.log('%cWME Tab Manager: %c' + message, 'color:black', 'color:#d97e00');
    }
  }

  function applyStyles() {
    if (!styleElement) {
      var styleElement = document.createElement('style');
      styleElement.textContent = `
#sidepanel-prefs .controls-container {
  padding: 10px 0;
}
#tabPreferencesOrder {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.tab-manager-list-card {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
}
.tab-manager-list-handle {
  color: #c2c2c2;
  cursor: move;
  font-size: 20px;
}
.tab-manager-tab-handle-name {
  border-right: 1px solid rgb(213, 215, 219);
  padding-right: 3px;
  flex-grow: 1;
}
.tab-manager-card-buttons {
  flex-shrink: 0;
}
.tab-manager-tab-details {
  border-top: 1px solid rgb(213, 215, 219);
  margin-top: 10px;
  padding-top: 10px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
  font-size: 14px;
}
.tab-manager-tab-details > div:nth-child(odd) {
  text-align: right;
}
.tab-manager-add-icon-button {
  margin-left: 10px;
  cursor: pointer;
}
.tab-manager-icon-button {
  border: none;
  background: none;
  padding: 0 2px;
  cursor: pointer;
  height: auto;
  outline: none;
}
.tab-manager-tab-details > div.tab-manager-icons-filter {
  grid-column: 1 / span 2;
  text-align: left;
  padding: 3px;
  border-top: 1px solid rgb(213, 215, 219);
  margin-top: 10px;
  padding-top: 10px;
}
.tab-manager-icons-filter > .form-search {
  margin-bottom: 5px;
}
.tab-manager-icons-filter > .form-search > .search-query {
  padding-left: 16px;
}
div.tab-manager-icons-filter > .tab-manager-icons-container {
  height: 10em;
  padding: 3px;
  overflow: auto;
  font-size: 12px;
  word-wrap: break-word;
  font-family: FontAwesome;
}
.tab-manager-select-icon {
  cursor: pointer;
  width: 1.28571em;
  float: left;
  margin-right: 1px;
  text-align: center;
}
.tab-manager-icons-container .selected {
  text-shadow: 0 0 10px #00edff, 1px 1px #00edff;
}
.tab-manager-icons-container .suggested {
  text-shadow: 0 0 10px orange, 1px 1px orange;
}
`;
    }
    if (!styleElement.parentNode) {
      document.head.appendChild(styleElement);
    }
  }

  init();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址