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.

As of 01.03.2023. See ბოლო ვერსია.

// ==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();
})();