IkaTweaks

Improvements for Ikariam

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

//
// ==UserScript==
//
// @name            IkaTweaks
// @description     Improvements for Ikariam
// @version         2.6
// @author          Yvonne P.
// @license         MIT; https://opensource.org/licenses/MIT
// @icon            http://de.ikariam.gameforge.com/favicon.ico
// @namespace       YveOne
// @include         /^https?:\/\/s\d+-\w+\.ikariam\.gameforge\.com.*?$/
// @run-at          document-start
//
// ==/UserScript==
//

/* jshint esversion: 6 */
/* global $ */
/* global ikariam */
/* global ajax */
/* global GM_info */
/* global dataSetForView */
/* global LocalizationStrings */

(function(window){
    "use strict";
    var jshintUnused;

    //--------------------------------------------------------------------------------------------------
    // CONSTANTS

    const _LINKS_ = {
        OnePiece    : 'http://www.iconarchive.com/show/one-piece-character-icons-by-crountch.html',
        GreasyFork  : 'https://greasyfork.org/de/scripts/401313-ikatweaks',
        OpenUserJS  : 'https://openuserjs.org/scripts/YveOne/IkaTweaks',
        GitHubRepo  : 'https://github.com/YveOne/Userscript-IkaTweaks',
    };

    // CONSTANTS
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // SYSTEM FUNCTIONS

    function forEach(obj, func)                 {for(var k in obj){if(obj.hasOwnProperty(k)){func(obj[k],k,obj);}}}
    function jsonDecode(str, dflt)              {var obj=null;try{obj=JSON.parse(str);}catch(e){}return((obj!==null)?obj:dflt);}
    function injectCSS(cssText)                 {var el=document.createElement('style');el.setAttribute('type','text/css');if(el.styleSheet){el.styleSheet.cssText=cssText;}else{el.appendChild(document.createTextNode(cssText));}document.querySelector('head').appendChild(el);return el;}
    function hookFunction(obj, fn, cb)          {(function(of){obj[fn]=function(){var ret=of.apply(this,arguments);cb.apply(this,arguments);return ret;};}(obj[fn]));}
    function removeElement(el)                  {try{return el.parentNode.removeChild(el);}catch(e){}return null;}
    function forceInt(str)                      {return parseInt(str.replace(/\D/g, ''));}
    function random(min, max)                   {min=Math.ceil(min);max=Math.floor(max);return Math.floor(Math.random()*(max-min+1))+min; } 
    function timestamp()                        {return Math.round(Date.now()/1000)}

    // v2 (c) Yvonne P.
    function waitFor(cond)                      {return new Promise((ok,ko)=>{var tEnd=Date.now()+waitFor.timeout,ret,w4=function(){try{ret=cond();}catch(e){ret=false;}if(ret){return ok(ret);}else{if(tEnd<Date.now()){return ko();}}setTimeout(w4,waitFor.sleep);};w4();});}
    waitFor.timeout = 2000;
    waitFor.sleep = 33;

    // v3 (c) Yvonne P.
    function LocalStorageHandler(tag) {
        var data = JSON.parse(localStorage.getItem(tag)) || {
            storedKeys : {},
        };
        function unsetItem(k1) {
            var s = {};
            forEach(data.storedKeys, (_, k) => {
                if (k1 !== k) {
                    s[k] = data.storedKeys[k];
                }
            });
            if (data) {
                data.storedKeys = s;
            }
            localStorage.setItem(tag, JSON.stringify(data));
        }
        function setItem(k) {
            if (data) {
                data.storedKeys[tag] = Date.now();
                data.storedKeys[k] = Date.now();
            }
            localStorage.setItem(tag, JSON.stringify(data));
        }
        this.drop = function(key) {
            key = tag+key;
            localStorage.removeItem(key);
            unsetItem(key);
            return (typeof localStorage.getItem(key) == 'undefined');
        };
        this.save = function(key, val) {
            key = tag+key;
            localStorage.setItem(key, val);
            setItem(key);
            return (localStorage.getItem(key) == val);
        };
        this.load = function(key, dflt) {
            key = tag+key;
            var v = localStorage.getItem(key);
            return (v!==null ) ? v : dflt;
        };
        this.data = function() {
            return JSON.parse(JSON.stringify(data));
        };
        this.clear = function(t) {
            var b = true;
            if (typeof t == 'string') {
                var s = [t];
                forEach(data.storedKeys, (_, k) => {
                    s.push(' "'+k+'"');
                });
                b = confirm(s.join("\n"));
            }
            if (b) {
                forEach(data.storedKeys, (_, k) => {
                    localStorage.removeItem(k);
                });
                data = null;
                return true;
            }
            return false;
        };
    }

    // v2 (c) Yvonne P.
    function EasyTemplates(){
        var that=this,tplList={};
        var reParse=new RegExp('\\{[a-zA-Z0-9_]+\\}','gi');
        this.set=function(id,html){tplList[id]=html;};
        this.get=function(id,data){id=id||'';return (tplList[id])?that.parse(tplList[id],data):'Template "'+id+'" not found';};
        this.each=function(obj,func){var ret=[],keys=Object.keys(obj);while(keys.length){var k=keys.shift(),a=func(obj[k],k,obj);if(a)ret.push(that.get.apply(null,a));}return ret.join('');};
        this.parse=function(html,data){html=html||'';data=data||{};return html.replace(reParse,function(x){var y=data[x.substr(1,x.length-2)];return (y!==null&&y!==undefined)?y:x;});};
    }

    // v4 (c) Yvonne P.
    function LanguageHandler(useLocal,baseLocal){
        var str={},ctr={},als={};
        function get(k){return (typeof str[k]==='string')?str[k]:k;}
        function set(c,l,n){if(c instanceof Array){while(c.length){set(c.shift(),n);}return;}if(als[c]===useLocal){c=als[c];}ctr[c]=l;if(c===baseLocal){Object.keys(n).forEach((k)=>{var v=n[k];if(v!==null){str[k]=(typeof str[k]=='string')?str[k]:v.toString();}});}else{if(c===useLocal){Object.keys(n).forEach((k)=>{var v=n[k];if(v!==null){str[k]=v.toString();}});}}}
        var ret=function(){if(arguments.length===1)return get.apply(null,arguments);if(arguments.length===3)return set.apply(null,arguments);return str;};
        ret.codes=function(){return JSON.parse(JSON.stringify(ctr));};
        ret.alias=function(a,b){als[a]=b;};
        ret.local=function(){return useLocal;};
        return ret;
    }

    const TPL  = new EasyTemplates();
    const LS   = new LocalStorageHandler('IkaTweaks_');
    const LANG = LanguageHandler(LS.load('LANG', location.hostname.match(/s\d+\-(\w+)\.ikariam\.gameforge\.com/i)[1]), 'en');

    (function(){
        var _injectCSS=injectCSS,queue=[],headAvailable=false;
        injectCSS=function(css,cb){if(headAvailable){(cb||function(){})(_injectCSS(css));}else{queue.push([css,cb]);}};
        waitFor(()=>{return document.querySelector('head');}).then(()=>{headAvailable=true;while(queue.length){injectCSS.apply(null,queue.shift());}}).catch(()=>{});
    }());

    // SYSTEM FUNCTIONS
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // HTML: Select DropDown

    TPL.set('SelectContainer', `
        <div id="js_{selectId}Container" class="select_container size{selectSize}">
            <select id="js_{selectId}Options" class="dropdown" name="{selectId}Options">
                {selectOptions}
            </select>
        </div>
    `);

    TPL.set('SelectOption', `
        <option value="{value}" {selected}>{text}</option>
    `);

    function SelectDropDown(selectId, size, options, selected) {
        this.val = function() {
            return $(`#js_${selectId}Options`).val();
        };
        this.tpl = function() {
            return TPL.get('SelectContainer', {
                selectSize      : size,
                selectId        : selectId,
                selectOptions   : TPL.each(options, function(v, k){
                    return ['SelectOption', {
                        value   : k,
                        text    : v,
                        selected: (selected == k) ? 'selected="selected"' : '',
                    }];
                }),
            });
        };
        this.change = function(cb) {
            $('#js_'+selectId+'Options').change(cb);
        };
    }

    // HTML: Select DropDown
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // HTML: Checkboxes

    function isChecked(id) {
        return $(id+'Img').hasClass('checked');
    }

    // HTML: Checkboxes
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // CORE

    waitFor.ajaxResponder = (function(waitFor){
        var resp = false, queue = [];
        waitFor(() => { return ikariam.controller; }).then((n) => {
            if(n.ajaxResponder===null){n.ajaxResponder=ikariam.getClass(ajax.Responder);}
            resp = n.ajaxResponder; while(queue.length) queue.shift()[0](resp);
        }).catch(() => { while(queue.length) queue.shift()[1](resp); });
        return function() {
            return new Promise((ok, ko) => { if (resp) { ok(resp); } else { queue.push([ok, ko]); } });
        };
    }(waitFor));

    const IkaTweaks = {};
    (function(IkaTweaks){

        var coreData = jsonDecode(LS.load('Core'), {});
        function coreDataSave() {
            LS.save('Core', JSON.stringify(coreData));
        }

        const mainViewIcon = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/mainViewIcon.png';
        var enabledModules = jsonDecode(LS.load('modules'), {});
        var definedModules = {};
        var sidebarButtons = [];

        function loadModule(modId) {
            var modData = jsonDecode(LS.load(modId), {});
            definedModules[modId].func(modData, function() {
                LS.save(modId, JSON.stringify(modData));
            });
        }

        injectCSS(`
            #IkaTweaks_sidebar1 .centerButton .button {width:200px;margin:2px 0px;}
            #IkaTweaks_sidebar2 .centerButton .button {width:200px;margin:2px 0px;}
            #IkaTweaks_c:before,
            #UpdateChecker_c:before {
                content: url('${mainViewIcon}');
            }
            /** extend h3 header **/
            .contentBox01h .header.hidden {
                overflow: hidden;
                height: 0px;
                padding-top: 7px;
            }
        `);

        IkaTweaks.setModule = function(modId, modFunc) {
            if (typeof enabledModules[modId] == 'undefined') enabledModules[modId] = false;
            definedModules[modId] = {
                name: `{str_${modId}_name}`,
                info: `{str_${modId}_info}`,
                func: modFunc,
            };
            if (enabledModules[modId]) {
                loadModule(modId);
            }
            // window icon
            injectCSS(`#${modId}_c:before { content: url('${mainViewIcon}'); }`);
        };

        IkaTweaks.addSidebarButton = function(btnText, btnFunc, useTop) {
            sidebarButtons.push({text:btnText,func:btnFunc,useTop:useTop});
        };

        IkaTweaks.success = function(t) {
            if (!t) {
                t = LANG('str_success'+random(0,4));
            }
            ikariam.controller.ajaxResponder.provideFeedback([{ location: 1, text: t, type: 10 }]);
        };
        IkaTweaks.warning = function(t) {
            ikariam.controller.ajaxResponder.provideFeedback([{ location: 1, text: t, type: 11 }]);
        };
        IkaTweaks.notice = function(t) {
            ikariam.controller.ajaxResponder.provideFeedback([{ location: 1, text: t, type: 12 }]);
        };

        IkaTweaks.changeHTML = function(id, html) {
            return new Promise((ok, ko) => {
                waitFor.ajaxResponder().then((ajaxResponder) => {
                    html = TPL.get('IkaTweaksFrame', {
                        version : GM_info.script.version,
                        mainview: html,
                        buttons1: TPL.each(sidebarButtons, function(btn, i){
                            return (!btn.useTop) ? null : ['IkaTweaksSidebar_button', {
                                btnId: 'IkaTweaksSidebar_button'+i,
                                btnText: btn.text
                            }];
                        }),
                        buttons2: TPL.each(sidebarButtons, function(btn, i){
                            return (btn.useTop) ? null : ['IkaTweaksSidebar_button', {
                                btnId: 'IkaTweaksSidebar_button'+i,
                                btnText: btn.text
                            }];
                        }),
                    });

                    html = TPL.parse(html, LANG());
                    ajaxResponder.changeHTML([id,html], true);
                    setTimeout(ikariam.controller.adjustSizes, 1000);

                    var sidebarButtonsLength = sidebarButtons.length;
                    for(var i=0; i<sidebarButtonsLength; i++) {
                        $('#IkaTweaksSidebar_button'+i).click(sidebarButtons[i].func);
                    }

                    $('table.table01 tr').not(':even').addClass('alt');
                    ikariam.controller.replaceCheckboxes();
                    ikariam.controller.replaceDropdownMenus();
                    ok();
                }).catch(ko);
            });
        };

        TPL.set('IkaTweaksFrame', `
            <div class="dynamic" id="IkaTweaks_sidebar1">
                <h3 class="header">{str_IkaTweaks} {version}</h3>
                <div class="content"><div class="centerButton">{buttons1}</div></div>
                <div class="footer"></div>
            </div>
            <div class="dynamic" id="IkaTweaks_sidebar2">
                <h3 class="header">{str_modules}</h3>
                <div class="content"><div class="centerButton">{buttons2}</div></div>
                <div class="footer"></div>
            </div>
            {mainview}
        `);

        TPL.set('IkaTweaksSidebar_button', `
            <button id="{btnId}" class="button">{btnText}</button>
        `);

        TPL.set('IkaTweaks_tabbedWindow', `
            <div id="mainview">
                <div class="buildingDescription"><h1>{str_IkaTweaks_menu}</h1></div>
                <ul class="tabmenu">
                    <li class="tab" id="js_tab_IkaTweaks_modulesWindow"><b>{str_IkaTweaks_tabModules}</b></li>
                    <li class="tab" id="js_tab_IkaTweaks_aboutWindow"><b>{str_IkaTweaks_tabAbout}</b></li>
                    <li class="tab" id="js_tab_IkaTweaks_versionWindow"><b>{str_IkaTweaks_version}</b></li>
                    <li class="tab" id="js_tab_IkaTweaks_changelogWindow"><b>{str_IkaTweaks_changelog}</b></li>
                </ul>
                <div>
                    {mainviewContent}
                </div>
            </div>
        `);

        TPL.set('IkaTweaks_modulesWindow', `
            <div class="contentBox01h" style="z-index: 101;">
                <h3 class="header">{str_IkaTweaks_tabModules}</h3>
                <div class="content">
                    <table class="table01"><tbody>
                        <tr>
                            <th style="width:50px;">{str_enabled}</th>
                            <th style="width:200px;">{str_name}</th>
                            <th>{str_description}</th>
                        </tr>
                        {modulesTR}
                        <tr>
                            <th colspan="3">
                                <div class="centerButton">
                                    <input id="js_IkaTweaks_saveModulesButton" type="button" class="button" value="{str_save}" />
                                </div>
                            </th>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
        `);

        TPL.set('IkaTweaks_modulesTR', `
            <tr class="{trClass}">
                <td><input id="IkaTweaksMainview_Checkbox{modId}" type="checkbox" class="notifications checkbox" {checked}></td>
                <td>{modName}</td>
                <td>{modInfo}</td>
            </tr>
        `);

        TPL.set('IkaTweaks_aboutWindow', `
            <div class="contentBox01h" style="z-index: 101;">
                <div class="header" style="height:0px;"></div>
                <div class="content">
                    <table class="table01"><tbody>
                        <tr>
                            <th style="width:150px;">{select}</th>
                            <th class="left">
                                <input id="js_IkaTweaks_saveLanguageButton" style="width:150px;" type="button" class="button" value="{str_saveLanguage}" />
                            </th>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
            <div class="contentBox01h" style="z-index: 101;">
                <h3 class="header">{str_IkaTweaks_aboutHeader}</h3>
                <div class="content">
                    <table class="table01"><tbody>
                        <tr>
                            <td colspan="2" class="center">
                                {str_IkaTweaks_aboutText2}
                                <div class="centerButton">
                                    <a id="js_IkaTweaks_openGreasyForkButton" class="button">{str_ToGreasyForkText}</a>
                                    <a id="js_IkaTweaks_openOpenUserJSButton" class="button">{str_ToOpenUserJSText}</a>
                                    <a id="js_IkaTweaks_openGitHubRepoButton" class="button">{str_ToGitHubRepoText}</a>
                                </div>
                            </td>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
            <div class="contentBox01h" style="z-index: 101;">
                <div class="header" style="height:0px;"></div>
                <div class="content">
                    <table class="table01"><tbody>
                        <tr>
                            <th style="min-width:150px;"><input id="js_IkaTweaks_clearStorageButton" style="min-width:150px;" type="button" class="button" value="{str_ClearStorageText}" /></th>
                            <th class="left">{str_ClearStorageInfo}</th>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
            <div class="contentBox01h" style="z-index: 101;">
                <h3 class="header">{str_IkaTweaks_creditsHeader}</h3>
                <div class="content">
                    <table class="table01 left"><tbody>
                        <tr>
                            <td>
                                {str_IkaTweaks_aboutCredits}
                            </td>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
        `);

        TPL.set('IkaTweaks_versionWindow', `
            <div class="contentBox01h">
                <h3 class="header">{str_IkaTweaks_version}</h3>
                <div class="content">
                    <table class="table01 left"><tbody>
                        {settingsTR}
                        <tr>
                            <th colspan="2">
                                <div class="centerButton">
                                    <input id="js_IkaTweaks_saveVersionSettingsButton" type="button" class="button" value="{str_save}" />
                                </div>
                            </th>
                        </tr>
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
            <div class="contentBox01h">
                <h3 class="header hidden"></h3>
                <div class="content">
                    <table class="table01" id="IkaTweaks_newVersionsTable">
                        <tr>
                            <th class="left" style="width:150px;">
                                <input id="js_IkaTweaks_versionCheckButton" style="width:150px;" type="button" class="button" value="{str_versionForceCheck}" />
                            </th>
                            <th class="left">
                                {str_versionLastCheckResult}: {lastResult}
                            </th>
                        </tr>
                    </table>
                </div>
                <div class="footer"></div>
            </div>
            <div class="contentBox01h" style="z-index: 101;display:none;" id="js_IkaTweaks_newVersionLinks">
                <h3 class="header">{str_versionGetNewestHere}</h3>
                <div class="content">
                    <table class="table01">
                        <tr>
                            <th class="center">
                                <div class="centerButton">
                                    <a id="js_IkaTweaks_openGreasyForkButton" class="button">{str_ToGreasyForkText}</a>
                                    <a id="js_IkaTweaks_openOpenUserJSButton" class="button">{str_ToOpenUserJSText}</a>
                                    <a id="js_IkaTweaks_openGitHubRepoButton" class="button">{str_ToGitHubRepoText}</a>
                                </div>
                            </th>
                        </tr>
                    </table>
                </div>
                <div class="footer"></div>
            </div>
        `);

        TPL.set('IkaTweaks_settingTR', `
            <tr>
                <td width="50"><input id="IkaTweaks_settingCheckbox{id}" type="checkbox" class="checkbox" {checked}></td>
                <td>{text}</td>
            </tr>
        `);

        TPL.set('IkaTweaks_changelogWindow', `
            <div class="contentBox01h">
                <h3 class="header">{str_IkaTweaks_changelog}</h3>
                <div class="content">
                    <table class="table01" id="js_IkaTweaks_changelogTable">

                    </table>
                </div>
                <div class="footer"></div>
            </div>
        `);

        if(typeof coreData.versionLastCheck     == 'undefined') coreData.versionLastCheck   = 0;
        if(typeof coreData.versionAutoCheck     == 'undefined') coreData.versionAutoCheck   = false;
        var versionNow = false;
        var versionNew = false;
        var versionCheckResult = '-';
        (function(v){
            versionNow = [
                parseInt(v[0]), // major
                parseInt(v[1]), // minor
            ];
        })(GM_info.script.version.match(/\d+/g));

        function versionCheck(onSuccess, onError) {
            var image = new Image();
            image.onload = function(){
                var versionChk = [ image.width-1, image.height-1 ];
                if ((versionChk[0] > versionNow[0]) || (versionChk[0] === versionNow[0] && versionChk[1] > versionNow[1])) {
                    versionCheckResult = '{str_versionNewAvailable}: v'+versionChk.join('.');
                    versionNew = versionChk;
                    onSuccess(true);
                } else {
                    versionCheckResult = '{str_versionUpToDate}';
                    onSuccess(false);
                }
            };
            image.onerror = onError;
            image.src = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/versions/versionImage.gif';
            coreData.versionLastCheck = timestamp();
            coreDataSave();
        }

        function versionAutoCheck() {
            if (coreData.versionAutoCheck) {
                if (coreData.versionLastCheck < timestamp()-86400) {
                    versionCheck(function(result) {
                        if (result) {
                            alert('IkaTweaks: ' + versionCheckResult);
                        }
                    });
                }
                setTimeout(versionAutoCheck, 3600);
            }
        }
        setTimeout(versionAutoCheck, 3600);
        versionAutoCheck();

        function versionShowNewList(cb) {
            var table = $('#IkaTweaks_newVersionsTable');
            var curMajor = versionNow[0];
            var curMinor = versionNow[1]+1;
            var newMajor = versionNew[0];
            var newMinor = versionNew[1];
            if (newMajor > curMajor) {
                curMajor = newMajor;
                curMinor = 0;
            }
            function loop() {
                var image = new Image();
                image.onload = function(){
                    table.prepend($('<tr></tr>').append($('<td colspan="2" class="left"></td>').append(image)));
                    curMinor++;
                    loop();
                };
                image.onerror = function(){
                    $('#js_IkaTweaks_newVersionLinks').show();
                    ikariam.controller.adjustSizes();
                    if (cb) cb();
                };
                image.src = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/versions/version'+curMajor+'.'+curMinor+'.png';
            }
            loop();
        }

        function changelogShowList(cb) {
            var table = $('#js_IkaTweaks_changelogTable');
            var curMajor = versionNow[0];
            var curMinor = 0;
            function loop() {
                var image = new Image();
                image.onload = function(){
                    table.prepend($('<tr></tr>').append($('<td colspan="2" class="left"></td>').append(image)));
                    curMinor++;
                    loop();
                };
                image.onerror = cb;
                image.src = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/versions/version'+curMajor+'.'+curMinor+'.png';
            }
            loop();
        }












        var tutorialHintInterval;
        var showSettingsWindow, showAboutWindow;
        var showVersionWindow, showChangelogWindow;

        showSettingsWindow = function(){
            IkaTweaks.changeHTML('IkaTweaks', TPL.get('IkaTweaks_tabbedWindow', {
                mainviewContent: TPL.get('IkaTweaks_modulesWindow', {
                    modulesTR: TPL.each(definedModules, (mod, modId) => {
                        return ['IkaTweaks_modulesTR', {
                            modId   : modId,
                            modName : mod.name,
                            modInfo : mod.info,
                            checked : enabledModules[modId] ? 'checked="checked"' : '',
                        }];
                    }),
                }),
            })).then(() => {
                $('#js_tab_IkaTweaks_modulesWindow').click(showSettingsWindow);
                $('#js_tab_IkaTweaks_aboutWindow').click(showAboutWindow);
                $('#js_tab_IkaTweaks_versionWindow').click(showVersionWindow);
                $('#js_tab_IkaTweaks_changelogWindow').click(showChangelogWindow);
                $('#js_tab_IkaTweaks_modulesWindow').addClass('selected');
                $('#js_IkaTweaks_saveModulesButton').click(function(){
                    var l = {};
                    forEach(definedModules, (_, modId) => {
                        l[modId] = isChecked('#IkaTweaksMainview_Checkbox'+modId);
                    });
                    enabledModules = l;
                    LS.save('modules', JSON.stringify(enabledModules));
                    LS.save('reopenSettingWindow', '1');
                    location.reload();
                });
                if(document.getElementById('js_IkaTweaks_tutorialArrow')) {
                    clearInterval(tutorialHintInterval);
                    LS.save('tutorialDone', '1');
                    $('#js_IkaTweaks_tutorialArrow').fadeOut();
                }
            }).catch(() => {});
        };

        showAboutWindow = function(){

            var LangSelect = new SelectDropDown('IkaTweaks_Languages', 175, LANG.codes(), LANG.local());

            IkaTweaks.changeHTML('IkaTweaks', TPL.get('IkaTweaks_tabbedWindow', {
                mainviewContent: TPL.get('IkaTweaks_aboutWindow', {
                    select  : LangSelect.tpl(),
                }),
            })).then(() => {
                $('#js_tab_IkaTweaks_modulesWindow').click(showSettingsWindow);
                $('#js_tab_IkaTweaks_aboutWindow').click(showAboutWindow);
                $('#js_tab_IkaTweaks_versionWindow').click(showVersionWindow);
                $('#js_tab_IkaTweaks_changelogWindow').click(showChangelogWindow);
                $('#js_tab_IkaTweaks_aboutWindow').addClass('selected');
                $('#js_IkaTweaks_saveLanguageButton').click(function(){
                    LS.save('LANG', LangSelect.val());
                    LS.save('reopenSettingWindow', '1');
                    location.reload();
                });
                $('#js_IkaTweaks_clearStorageButton').click(function(){
                    if(LS.clear()) {
                        LS.save('reopenSettingWindow', '1');
                        location.reload();
                    }
                });
                $('#js_IkaTweaks_openGreasyForkButton').attr({
                    'href': _LINKS_.GreasyFork,
                    'target': '_blank',
                });
                $('#js_IkaTweaks_openOpenUserJSButton').attr({
                    'href': _LINKS_.OpenUserJS,
                    'target': '_blank',
                });
                $('#js_IkaTweaks_openGitHubRepoButton').attr({
                    'href': _LINKS_.GitHubRepo,
                    'target': '_blank',
                });
                $('#myEmail').attr('style', 'position:absolute;width:82px;height:14px;display:inline-block;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFIAAAAOCAYAAAC8YEsXAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAADP0lEQVR42mL8//8/wyigHAAEEAuMEaor8A7KTAfi1WSYFQrEaUDsCsRKUH4nCfrLgdgFiI2h/LNAvAfNjA4oXQHEu4F4FpluJQRAYSFEwK8zQYzVlz+A1QEEEAuSpCDUgWfJtPws1GPIHiYWnIEG4Cxo4DFAI6MDGriuSGIwMIsCtxICgkT4tRPZnwABxISmACR5D8qeCY0ZEF6FpEYJ6nGYeAc0EAShtDHU8y7QVAaLQZieu1A+DOyG0oxQM8qhZtyDRqwSkjnIwBhJfSia+2YiqUF2qxKeFAZTV44lp9yFysEC7h56bgMIICYcBq+CZtNOKA5FMuQMUqAbI2VJF6gedKAENQ8Wi2eRIgYW8CbQADWGqlFCss8VRwpPg9opiOb5NKg5sAi/h2TmGSLc14EWwB3Q4qMTas8qbAEGEECMsMoGWEb+h6YKWCzsQco6yA46g6QOBP5DUw5MnxCSZWFYyktjJDNWIZWFIDFlqD1K0FQA47/DYu47qJmr0dTC3CMIDWgTNLeaoBUJHWjqkN13Buq2CixyYPOAZSSYDRBAuFLkPaij/0MxrAIAWfgeTe17AuXJPaj+d1CzkFOFCzQw0qAOvoekB5kWJGD+PaSUyICUAo2R/PAfyU70FHkPrfxDlitH0n8GlyMAAghf1t4DjSVGpJrxHhZPESqYO5BSJCNaakY24z2anntI2YtQpTILGpBpSGrfQ/3AiIRNsLQk3qP5QQlNrgKLGRgAIICY8DgOlrWRC/PVaLVyBxE1nyCecuoeUkCFIqWAciSxVdAmGT7QCbUnDSmgZqFVeOVQu5WwRAKyOuQycDVSZQqT243NAQABxITHYbCs3QH1lBJS+QRL7ml4mgcuUIfDPPkfWpa9RypvVkPN6oRG3F0kPiwQYeWsEhFNEuTIPgtNTR1I/qiARl45tKjBpg45Z1RAzTsDlXPBFakAAYSrsiGUVSsIFOCkNn73IHkSWzHjQqCBPFAAXtkABBATFkcbE9CcBk05q6CB8J7ChrEJNMXeRTJ3FVIqEMRVLg0gMEZvBgEEEBNaeUCsx1cjlS+UppR70LaiMlLPiAGpsnPFkVIHA4CHGUAAMY4OWlAHAAQYAH37+Z/ovMxFAAAAAElFTkSuQmCC);');
                $('#myEmail').after('<span style="width:82px;height:1px;display:inline-block;"></span>');
                $('#creditUrl1').attr({
                    'href': _LINKS_.OnePiece,
                    'target': '_blank',
                }).html(_LINKS_.OnePiece);

            }).catch(() => {});
        };

        showVersionWindow = function(){
            var checkboxes = {
                versionAutoCheck    : coreData.versionAutoCheck,
            };
            IkaTweaks.changeHTML('IkaTweaks', TPL.get('IkaTweaks_tabbedWindow', {
                mainviewContent: TPL.get('IkaTweaks_versionWindow', {
                    lastResult: versionCheckResult,
                    settingsTR: TPL.each(checkboxes, (checked, k) => {
                        return ['IkaTweaks_settingTR', {
                            id      : k,
                            text    : '{str_'+k+'}',
                            checked : (checked) ? 'checked="checked"' : '',
                        }];
                    }),
                }),
            })).then(() => {
                $('#js_tab_IkaTweaks_modulesWindow').click(showSettingsWindow);
                $('#js_tab_IkaTweaks_aboutWindow').click(showAboutWindow);
                $('#js_tab_IkaTweaks_versionWindow').click(showVersionWindow);
                $('#js_tab_IkaTweaks_changelogWindow').click(showChangelogWindow);
                $('#js_tab_IkaTweaks_versionWindow').addClass('selected');

                $('#js_IkaTweaks_openGreasyForkButton').attr({
                    'href': _LINKS_.GreasyFork,
                    'target': '_blank',
                });
                $('#js_IkaTweaks_openOpenUserJSButton').attr({
                    'href': _LINKS_.OpenUserJS,
                    'target': '_blank',
                });
                $('#js_IkaTweaks_openGitHubRepoButton').attr({
                    'href': _LINKS_.GitHubRepo,
                    'target': '_blank',
                });

                $('#js_IkaTweaks_versionCheckButton').click(function(){
                    $('#js_IkaTweaks_versionCheckButton').attr('disabled', 'disabled');
                    versionCheck(function(result){
                        showVersionWindow();
                        IkaTweaks.success();
                    },function(){
                        IkaTweaks.warning(LANG('str_UpdateChecker_checkFailed'));
                    });
                });
                $('#js_IkaTweaks_saveVersionSettingsButton').click(function(){
                    forEach(checkboxes, (_, k) => {
                        coreData[k] = isChecked('#IkaTweaks_settingCheckbox'+k);
                    });
                    coreDataSave();
                    IkaTweaks.success();
                });
                if (versionNew) {
                    versionShowNewList();
                }
            }).catch(() => {});
        };

        showChangelogWindow = function(){
            var checkboxes = {
                versionAutoCheck    : coreData.versionAutoCheck,
            };
            IkaTweaks.changeHTML('IkaTweaks', TPL.get('IkaTweaks_tabbedWindow', {
                mainviewContent: TPL.get('IkaTweaks_changelogWindow', {
                }),
            })).then(() => {
                $('#js_tab_IkaTweaks_modulesWindow').click(showSettingsWindow);
                $('#js_tab_IkaTweaks_aboutWindow').click(showAboutWindow);
                $('#js_tab_IkaTweaks_versionWindow').click(showVersionWindow);
                $('#js_tab_IkaTweaks_changelogWindow').click(showChangelogWindow);
                $('#js_tab_IkaTweaks_changelogWindow').addClass('selected');
                changelogShowList(() => {
                    ikariam.controller.adjustSizes();
                });
            }).catch(() => {});
        };

        waitFor(() => {
            return ($ !== undefined);
        }).then(() => {
            $('#GF_toolbar ul li.options').after($('<li></li>').append($('<a>',{
                'id'   : 'IkaTweaksToolbarButton',
                'click': function(){ showSettingsWindow();return false; },
                'href' : '#',
                'class': 'noViewParameters',
                'title': 'IkaTweaks',
                'text' : 'IkaTweaks'
            })));
            var tutorialDone = LS.load('tutorialDone');
            var showArrow = !tutorialDone;
            switch(LS.load('version', false)) {
                case false:
                    if (tutorialDone) {
                        alert(`IkaTweaks: Important information!\n\nThis new version contains a lot of changes and bug fixes. Please open the menu and check your settings.\nSorry for the inconveniences and thanks for using (and supporting) IkaTweaks =)`);
                        showArrow = true;
                    }
                    break;
            }
            LS.save('version', GM_info.script.version);
            if(showArrow) {
                var e=20;
                var arrow = $('<div>').attr({
                    'id': 'js_IkaTweaks_tutorialArrow',
                    'class': 'aniArrow',
                    'style': 'top:'+e+'px;display:block;',
                }).appendTo($('#IkaTweaksToolbarButton'))[0];
                var arrowCounter=0;
                tutorialHintInterval = setInterval(function(){
                    arrow.style.top = (e+(Math.sin(++arrowCounter/15)*10))+'px';
                }, 10);
            }
            if(LS.load('reopenSettingWindow')) {
                LS.drop('reopenSettingWindow');
                showSettingsWindow();
            }
        }).catch(() => {});

        IkaTweaks.addSidebarButton('{str_IkaTweaks_menu}', showSettingsWindow, true);

    })(IkaTweaks);

    // CORE
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // MODULE: GeneralTweaks

    IkaTweaks.setModule('GeneralTweaks', function(modData, modDataSave){

        // bug fixes
        if(typeof modData.fixResearchWindowList     == 'undefined') modData.fixResearchWindowList       = true;
        if(typeof modData.fixCitySelectItems        == 'undefined') modData.fixCitySelectItems          = true;
        if(typeof modData.fixWindowTopPadding       == 'undefined') modData.fixWindowTopPadding         = true;
        if(typeof modData.fixScrollbars             == 'undefined') modData.fixScrollbars               = true;
        if(typeof modData.fixHoverEffectSwitching   == 'undefined') modData.fixHoverEffectSwitching     = true;
        if(typeof modData.fixDarkSelectActiveColor  == 'undefined') modData.fixDarkSelectActiveColor    = true;
        if(typeof modData.fixWindowTabSizes         == 'undefined') modData.fixWindowTabSizes           = true;
        if(typeof modData.removeBlueLinkBorders     == 'undefined') modData.removeBlueLinkBorders       = true;


        // anti ads
        if(typeof modData.adsHideSpeedUpButtons     == 'undefined') modData.adsHideSpeedUpButtons       = modData.adsHideSpeedUpButton || false;
        if(typeof modData.adsHideWindowAds          == 'undefined') modData.adsHideWindowAds            = true;
        if(typeof modData.adsHideHappyHour          == 'undefined') modData.adsHideHappyHour            = false;
        if(typeof modData.adsHideOfferBoxes         == 'undefined') modData.adsHideOfferBoxes           = true;
        if(typeof modData.adsHideSlotResourceShop   == 'undefined') modData.adsHideSlotResourceShop     = false;
        if(typeof modData.adsHideSlotPremiumTrader  == 'undefined') modData.adsHideSlotPremiumTrader    = false;
        if(typeof modData.adsHideArchiveButtons     == 'undefined') modData.adsHideArchiveButtons       = false;
        if(typeof modData.adsHideMilitaryDummies    == 'undefined') modData.adsHideMilitaryDummies      = false;
        if(typeof modData.adsHideAdvisorButtons     == 'undefined') modData.adsHideAdvisorButtons       = false;
        if(typeof modData.adsHideToolbarMMOAds      == 'undefined') modData.adsHideToolbarMMOAds        = false;

        // animations
        if(typeof modData.animHideWalkers           == 'undefined') modData.animHideWalkers         = false;
        if(typeof modData.animHideBirdsOnly         == 'undefined') modData.animHideBirdsOnly       = false;
        if(typeof modData.animHideWalkerBubbles     == 'undefined') modData.animHideWalkerBubbles   = false;

        // cities
        if(typeof modData.cityIgnoreCapital             == 'undefined') modData.cityIgnoreCapital           = false;
        if(typeof modData.cityHidePirateFortress        == 'undefined') modData.cityHidePirateFortress      = false;
        if(typeof modData.cityHideLockedPosition        == 'undefined') modData.cityHideLockedPosition      = false;
        if(typeof modData.cityUseCustomBackground       == 'undefined') modData.cityUseCustomBackground     = false;
        if(typeof modData.cityCustomBackground          == 'undefined') modData.cityCustomBackground        = 0;
        if(typeof modData.cityHideDailyTasks            == 'undefined') modData.cityHideDailyTasks          = false;
        if(typeof modData.cityHideRegistrationGifts     == 'undefined') modData.cityHideRegistrationGifts   = false;
        if(typeof modData.cityHideFlyingShop            == 'undefined') modData.cityHideFlyingShop          = true;
        if(typeof modData.cityHideAmbrosiaFountain      == 'undefined') modData.cityHideAmbrosiaFountain    = true;

        // ressources
        if(typeof modData.resShowMissing                == 'undefined') modData.resShowMissing              = modData.ressShowMissing || true;
        if(typeof modData.resShowRemaining              == 'undefined') modData.resShowRemaining            = modData.ressShowRemaining || false;
        if(typeof modData.warehouseShowRemainingSave    == 'undefined') modData.warehouseShowRemainingSave  = false;

        // interactive
        if(typeof modData.cityHoverToBackground         == 'undefined') modData.cityHoverToBackground       = modData.fixHoverToBackground || false;
        if(typeof modData.islandHoverToBackground       == 'undefined') modData.islandHoverToBackground     = modData.fixHoverToBackground || false;
        if(typeof modData.actShowMouseoverPlaques       == 'undefined') modData.actShowMouseoverPlaques     = false;
        if(typeof modData.actHideMouseoverTitles        == 'undefined') modData.actHideMouseoverTitles      = false;
        if(typeof modData.actPreventPlaqueScaling       == 'undefined') modData.actPreventPlaqueScaling     = false;

        // fleets
        if(typeof modData.fleetHideSpeedColumn          == 'undefined') modData.fleetHideSpeedColumn        = false;
        if(typeof modData.fleetHidePremiumFleetInfo     == 'undefined') modData.fleetHidePremiumFleetInfo   = false;
        if(typeof modData.fleetShowCapacities           == 'undefined') modData.fleetShowCapacities         = false;
        if(typeof modData.fleetDontBreakLines           == 'undefined') modData.fleetDontBreakLines         = false;
        if(typeof modData.fleetReadableFilterButtons    == 'undefined') modData.fleetReadableFilterButtons  = false;
        if(typeof modData.fleetHideZeroFilterButtons    == 'undefined') modData.fleetHideZeroFilterButtons  = false;
        if(typeof modData.fleetMouseOverFleetInfo      == 'undefined') modData.fleetMouseOverFleetInfo    = false;




        ///////////////////////////////////////////////////////////////////////

        const CITY_BACKGROUNDS_COUNT = 5;
        const CITY_BACKGROUNDS = [
            {
                'nw': '//gf2.geo.gfsrv.net/cdn1b/1e250328264c77f3d5d2de7176bf3b.jpg',
                'ne': '//gf1.geo.gfsrv.net/cdnc1/552b032dccb6186776fa0a8e7aff38.jpg',
                'sw': '//gf1.geo.gfsrv.net/cdn36/d1c51f8791b8dd42887b4f4cab84a5.jpg',
                'se': '//gf2.geo.gfsrv.net/cdna2/bc0dc662a07cc16cdd6622f599a7cb.jpg',
            },
            {
                'nw': '//gf1.geo.gfsrv.net/cdn01/29c74235f5eff480c7f7c205e644fe.jpg',
                'ne': '//gf3.geo.gfsrv.net/cdn54/ee1e1655ebd0b1a7e5c0bc584824a4.jpg',
                'sw': '//gf1.geo.gfsrv.net/cdnf7/07f4d3bb04d1cfc0ec565aee163ed2.jpg',
                'se': '//gf2.geo.gfsrv.net/cdn14/0aee9fee17624ef1322ee6f6133309.jpg',
            },
            {
                'nw': '//gf1.geo.gfsrv.net/cdn3b/2b919d92c79b0b9a80cefafe88ef58.jpg',
                'ne': '//gf3.geo.gfsrv.net/cdne1/4d56d4e51fefd7cd46ece81109f119.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdn10/df064c1633744b1c33cc4573575891.jpg',
                'se': '//gf3.geo.gfsrv.net/cdnba/1c46d69d1413178b52ac9a82f32048.jpg',
            },
            {
                'nw': '//gf3.geo.gfsrv.net/cdn88/88e5a1eb809101da2282719be49d7e.jpg',
                'ne': '//gf2.geo.gfsrv.net/cdn42/7345451d8a2b1184ef6b6df6878267.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdn43/a6f005720cf4571d51aebaa2437036.jpg',
                'se': '//gf3.geo.gfsrv.net/cdn81/cd248b5153bee36d2308765f7894bd.jpg',
            },
            {
                'nw': '//gf2.geo.gfsrv.net/cdn1a/3d1c1893f5c157a63e54560d9c1604.jpg',
                'ne': '//gf2.geo.gfsrv.net/cdn44/462ee215d7a6c299758d0c30aa48bc.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdna6/44fb3d605e47d91f1b978788be16ed.jpg',
                'se': '//gf2.geo.gfsrv.net/cdn40/c52fc8f2b70787f27a15b4f62e323c.jpg',
            },
        ];

        const CITY_BACKGROUNDS_CAPITAL = [
            {
                'nw': '//gf2.geo.gfsrv.net/cdn1b/1e250328264c77f3d5d2de7176bf3b.jpg',
                'ne': '//gf2.geo.gfsrv.net/cdn1b/2a9c139ad689ed5a5838d1b5d65bb5.jpg',
                'sw': '//gf1.geo.gfsrv.net/cdn36/d1c51f8791b8dd42887b4f4cab84a5.jpg',
                'se': '//gf3.geo.gfsrv.net/cdnea/94eaf99ed5ac493a18ed532df203fa.jpg',
            },
            {
                'nw': '//gf1.geo.gfsrv.net/cdn01/29c74235f5eff480c7f7c205e644fe.jpg',
                'ne': '//gf3.geo.gfsrv.net/cdnbd/5316ff26044f7fa39c37e905024309.jpg',
                'sw': '//gf1.geo.gfsrv.net/cdnf7/07f4d3bb04d1cfc0ec565aee163ed2.jpg',
                'se': '//gf3.geo.gfsrv.net/cdne2/d2f889c380847628d9d843e0a787d3.jpg',
            },
            {
                'nw': '//gf1.geo.gfsrv.net/cdn3b/2b919d92c79b0b9a80cefafe88ef58.jpg',
                'ne': '//gf3.geo.gfsrv.net/cdn80/3972d1d22db3b9b49e584948275208.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdn10/df064c1633744b1c33cc4573575891.jpg',
                'se': '//gf2.geo.gfsrv.net/cdnd2/b069fd71423e719623114c24f6f507.jpg',
            },
            {
                'nw': '//gf2.geo.gfsrv.net/cdn41/4aff5cf60ff96eb23c895080f236f1.jpg',
                'ne': '//gf2.geo.gfsrv.net/cdnd6/65c6ef4f57cc4467d7a91c6ab34c88.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdn43/a6f005720cf4571d51aebaa2437036.jpg',
                'se': '//gf3.geo.gfsrv.net/cdned/09edd1df297346b09f7de8a6c021f4.jpg',
            },
            {
                'nw': '//gf2.geo.gfsrv.net/cdnd6/875162f980bf06afcbc2dd3af6d23c.jpg',
                'ne': '//gf1.geo.gfsrv.net/cdn01/eeba3ad6b113d71ef18fb594070641.jpg',
                'sw': '//gf2.geo.gfsrv.net/cdna6/44fb3d605e47d91f1b978788be16ed.jpg',
                'se': '//gf2.geo.gfsrv.net/cdn46/12f80c826b9a4f841f3bd0d49df1d3.jpg',
            },
        ];

        const UNIT_WEIGHTS = {
            ship_transport: -500,

            slinger: 3,
            archer: 5,
            marksman: 5,
            spearman: 3,
            swordsman: 3,
            phalanx: 5,
            steamgiant: 15,
            gyrocopter: 15,
            bombardier: 30,
            ram: 30,
            catapult: 30,
            mortar: 30,
            cook: 20,
            medic: 10,
            spartan: 5,

            wood: 1,
            wine: 1,
            marble: 1,
            glass: 1,
            sulfur: 1,
        };






        function actRescalePlaques() {
            switch(ikariam.backgroundView.id) {

                case 'city':
                    if (modData.actPreventPlaqueScaling) {
                        var s = 1/ikariam.worldview_scale_city;
                        $('.timetofinish').each(function() {
                            $(this).css('transform', 'scale(' + s + ')');
                        });
                    } else {
                        $('.timetofinish').each(function() {
                            $(this).css('transform', '');
                        });
                    }
                    break;

                case 'island':
                    if (modData.actPreventPlaqueScaling) {
                        var s = 1/ikariam.worldview_scale_island;
                        $('#island .location_list .cityLocationScroll').each(function() {
                            $(this).css('transform', 'scale(' + s + ')');
                        });
                    } else {
                        $('#island .location_list .cityLocationScroll').each(function() {
                            $(this).css('transform', '');
                        });
                    }
                    break;

            }
        }

        function actOnMouseover() {
            switch(ikariam.backgroundView.id) {

                case 'city':
                    var title = $(this).attr('title');
                    if (modData.actShowMouseoverPlaques) {
                        var pos = forceInt($(this).attr('id'));
                        $('#js_CityPosition'+pos+'Scroll').css('display', 'block');
                        $('#js_CityPosition'+pos+'Scroll').css('pointer-events', 'none');
                        $('#js_CityPosition'+pos+'ScrollName').text(title);
                        $(this)[0]._tweakInterval = setInterval(() => {
                            $('#js_CityPosition'+pos+'ScrollName').text(title);
                        }, 1000);
                    }
                    if (modData.actHideMouseoverTitles) {
                        $(this).attr('title', '');
                        $(this).attr('_title', title);
                    }
                    break;

                case 'island':
                    if (modData.actHideMouseoverTitles) {
                        var title = $(this).attr('title');
                        $(this).attr('title', '');
                        $(this).attr('_title', title);
                    }
                    break;

            }
        }

        function actOnMouseout() {
            switch(ikariam.backgroundView.id) {

                case 'city':
                    var pos = forceInt($(this).attr('id'));
                    $('#js_CityPosition'+pos+'Scroll').css('display', '');
                    $('#js_CityPosition'+pos+'Scroll').css('pointer-events', '');
                    var title = $(this).attr('title') || $(this).attr('_title');
                    $(this).attr('title', title);
                    $(this).attr('_title', null);
                    if (modData.fixHoverEffectSwitching) {
                        $('#position'+pos+' .img_pos.hover').addClass('invisible');
                    }
                    clearInterval($(this)[0]._tweakInterval);
                    $(this)[0]._tweakInterval = null;
                    break;

                case 'island':
                    var title = $(this).attr('title') || $(this).attr('_title');
                    $(this).attr('title', title);
                    $(this).attr('_title', null);
                    if (modData.fixHoverEffectSwitching) {
                        $(this).prev().removeClass('invisible');
                    }
                    break;

            }
        }

        function actSetHoverHandlers() {
            switch(ikariam.backgroundView.id) {

                case 'city':
                    if (modData.actShowMouseoverPlaques
                     || modData.actHideMouseoverTitles
                     || modData.fixHoverEffectSwitching) {
                        $('#city #locations .building a:not([_tweaked])').each(function() {
                            $(this).on('mouseover', actOnMouseover);
                            $(this).on('mouseout', actOnMouseout);
                            $(this).attr('_tweaked', '1');
                        });
                    } else {
                        $('#city #locations .building a[_tweaked]').each(function() {
                            $(this).off('mouseover');
                            $(this).off('mouseout');
                            $(this).attr('_tweaked', null);
                            actOnMouseout.call(this);
                        });
                    }
                    break;

                case 'island':
                    if (modData.actHideMouseoverTitles
                     || modData.fixHoverEffectSwitching) {
                        $('#island a.island_feature_img:not([_tweaked])').each(function() {
                            $(this).on('mouseover', actOnMouseover);
                            $(this).on('mouseout', actOnMouseout);
                            $(this).attr('_tweaked', '1');
                        });
                        $('#island_heliosLocation a:not([_tweaked])').each(function() {
                            $(this).on('mouseover', actOnMouseover);
                            $(this).on('mouseout', actOnMouseout);
                            $(this).attr('_tweaked', '1');
                        });
                    } else {
                        $('#island a.island_feature_img[_tweaked]').each(function() {
                            $(this).off('mouseover');
                            $(this).off('mouseout');
                            $(this).attr('_tweaked', null);
                            actOnMouseout.call(this);
                        });
                        $('#island_heliosLocation a[_tweaked]').each(function() {
                            $(this).off('mouseover');
                            $(this).off('mouseout');
                            $(this).attr('_tweaked', null);
                            actOnMouseout.call(this);
                        });
                    }
                    break;

            }
        }

        function updateRessourcesCosts() {
            if (modData.ressShowMissing || modData.ressShowRemaining) {
                var res = {
                    'wood':'resource',
                    'wine':'1',
                    'marble':'2',
                    'glass':'3',
                    'sulfur':'4',
                };
                $('#buildingUpgrade ul.resources li').each(function(i){
                    var t = $(this);
                    forEach(res, (v, k) => {
                        if (t.hasClass(k)) {
                            var req = parseInt(t.html().replace(/\D+/g,''));
                            var cur = ikariam.model.currentResources[v];
                            var lft = req-cur;
                            if (lft>0) {
                                if (modData.ressShowMissing) {
                                    t.addClass('red bold').css({'line-height':'initial'});
                                    t.append('<span style="display:block;font-weight:normal;font-size:10px;">-'+ikariam.model.shortenValue(lft,6)+'</span>');
                                }
                            } else {
                                if(modData.ressShowRemaining) {
                                    t.css({'line-height':'initial'});
                                    t.append('<span class="green" style="display:block;font-weight:normal;font-size:10px;">+'+ikariam.model.shortenValue(lft*-1,6)+'</span>');
                                }
                            }
                            return;
                        }
                    });
                });
            }
        }

        function updateFleetMovements() {
            if (document.getElementById('militaryAdvisor')) {

//http://gf3.geo.gfsrv.net/cdnb4/c8d5abf2d65cd8386593aced475cae.js

                if (!$('#militaryMovementsFleetMovementsFilters')[0].hasAttribute('_onclick')) {
                    $('#militaryMovementsFleetMovementsFilters').attr('_onclick', '');
                    $('#militaryMovementsFleetMovementsFilters > div').on('click', updateFleetMovements);
                }

                $('#militaryMovementsFleetMovementsFilters').hide();
                waitFor(() => {
                    return ikariam.templateView.viewScripts.militaryAdvisor.militaryMovementsFleetMovements;
                }).then((m) => {
                    $('#militaryMovementsFleetMovementsFilters').show();

                    //hookFunction(m, 'selectFilter', updateFleetMovements);

                    if (modData.fleetHideZeroFilterButtons) {
                        $('#militaryMovementsFleetMovementsFilters div.amount').each(function(){
                            if (!parseInt($(this).text())) {
                                $(this).parent().addClass('hidden');
                            }
                        });
                    }

                    if (modData.fleetHidePremiumFleetInfo) {
                        $('table.military_event_table td.spyMilitary.magnify_icon').each(function(){
                            var t = $(this);
                            if (t.find('.unit_detail_icon').length === 0) {
                                t.css('opacity', 0);
                                t.css('cursor', 'unset');
                                t.attr('onclick', null);
                            }
                        });
                    }

                    if (modData.fleetShowCapacities) {

                        var ignoreClasses = ['unit_detail_icon','floatleft','icon40','bold','center','resource_icon'];
                        $('table.military_event_table tr.own').each(function(){
                            var tr = $(this);
                            var space = 0;
                            var used = 0;
                            tr.find('div.unit_detail_icon').each(function(){
                                var icon = $(this);
                                var cls = icon[0].className.split(' ').filter((s) => {
                                    return ignoreClasses.indexOf(s) === -1;
                                }).join().trim();
                                if (cls) {
                                    var weight = UNIT_WEIGHTS[cls];
                                    if (weight) {
                                        weight *= forceInt(icon.text());
                                        if (weight < 0) {
                                            space -= weight;
                                        } else {
                                            used += weight;
                                        }
                                    } else {
                                        // SHOULD NOT HAPPEN !!!
                                    }
                                } else {
                                    // SHOULD NOT HAPPEN !!!
                                }
                            });
                            if (space) {
                                $('td:nth-child(4)', this).append(`<div>${used} / ${space}</div>`);
                            }
                        });
                    }

                    if (modData.fleetMouseOverFleetInfo) {
                        $('table.military_event_table tr:first-child th[colspan="2"]').attr('colspan', null);
                        function triggerOver() {
                            $(document).trigger("closeExclusiveInfo");
                            var id = $(this).closest('tr').attr('data-fleet-id');
                            ikariam.controller.clickTarget = this;
                            ikariam.controller.actionMonitor.toggleExclusiveInfo('fleetInfo'+id);
                        }
                        function triggerOut() {
                            $('#exclusiveTooltip').remove();
                        }
                        $('table.military_event_table tr:not([data-fleet-id]) td.spyMilitary.magnify_icon').each(function(){
                            var td = $(this);
                            var tr = td.closest('tr');
                            var prev = td.prev();
                            if (td[0].hasAttribute('onclick')) {
                                tr.attr('data-fleet-id', td.attr('onclick').match(/fleetInfo(\d+)/)[1]);
                                prev.on("mouseover", triggerOver);
                            } else {
                                tr.attr('data-fleet-id', '');
                            }
                            prev.on("mouseout", triggerOut);
                            td.css('display', 'none');
                            $('div', prev).css('pointer-events', 'none');
                            prev.attr('title', null);
                        });
                    }

                }).catch(() => {});
            }
        }

        function updateWarehouseInfos() {
            if (document.getElementById('warehouse')) {

                if (modData.warehouseShowRemainingSave) {
                    var res = [ 'wood', 'wine', 'marble', 'glass', 'sulfur' ];
                    res.forEach((res) => {
                        var secure = forceInt($('#js_secure_'+res).text());
                        var plunderable = forceInt($('#js_plunderable_'+res).text());
                        var total = forceInt($('#js_total_'+res).text());
                        var capacity = forceInt($('#js_capacity_'+res).text());
                        if (!plunderable) {
                            var restSecure = secure - total;
                            $('#js_plunderable_'+res)[0].innerHTML = ikariam.model.shortenValue(restSecure, 6);
                            $('#js_plunderable_'+res).addClass('green');
                        }
                    });
                }
            }
        }

        waitFor.ajaxResponder().then((ajaxResponder) => {
            hookFunction(ajaxResponder, 'changeView', () => {
                updateRessourcesCosts();
                updateFleetMovements();
            });
            hookFunction(ajaxResponder, 'updateViewScriptData', updateFleetMovements);
            hookFunction(ajaxResponder, 'updateTemplateData', updateWarehouseInfos);
            updateRessourcesCosts();
            updateFleetMovements();
        }).catch(() => {});

        waitFor(() => {
            return dataSetForView.relatedCityData && ikariam.backgroundView.screen.update;
        }).then(() => {
            hookFunction(ikariam.backgroundView.screen, 'update', () => {
                actSetHoverHandlers();
                actRescalePlaques();
            });
            hookFunction(ikariam.controller, 'scaleWorldMap', actRescalePlaques);
            actSetHoverHandlers();
            actRescalePlaques();
            actRescalePlaques();
        }).catch(() => {});

        var cssElement;
        function updateCSS() {
            var css = [];

            //
            // bugFixes
            //
            if (modData.fixResearchWindowList) {
                css.push('ol.research_type { width: 220px !important; }');
            }
            if (modData.fixCitySelectItems) {
                css.push('.dropdownContainer.city_select li.last-child { background-position: left -142px !important; padding-bottom: 3px; }');
                css.push('.dropdownContainer.city_select li.last-child:hover{ background-position: left -196px !important; }');
            }
            if (modData.fixWindowTopPadding) {
                css.push('#container .mainContentBox .mainContent { padding-top: 20px !important; }');
            }
            if (modData.fixScrollbars) {
                /** fix scrollbar x position **/
                css.push('#container .scroller { background-position-x: -1px !important; }');
                /** fix scroll window margin top **/
                css.push('#container .mainContentBox .mainContentScroll { margin-top: -0.5px; }');
                /** fix scroll area margin right to parent view window **/
                css.push('#container .scroll_area { margin-right: 3px; }');
                css.push('#container .scroll_area.scroll_disabled { margin-right: 0px; }');
            }
            if (modData.fixDarkSelectActiveColor) {
                css.push('.select_container.city_select .dropDownButton.active a { color: #faeac6; }');
            }
            if (modData.fixWindowTabSizes) {
                css.push('#container .tabmenu .tab { border: 1px solid transparent; border-bottom: 0; }');
                css.push('#container .tabmenu .selected, #container .tabmenu .tab:hover { padding: 2px 3px 1px 3px !important; }');
            }
            if (modData.removeBlueLinkBorders) {
                css.push('a { border: 0; text-decoration: none; outline: none; }');
            }





            //
            // antiAds
            //
            if (modData.adsHideHappyHour) {
                css.push('div.btnIngameCountdown.happyHour  { height: 0; padding: 0; overflow: hidden; }'); // screw that display:block
            }
            if (modData.adsHideSpeedUpButtons) {
                css.push('#city #locations .timetofinish.buildingSpeedup { padding-right: 16px; pointer-events: none; }');
                css.push('#city .buildingSpeedupButton { display: none; }');
                css.push('#js_MilitaryMovementsFleetMovementsTable .action_icon.accLoading { display: none; }');
                css.push('#js_MilitaryMovementsFleetMovementsTable .table01 .right br { display: none; }');
            }
            if (modData.adsHideSlotResourceShop) {
                css.push('#container .slot_menu li.expandable.resourceShop { display: none !important; }');
            }
            if (modData.adsHideSlotPremiumTrader) {
                css.push('#container .slot_menu li[onclick^="ajaxHandlerCall(\'?view=premiumTrader\')"] { display: none !important; }');
            }
            if (modData.adsHideOfferBoxes) {

                css.push('#townHall .premiumOffer { display: none !important; }');
                css.push('#warehouse .premiumOfferBox { display: none !important; }');
                css.push('#finances #js_BadTaxAccountantOffer { display: none !important; }');
                css.push('#finances td.premiumOffer { height: unset; padding: unset; }');

                // premium boxes in mines
                css.push('#setWorkers .content { min-height: unset !important; }');
                css.push('#setWorkers #workersWrapper + .premiumOfferBox { display: none !important; }');

                // bottom premium boxes in advisor views
                css.push('#tab_tradeAdvisor > div.contentBox01h:last-child { display: none !important; }');
                css.push('#militaryAdvisor #militaryMovements + div.contentBox01h { display: none !important; }');
                css.push('#researchAdvisor .mainContent > div.contentBox01h:last-child { display: none !important; }');
                css.push('#diplomacyAdvisor #tab_diplomacyAdvisor > div:last-child.contentBox01h { display: none !important; }');
            }
            if (modData.adsHideWindowAds) {
                css.push('#container .mainContent > div:first-child.center { display: none !important; }');     // advertising at top of each view window
            }
            if (modData.adsHideArchiveButtons) {
                css.push('#tab_diplomacyAdvisor .button_bar_msg { display: none !important; }');
                css.push('#tab_diplomacyAdvisor .button_bar_right { display: none !important; }');
            }
            if (modData.adsHideMilitaryDummies) {
                css.push('#barracks #premium_btn { display: none !important; }');
                css.push('#barracks #premium_btn2 { display: none !important; }');
                css.push('#shipyard #premium_btn { display: none !important; }');
                css.push('#shipyard #premium_btn2 { display: none !important; }');
                css.push('#cityMilitary #premium_btn { display: none !important; }');
                css.push('#cityMilitary #premium_btn2 { display: none !important; }');
            }
            if (modData.adsHideAdvisorButtons) {
                css.push('#header #advisors a#js_GlobalMenu_citiesPremium     { display: none; }');
                css.push('#header #advisors a#js_GlobalMenu_militaryPremium   { display: none; }');
                css.push('#header #advisors a#js_GlobalMenu_researchPremium   { display: none; }');
                css.push('#header #advisors a#js_GlobalMenu_diplomacyPremium  { display: none; }');
            }
            if (modData.adsHideToolbarMMOAds) {
                css.push('#GF_toolbar ul li:last-child  { display: none; }');
            }

            //
            // animations
            //
            if (modData.animHideWalkers) {
                css.push('#walkers { display: none; }');
            }
            if (modData.animHideBirdsOnly) {
                css.push('div.bird, div.animated_bird { display: none; }');
            }
            if (modData.animHideWalkerBubbles) {
                css.push('.not_selectable, .animation {pointer-events: none;}');
            }

            //
            // cities
            //
            if (modData.cityHideLockedPosition) {
                css.push('#locations .lockedPosition        { display: none; }');
            }
            if (modData.cityHideDailyTasks) {
                css.push('#city #cityDailyTasks             { display: none; }');
            }
            if (modData.cityHideRegistrationGifts) {
                css.push('#cityRegistrationGifts            { display: none; }');
            }
            if (modData.cityHideFlyingShop) {
                css.push('#city #cityFlyingShopContainer    { display: none; }');
            }
            if (modData.cityHideAmbrosiaFountain) {
                css.push('#city #cityAmbrosiaFountain       { display: none; }');
            }
            if (modData.cityHidePirateFortress) {
                css.push('#city #pirateFortressBackground { display: none; }');
                css.push('#city #pirateFortressShip       { display: none; }');
                css.push('#city #locations #position17    { display: none; }');
            }
            if (modData.cityIgnoreCapital || modData.cityUsecityCustomBackground) {

                var townBackgrounds = [];
                var capitalBackgrounds = [];
                if (modData.cityUseCustomBackground) {
                    for(var i=0; i<CITY_BACKGROUNDS_COUNT; i+=1) {
                        townBackgrounds.push(CITY_BACKGROUNDS[modData.cityCustomBackground]);
                        capitalBackgrounds.push(CITY_BACKGROUNDS_CAPITAL[modData.cityCustomBackground]);
                    }
                } else {
                    for(var i=0; i<CITY_BACKGROUNDS_COUNT; i+=1) {
                        townBackgrounds.push(CITY_BACKGROUNDS[i]);
                        capitalBackgrounds.push(CITY_BACKGROUNDS_CAPITAL[i]);
                    }
                }
                if (modData.cityIgnoreCapital) {
                    capitalBackgrounds = townBackgrounds;
                }

                for(var i=0; i<CITY_BACKGROUNDS_COUNT; i+=1) {
                    css.push('.phase'+(i+1)+' #city_background_nw{background-image:url('+townBackgrounds[i].nw+')}');
                    css.push('.phase'+(i+1)+' #city_background_ne{background-image:url('+townBackgrounds[i].ne+')}');
                    css.push('.phase'+(i+1)+' #city_background_sw{background-image:url('+townBackgrounds[i].sw+')}');
                    css.push('.phase'+(i+1)+' #city_background_se{background-image:url('+townBackgrounds[i].se+')}');
                    css.push('.phase'+(i+1)+'.isCapital #city_background_nw{background-image:url('+capitalBackgrounds[i].nw+')}');
                    css.push('.phase'+(i+1)+'.isCapital #city_background_ne{background-image:url('+capitalBackgrounds[i].ne+')}');
                    css.push('.phase'+(i+1)+'.isCapital #city_background_sw{background-image:url('+capitalBackgrounds[i].sw+')}');
                    css.push('.phase'+(i+1)+'.isCapital #city_background_se{background-image:url('+capitalBackgrounds[i].se+')}');
                }
            }

            //
            // fleets
            //
            if (modData.fleetHideSpeedColumn) {
                css.push('#js_MilitaryMovementsFleetMovementsTable table tr th:nth-child(3) { display: none; }');
                css.push('#js_MilitaryMovementsFleetMovementsTable table tr td:nth-child(3) { display: none; }');
            }
            if (modData.fleetDontBreakLines) {
                css.push('#js_MilitaryMovementsFleetMovementsTable table { white-space: nowrap; }');
            }
            if (modData.fleetReadableFilterButtons) {
                css.push('#militaryMovementsFleetMovementsFilters { background-color: #faeac6; height: 44px; margin-right: 3.5px; padding: 5px 9px 0; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter:not(.selected) { opacity: 0.5; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter:not(.selected):hover { opacity: 0.75; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter > div.mission_icon.all { background-color: #fff8d7; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter > div.amount { background: #532000; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter > div.amount { color: #fff8d7; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter { cursor: pointer; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter { margin-right: 0; }');
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter > div.amount { margin-top: -2px; line-height: 14px; }');
            }
            if (modData.fleetHideZeroFilterButtons) {
                css.push('#militaryMovementsFleetMovementsFilters > div.missionFilter.hidden { display: none; }');
            }
            if (modData.miliHidePremiumFleetInfo) {
                css.push('#exclusiveTooltip { pointer-events: none; }');
            }







            //
            // interactive
            //
            if (modData.actHideMouseoverTitles) {
                css.push('#island #cities .hover { pointer-events: none; }');
            }
            if (modData.cityHoverToBackground) {
                css.push('#city #locations .img_pos.buildingimg { z-index: 2; pointer-events: none; }');
                css.push('#city #locations .img_pos.constructionSite { z-index: 2; pointer-events: none; }');
                //css.push('#island a.island_feature_img { z-index: 3; position: absolute; }');
            }
            if (modData.islandHoverToBackground) {
                css.push('#island #cities .hover { position: absolute; z-index: -1; }');
                css.push('#island #islandtradegood .hover { z-index: 0; }');
                css.push('#island #islandtradegood .hover { z-index: 0; }');
                css.push('#island #islandtradegood a { position: absolute; z-index: 1; }');
            }








            if(cssElement) removeElement(cssElement);
            injectCSS(css.join(''), function(el){cssElement=el;});
        }
        updateCSS();





        var categories = [
            ['bugFixes', [
                'fixResearchWindowList',
                'fixCitySelectItems',
                'fixWindowTopPadding',
                'fixScrollbars',
                'fixHoverEffectSwitching',
                'fixDarkSelectActiveColor',
                'fixWindowTabSizes',
                'removeBlueLinkBorders',
            ]],
            ['antiAds', [
                'adsHideSpeedUpButtons',
                'adsHideWindowAds',
                'adsHideHappyHour',
                'adsHideOfferBoxes',
                'adsHideSlotResourceShop',
                'adsHideSlotPremiumTrader',
                'adsHideArchiveButtons',
                'adsHideMilitaryDummies',
                'adsHideAdvisorButtons',
                'adsHideToolbarMMOAds',
            ]],
            ['animations', [
                'animHideWalkers',
                'animHideWalkerBubbles',
                'animHideBirdsOnly',
            ]],
            ['cities', [
                'cityIgnoreCapital',
                'cityHidePirateFortress',
                'cityHideLockedPosition',
                'cityUseCustomBackground',
                'cityHideDailyTasks',
                'cityHideRegistrationGifts',
                'cityHideFlyingShop',
                'cityHideAmbrosiaFountain',
            ]],
            ['interactive', [
                'actShowMouseoverPlaques',
                'actHideMouseoverTitles',
                'actPreventPlaqueScaling',
                'cityHoverToBackground',
                'islandHoverToBackground',
            ]],
            ['resources', [
                'resShowMissing',
                'resShowRemaining',
                'warehouseShowRemainingSave',
            ]],
            ['fleets', [
                'fleetHideSpeedColumn',
                'fleetHidePremiumFleetInfo',
                'fleetShowCapacities',
                'fleetDontBreakLines',
                'fleetReadableFilterButtons',
                'fleetHideZeroFilterButtons',
                'fleetMouseOverFleetInfo',
            ]],
        ];


        TPL.set('GeneralTweaks_settingsWindow', `
            <div id="mainview">
                <div class="buildingDescription"><h1>{str_GeneralTweaks_name}</h1></div>
                <div>
                    {categories}
                    <div class="contentBox01h" style="z-index: 101;">
                        <h3 class="header hidden"></h3>
                        <div class="content">
                            <table class="table01 left"><tbody>
                                <tr>
                                    <th colspan="2">
                                        <div class="centerButton">
                                            <input id="js_GeneralTweaks_settingsButton" type="button" class="button" value="{str_save}" />
                                        </div>
                                    </th>
                                </tr>
                            </tbody></table>
                        </div>
                        <div class="footer"></div>
                    </div>
                </div>
            </div>
        `);

        TPL.set('GeneralTweaks_category', `
            <div class="contentBox01h" style="z-index: 101;">
                <h3 class="header">{categoryTitle}</h3>
                <div class="content">
                    <table class="table01 left"><tbody>
                        {categoryRows}
                    </tbody></table>
                </div>
                <div class="footer"></div>
            </div>
        `);

        TPL.set('GeneralTweaks_settingTR', `
            <tr>
                <td width="50"><input id="GeneralTweaks_settingCheckbox{id}" type="checkbox" class="notifications checkbox" {checked}></td>
                <td>{text}</td>
            </tr>
        `);

        TPL.set('GeneralTweaks_settingSelectTR', `
            <tr>
                <td width="50" style="vertical-align: top;"><input id="GeneralTweaks_settingCheckbox{id}" type="checkbox" class="notifications checkbox" {checked}></td>
                <td>{text} {selectContainer}</td>
            </tr>
        `);

        IkaTweaks.addSidebarButton('{str_GeneralTweaks_name}', function() {

            var cityCustomBackgroundList = [ '1', '2', '3', '4', '5' ].map((level) => {
                return TPL.parse(LANG('str_GeneralTweaks_cityUseCustomBackgroundLevel'), {level:level});
            });
            var cityCustomBackgroundSelect = new SelectDropDown('GeneralTweaks_customBackgroundSelect', 95, cityCustomBackgroundList, modData.cityCustomBackground);

            IkaTweaks.changeHTML('GeneralTweaks', TPL.get('GeneralTweaks_settingsWindow', {
                categories: TPL.each(categories, function(category){
                    var categoryId = category[0];
                    var checkboxes = category[1];
                    return ['GeneralTweaks_category', {
                        categoryTitle: '{str_GeneralTweaks_'+categoryId+'}',
                        categoryRows:  TPL.each(checkboxes, function(k){
                            switch(k) {
                                case 'cityUseCustomBackground':
                                    return ['GeneralTweaks_settingSelectTR', {
                                        id      : k,
                                        text    : '{str_GeneralTweaks_'+k+'}',
                                        checked : (modData[k]) ? 'checked="checked"' : '',
                                        selectContainer : cityCustomBackgroundSelect.tpl(),
                                    }];
                                default:
                                    return ['GeneralTweaks_settingTR', {
                                        id      : k,
                                        text    : '{str_GeneralTweaks_'+k+'}',
                                        checked : (modData[k]) ? 'checked="checked"' : '',
                                    }];
                            }
                        })
                    }];
                })
            })).then(() => {
                $('#js_GeneralTweaks_settingsButton').click(function(){
                    function forEachCheckBox(k) {
                        modData[k] = isChecked('#GeneralTweaks_settingCheckbox'+k);
                    }
                    categories.forEach((category) => {
                        var categoryId = category[0];
                        var checkboxes = category[1];
                        checkboxes.forEach(forEachCheckBox);
                    });
                    modData.cityCustomBackground = parseInt(cityCustomBackgroundSelect.val());
                    modDataSave();
                    updateCSS();
                    actRescalePlaques();
                    actSetHoverHandlers();
                    IkaTweaks.success();
                });
            }).catch(() => {});
        });

    });

    // MODULE: GeneralTweaks
    //--------------------------------------------------------------------------------------------------

    //--------------------------------------------------------------------------------------------------
    // MODULE: CityListing

    IkaTweaks.setModule('CityListing', function(modData, modDataSave){
        if(typeof modData.hideCoords        == 'undefined') modData.hideCoords          = false;
        if(typeof modData.showTradegoods    == 'undefined') modData.showTradegoods      = true;
        if(typeof modData.highlightSelected == 'undefined') modData.highlightSelected   = true;
        if(typeof modData.sortList          == 'undefined') modData.sortList            = true;
        if(typeof modData.sortedList        == 'undefined') modData.sortedList          = [];

        // afterI = after what child ele append others?
        // chields = {cityId:listEle, cityId:listEle, ...}
        function sortElementChilds(parent, childs, afterI, altRows) {
            var sortedList = modData.sortedList.slice(0), ch;
            while(sortedList.length) {
                ch = childs[sortedList.pop()]; // from last, to fist
                if (ch) {
                    // remove element from DOM and append again at top
                    parent.children().eq(afterI).before(ch);
                }
            }
            if (altRows) {
                sortedList = modData.sortedList.slice(0);
                var alt = false;
                while(sortedList.length) {
                    ch = childs[sortedList.shift()]; // but now from first to last
                    if (ch) {
                        ch.removeClass('alt');
                        if (alt) {
                            ch.addClass('alt');
                        }
                        alt = !alt;
                    }
                }
            }
        }

        var tradegoodImagesCache = {};
        var tradegoodImagePaths = {
            '1': '/skin/resources/icon_wine.png',
            '2': '/skin/resources/icon_marble.png',
            '3': '/skin/resources/icon_glass.png',
            '4': '/skin/resources/icon_sulfur.png'
        };
        function getTradegoodImage(k, cityId, tradegood) {
            var cacheKey = k+cityId;
            var img = tradegoodImagesCache[cacheKey] || $('<img>');
            tradegoodImagesCache[cacheKey] = img;
            img.attr({
                'class': 'citySelectTradegoodIcon',
                'src': tradegoodImagePaths[tradegood] || '',
            });
            return img;
        }

        function updateCitySelect_appendTradegoods() {
            var relatedCityData = ikariam.model.relatedCityData, relatedCity = relatedCityData[relatedCityData.selectedCity];
            if (relatedCity.relationship == 'ownCity') {
                $('#js_citySelectContainer span').append(getTradegoodImage('span', relatedCity.id, relatedCity.tradegood));
            }
            var citySelectUl = $('#dropDown_js_citySelectContainer ul');
            citySelectUl.addClass('width'+citySelectUl.width());
            forEach(relatedCityData, (relatedCity, cityKey) => {
                if (relatedCity && relatedCity.relationship == 'ownCity' && relatedCity.tradegood) {
                    $('#dropDown_js_citySelectContainer li[selectvalue="'+relatedCity.id+'"]').append(getTradegoodImage('li', relatedCity.id, relatedCity.tradegood));
                }
            });
        }

        function updateCitySelect_highlightSelected() {
            var relatedCityData = ikariam.model.relatedCityData, relatedCity = relatedCityData[relatedCityData.selectedCity];
            $('#dropDown_js_citySelectContainer li[selectvalue="'+relatedCity.id+'"]').addClass('active');
        }

        function updateCitySelect_hideCoords() {
            var aSpan = $('#js_citySelectContainer span a');
            aSpan.text(aSpan.text().replace(/\[\d+\:\d+\]\s+/, ''));
            var aLi = $('#dropDown_js_citySelectContainer li a');
            aLi.each(function(){
                $(this).text($(this).text().replace(/\[\d+\:\d+\]\s+/, ''));
            });
        }

        function updateCitySelect_sort() {
            var parent = $('#dropDown_js_citySelectContainer ul');
            var childs = {};
            $('#dropDown_js_citySelectContainer ul li').each(function(i){
                childs[$(this).attr('selectvalue').match(/\d+/)] = $(this);
            });
            sortElementChilds(parent, childs, 0);
            // refresh classes for :first and :last
            (function(li){
                li.removeClass('first-child');
                li.removeClass('last-child');
                li.first().addClass('first-child');
                li.last().addClass('last-child');
            })(parent.children());
        }

        function updateCitySelect() {
            if(modData.showTradegoods)      updateCitySelect_appendTradegoods();
            if(modData.highlightSelected)   updateCitySelect_highlightSelected();
            if(modData.hideCoords)          updateCitySelect_hideCoords();
            if(modData.sortList)            updateCitySelect_sort();
        }

        function changeViewUpdate() {
            if(!(modData.sortList)) return;

            var unsortedIds = [];
            forEach(ikariam.model.relatedCityData, (relatedCity, cityKey) => {
                if (relatedCity && relatedCity.relationship == 'ownCity') {
                    unsortedIds.push(relatedCity.id);
                }
            });

            var parent, childs = {};
            if(document.getElementById('palace_c')) {
                parent = $('#palace_c table:eq(1) tbody');
                parent.find('tr:gt(0)').each(function(i){
                    childs[unsortedIds[i]] = $(this);
                });
                return sortElementChilds(parent, childs, 1, true);
            }
            if(document.getElementById('palaceColony_c')) {
                parent = $('#palaceColony_c table:eq(0) tbody');
                parent.find('tr').each(function(i){
                    childs[unsortedIds[i]] = $(this);
                });
                return sortElementChilds(parent, childs, 0);
            }
            if(document.getElementById('culturalPossessions_assign_c')) {
                parent = $('#culturalPossessions_assign_c ul:eq(1)');
                parent.find('li').each(function(i){
                    childs[unsortedIds[i]] = $(this);
                });
                return sortElementChilds(parent, childs, 0);
            }
            if(document.getElementById('finances_c')) {
                parent = $('#finances_c table:eq(1) tbody');
                parent.find('tr:gt(0)').each(function(i){
                    childs[unsortedIds[i]] = $(this);
                });
                return sortElementChilds(parent, childs, 1, true);
            }
            if(document.getElementById('port_c')) {
                parent = $('#port_c ul.cities');
                parent.find('li').each(function(i){
                    childs[$(this).attr('id').match(/\d+/)] = $(this);
                });
                return sortElementChilds(parent, childs, 0);
            }
        }

        waitFor(() => {
            jshintUnused = ikariam.backgroundView.updateCityDropdownMenu;
            jshintUnused = ikariam.model.relatedCityData;
            return true;
        }).then((ret) => {
            hookFunction(ikariam.backgroundView, 'updateCityDropdownMenu', updateCitySelect);
            updateCitySelect();
        }).catch(() => {});

        waitFor.ajaxResponder().then((ajaxResponder) => {
            hookFunction(ajaxResponder, 'changeView', changeViewUpdate);
            changeViewUpdate();
        }).catch(() => {});

        injectCSS(`
            img.citySelectTradegoodIcon {width:15px;height:12px;position:absolute;margin-top:5px;}
            ul.width177 img.citySelectTradegoodIcon {left:138px;}
            ul.width158 img.citySelectTradegoodIcon {left:138px;}
            #js_citySelectContainer span img.citySelectTradegoodIcon {left:146px;width:20px;height:16px;}
            #js_citySelectContainer span a {display:inline;}
            .dropdownContainer.city_select li.active{font-weight:bold;}
            #CityListing_sortingList button {cursor:pointer;display:inline-block;border:none;width:17px;height:17px;}
            #CityListing_sortingList button.up {background:url(/skin/friends/button17_up.png) no-repeat center 0px;}
            #CityListing_sortingList button.down {background:url(/skin/friends/button17_down.png) no-repeat center 0px;}
        `);

/*
        var cssElement;
        function updateCSS() {
            var css = [];

            if(cssElement) removeElement(cssElement);
            injectCSS(css.join(''), function(el){cssElement=el;});
        }
        updateCSS();
*/


        TPL.set('CityListing_settingsWindow', `
            <div id="mainview">
                <div class="buildingDescription"><h1>{str_CityListing_name}</h1></div>
                <div>
                    <div class="contentBox01h" style="z-index: 101;">
                        <h3 class="header hidden"></h3>
                        <div class="content">
                            <table class="table01 left"><tbody>
                                {settingsTR}
                                <tr>
                                    <th colspan="2">
                                        <div class="centerButton">
                                            <input id="js_CityListing_saveSettingsButton" type="button" class="button" value="{str_save}" />
                                        </div>
                                    </th>
                                </tr>
                            </tbody></table>
                        </div>
                        <div class="footer"></div>
                    </div>
                </div>
            </div>
        `);

        TPL.set('CityListing_settingTR', `
            <tr>
                <td width="50"><input id="CityListing_settingCheckbox{id}" type="checkbox" class="checkbox" {checked}></td>
                <td>{text}</td>
            </tr>
        `);
        TPL.set('CityListing_settingListTR', `
            <tr>
                <td width="50" rowspan="2" style="vertical-align:top;"><input id="CityListing_settingCheckbox{id}" type="checkbox" class="checkbox" {checked}></td>
                <td>{text}</td>
            </tr>
            <tr>
                <td style="padding-left: 0;">
                    <table><tbody id="CityListing_sortingList"></tbody></table>
                </td>
            </tr>
        `);

        IkaTweaks.addSidebarButton('{str_CityListing_name}', function(){

            var checkboxes = {
                hideCoords          : modData.hideCoords,
                showTradegoods      : modData.showTradegoods,
                highlightSelected   : modData.highlightSelected,
                sortList            : modData.sortList,
            };

            IkaTweaks.changeHTML('CityListing', TPL.get('CityListing_settingsWindow', {
                settingsTR: TPL.each(checkboxes, function(checked, k){
                    return [(k==='sortList'?'CityListing_settingListTR':'CityListing_settingTR'), {
                        id      : k,
                        text    : '{str_CityListing_'+k+'}',
                        checked : (checked) ? 'checked="checked"' : '',
                    }];
                }),
            })).then(() => {

                var CityListing_sortingList = $('#CityListing_sortingList');

                var relatedCity, relatedCityData = ikariam.model.relatedCityData;

                // add current ids of moddata
                var newSortedList = [];
                while (modData.sortedList.length) {
                    relatedCity = relatedCityData['city_'+modData.sortedList.shift()];
                    if (relatedCity && relatedCity.relationship == 'ownCity') {
                        if (newSortedList.indexOf(relatedCity.id) == -1) {
                            newSortedList.push(relatedCity.id);
                        }
                    }
                }

                // add missing ownCity ids of relatedCityData
                forEach(relatedCityData, (relatedCity, cityKey) => {
                    if (relatedCity && relatedCity.relationship == 'ownCity') {
                        if (newSortedList.indexOf(relatedCity.id) == -1) {
                            newSortedList.push(relatedCity.id);
                        }
                    }
                });

                function onClickSortUp(that) {
                    var tr = $(this).closest('tr'); // jshint ignore:line
                    var ch = CityListing_sortingList.children();
                    if(tr[0] === ch.first()[0]) ch.last().after(tr);
                    else tr.prev().before(tr);
                }

                function onClickSortDown() {
                    var tr = $(this).closest('tr'); // jshint ignore:line
                    var ch = CityListing_sortingList.children();
                    if(tr[0] === ch.last()[0]) ch.first().before(tr);
                    else tr.next().after(tr);
                }

                // fill #CityListing_sortingList
                while(newSortedList.length)
                {
                    var cityId = newSortedList.shift();
                    modData.sortedList.push(cityId);
                    CityListing_sortingList.append($('<tr>', {cityId:cityId})
                        .append($('<td>')
                            .append($('<button>', {'class':'up',    click:onClickSortUp}))
                            .append($('<button>', {'class':'down',  click:onClickSortDown}))
                        ).append($('<td>', {html:relatedCityData['city_'+cityId].name}))
                    );
                }

                $('#js_CityListing_saveSettingsButton').click(function(){
                    forEach(checkboxes, (_, k) => {
                        modData[k] = isChecked('#CityListing_settingCheckbox'+k);
                    });
                    modData.sortedList = [];
                    $('#CityListing_sortingList tr').each(function(){
                        modData.sortedList.push(parseInt($(this).attr('cityId')));
                    });
                    modDataSave();
                    ikariam.backgroundView.updateCityDropdownMenu();
                    IkaTweaks.success();
                });

            }).catch(() => {});

        });

    });

    // MODULE: CityListing
    //-----------------------------------------------------------------------------

    //-----------------------------------------------------------------------------
    // MODULE: ChangeAdvisors

    IkaTweaks.setModule('ChangeAdvisors', function(modData, modDataSave){
        if(typeof modData.replacements == 'undefined') modData.replacements = {cities:'maleMayor',military:'maleGeneral',research:'maleScientist',diplomacy:'maleDiplomat'};

        const advisors = [ 'cities', 'military', 'research', 'diplomacy' ];
        const advisorImages = {
            cities: {

                maleMayor: {
                    normal: '/skin/layout/advisors/mayor.png',
                    active: '/skin/layout/advisors/mayor_active.png',
                    mini  : '/skin/minimized/tradeAdvisor.png',
                    fixes : 'background-position-y:1px;',
                },
                maleMayorPremium: {
                    normal: '/skin/layout/advisors/mayor_premium.png',
                    active: '/skin/layout/advisors/mayor_premium_active.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/maleMayorPremiumMini.png',
                    fixes : 'background-position-y:1px;',
                },

                femaleMayor: {
                    normal: 'https://gf2.geo.gfsrv.net/cdnad/c65484ebe05e4218aa8af0a016f70f.png',
                    active: 'https://gf1.geo.gfsrv.net/cdn6b/8004819074813a1e74377e16bc39db.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/femaleMayorMini.png',
                },

                femaleMayorPremium: {
                    normal: 'https://gf2.geo.gfsrv.net/cdn43/438ff9314cd4594efbe0bd3cde2daa.png',
                    active: 'https://gf1.geo.gfsrv.net/cdn93/5ddaa2ef6569a1274842e5661aa7ec.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/femaleMayorPremiumMini.png',
                },

                barbarianMayor: {
                    normal: 'https://gf2.geo.gfsrv.net/cdndd/4038e23b2dee65425ff19452e43b60.png',
                    active: 'https://gf2.geo.gfsrv.net/cdnda/97f887e4a5e0dea4c974fe888adc07.png',
                    mini  : '/skin/minimized/tradeAdvisor.png',
                },
                barbarianMayorPremium: {
                    normal: 'https://gf1.geo.gfsrv.net/cdnf2/8cddf3b88425522f2cec8bf954ff67.png',
                    active: 'https://gf2.geo.gfsrv.net/cdn4e/0aca9eab9f2e921abc609bf18bc72e.png',
                    mini  : '/skin/minimized/tradeAdvisor.png',
                },

                onePieceLuffy: {
                    normal: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceLuffyNormal.png',
                    active: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceLuffyActive.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceLuffyMini.png',
                    fixes : 'background-position-y:1px;',
                },

            },
            military: {

                maleGeneral: {
                    normal: '/skin/layout/advisors/general.png',
                    active: '/skin/layout/advisors/general_active.png',
                    alert : '/skin/layout/advisors/general_alert.png',
                    mini  : '/skin/minimized/militaryAdvisor.png',
                    fixes : 'background-position-y:1px;',
                },
                maleGeneralPremium: {
                    normal: '/skin/layout/advisors/general_premium.png',
                    active: '/skin/layout/advisors/general_premium_active.png',
                    alert : '/skin/layout/advisors/general_premium_alert.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/maleGeneralPremiumMini.png',
                    fixes : 'background-position-y:1px;',
                },

                femaleGeneral: {
                    normal: 'https://gf1.geo.gfsrv.net/cdn93/de97d2e03efcb617b55e554872bd7f.png',
                    active: 'https://gf3.geo.gfsrv.net/cdn59/9945e4257ea5fa1115c780b61c6f87.png',
                    alert : 'https://gf2.geo.gfsrv.net/cdn4b/cb7e1b73321ca334edb3f7cde014a4.png',
                    mini  : '/skin/minimized/militaryAdvisor.png',
                }, 
                femaleGeneralPremium: {
                    normal: 'https://gf2.geo.gfsrv.net/cdn7d/846622eb7b6d22b4d8e9ed67fe9540.png',
                    active: 'https://gf3.geo.gfsrv.net/cdnbf/be172f27c65aa08dad2492c7a688dc.png',
                    alert : 'https://gf1.geo.gfsrv.net/cdnc9/300f3eb409c5547ecef988ee60ec63.png',
                    mini  : '/skin/minimized/militaryAdvisor.png',
                },

                barbarianGeneral: {
                    normal: 'https://gf3.geo.gfsrv.net/cdn2f/64face27ba8bb80615bc7ae1bf5e08.png',
                    active: 'https://gf2.geo.gfsrv.net/cdnde/7b6e769a93988aa4f305a8b17ca45c.png',
                    alert : 'https://gf2.geo.gfsrv.net/cdn1a/9696dae76e5ab16f4604fa311cb87d.png',
                    mini  : '/skin/minimized/militaryAdvisor.png',
                },
                barbarianGeneralPremium: {
                    normal: 'https://gf1.geo.gfsrv.net/cdnc5/f907938b1897e3791c4b99c56c26cd.png',
                    active: 'https://gf2.geo.gfsrv.net/cdna5/a14f7b05b3a33f47ff0a5689de6c94.png',
                    alert : 'https://gf3.geo.gfsrv.net/cdne6/76ef247198b1a3493b59d5fb8512da.png',
                    mini  : '/skin/minimized/militaryAdvisor.png',
                },

                onePieceZoro: {
                    normal: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceZoroNormal.png',
                    active: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceZoroActive.png',
                    alert : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceZoroAlert.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceZoroMini.png',
                    fixes : 'background-position-y:1px;',
                },

            },
            research: {

                maleScientist: {
                    normal: '/skin/layout/advisors/scientist.png',
                    active: '/skin/layout/advisors/scientist_active.png',
                    mini  : '/skin/minimized/researchAdvisor.png',
                    fixes : 'background-position-y:1px;',
                },
                maleScientistPremium: {
                    normal: '/skin/layout/advisors/scientist_premium.png',
                    active: '/skin/layout/advisors/scientist_premium_active.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/maleScientistPremiumMini.png',
                    fixes : 'background-position-y:1px;',
                },

                femaleScientist: {
                    normal: 'https://gf3.geo.gfsrv.net/cdn2c/718516ef80f6b471d46829db736645.png',
                    active: 'https://gf1.geo.gfsrv.net/cdnf6/195cc0fee967c828a16cbbe6771927.png',
                    mini  : '/skin/minimized/researchAdvisor.png',
                },
                femaleScientistPremium: {
                    normal: 'https://gf2.geo.gfsrv.net/cdn4e/b5ac699f00b625287c7cca2ee4113c.png',
                    active: 'https://gf3.geo.gfsrv.net/cdn22/17ad363fb799ac7813e8f66a99b865.png',
                    mini  : '/skin/minimized/researchAdvisor.png',
                },

                barbarianScientist: {
                    normal: 'https://gf3.geo.gfsrv.net/cdne0/568aff5ecbf5a5d9b24cc109fb40bc.png',
                    active: 'https://gf2.geo.gfsrv.net/cdn48/fd3a7d5df4256d5ee7792f3196ecbd.png',
                    mini  : '/skin/minimized/researchAdvisor.png',
                },

                barbarianScientistPremium: {
                    normal: 'https://gf2.geo.gfsrv.net/cdn7a/eb29418610d19bfd0e87574abbaaef.png',
                    active: 'https://gf3.geo.gfsrv.net/cdn8d/d2b95e6eeb9191f0a207adbe1d9994.png',
                    mini  : '/skin/minimized/researchAdvisor.png',
                },

                onePieceUsopp: {
                    normal: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceUsoppNormal.png',
                    active: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceUsoppActive.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceUsoppMini.png',
                    fixes : 'background-position-y:1px;',
                },

            },
            diplomacy: {

                maleDiplomat: {
                    normal: '/skin/layout/advisors/diplomat.png',
                    active: '/skin/layout/advisors/diplomat_active.png',
                    mini  : '/skin/minimized/diplomacyAdvisor.png',
                    fixes : 'background-position-y:1px;',
                },
                maleDiplomatPremium: {
                    normal: '/skin/layout/advisors/diplomat_premium.png',
                    active: '/skin/layout/advisors/diplomat_premium_active.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/maleDiplomatPremiumMini.png',
                    fixes : 'background-position-y:1px;',
                },

                femaleDiplomat: {
                    normal: 'https://gf2.geo.gfsrv.net/cdn79/34aae35dbef7ac0666cd5d4e795fec.png',
                    active: 'https://gf3.geo.gfsrv.net/cdn22/9183f61d09d24f5fae2394bc970bbf.png',
                    mini  : '/skin/minimized/diplomacyAdvisor.png',
                },
                femaleDiplomatPremium: {
                    normal: 'https://gf1.geo.gfsrv.net/cdn9c/f57b89fb383d7071aabaff522582a0.png',
                    active: 'https://gf1.geo.gfsrv.net/cdn32/70449a533dbc49e268644784739e29.png',
                    mini  : '/skin/minimized/diplomacyAdvisor.png',
                },

                barbarianDiplomat: {
                    normal: 'https://gf1.geo.gfsrv.net/cdn35/e6b1edfb47413ca3125ade683d12fe.png',
                    active: 'https://gf1.geo.gfsrv.net/cdn6e/a250bc22b3802efc5aa663c0c9e2e8.png',
                    mini  : '/skin/minimized/diplomacyAdvisor.png',
                },
                barbarianDiplomatPremium: {
                    normal: 'https://gf3.geo.gfsrv.net/cdn2d/6ed7dcb6cac8e54520f1019b8f5056.png',
                    active: 'https://gf2.geo.gfsrv.net/cdn7a/27a589adb2ace5d7067db7b954736d.png',
                    mini  : '/skin/minimized/diplomacyAdvisor.png',
                },

                onePieceNami: {
                    normal: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceNamiNormal.png',
                    active: 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceNamiActive.png',
                    mini  : 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/onePieceNamiMini.png',
                    fixes : 'background-position-y:1px;',
                },
            },
        };

        var advisorsStyleRules = `
            #header #advisors #advCities a      {{citiesFixes}}
            #header #advisors #advMilitary a    {{militaryFixes}}
            #header #advisors #advResearch a    {{researchFixes}}
            #header #advisors #advDiplomacy a   {{diplomacyFixes}}
            #header #advisors #advCities .normal            {background-image:url({citiesNormal})}
            #header #advisors #advCities .normalactive      {background-image:url({citiesActive})}
            #header #advisors #advCities .premium           {background-image:url({citiesNormal})}
            #header #advisors #advCities .premiumactive     {background-image:url({citiesActive})}
            #header #advisors #advMilitary .normal          {background-image:url({militaryNormal})}
            #header #advisors #advMilitary .normalactive    {background-image:url({militaryActive})}
            #header #advisors #advMilitary .normalalert     {background-image:url({militaryAlert})}
            #header #advisors #advMilitary .premium         {background-image:url({militaryNormal})}
            #header #advisors #advMilitary .premiumactive   {background-image:url({militaryActive})}
            #header #advisors #advMilitary .premiumalert    {background-image:url({militaryAlert})}
            #header #advisors #advResearch .normal          {background-image:url({researchNormal})}
            #header #advisors #advResearch .normalactive    {background-image:url({researchActive})}
            #header #advisors #advResearch .premium         {background-image:url({researchNormal})}
            #header #advisors #advResearch .premiumactive   {background-image:url({researchActive})}
            #header #advisors #advDiplomacy .normal         {background-image:url({diplomacyNormal})}
            #header #advisors #advDiplomacy .normalactive   {background-image:url({diplomacyActive})}
            #header #advisors #advDiplomacy .premium        {background-image:url({diplomacyNormal})}
            #header #advisors #advDiplomacy .premiumactive  {background-image:url({diplomacyActive})}
            #container #tradeAdvisor_c:before, #container #premiumTradeAdvisor_c:before, #container #tradeRoutes_c:before, #container #registrationGifts_c:before, #container #dailyTasks_c:before, #container #dailyTasksRewards_c:before, #container #premiumTradeAdvisorCitizens_c:before, #container #premiumTradeAdvisorBuildings_c:before {
                content: url({citiesMini});
            }
            #container #militaryAdvisor_c:before, #container #retreat_c:before, #container #premiumMilitaryAdvisor_c:before, #container #militaryAdvisorCombatList_c:before, #container #militaryAdvisorWarList_c:before, #container #militaryAdvisorOldWars_c:before, #container #militaryAdvisorOldAllyWars_c:before, #container #militaryAdvisorWarDetails_c:before, #container #militaryAdvisorAllyWarDetails_c:before {
                content: url({militaryMini});
            }
            #container #researchAdvisor_c:before, #container #researchDetail_c:before, #container #premiumResearchAdvisor_c:before {
                content: url({researchMini});
            }
            #container #diplomacyAdvisor_c:before, #container #diplomacyIslandBoard_c:before, #container #diplomacyAdvisorOutBox_c:before, #container #diplomacyAdvisorArchive_c:before, #container #diplomacyAdvisorArchiveOutBox_c:before, #container #diplomacyTreaty_c:before, #container #diplomacyAlly_c:before, #container #diplomacyAdvisorSearchUser_c:before, #container #diplomacyAlly_c:before, #container #premiumDiplomacyAdvisor_c:before, #container #ignoreList_c:before, #container #diplomacyAllyMemberlist_c:before, #container #diplomacyAllySearch_c:before, #container #diplomacyAllyInfo_c:before {
                content: url({diplomacyMini});
            }
        `;

        var cssElement;
        function updateCSS() {
            var css = [];
            var cities      = advisorImages.cities[modData.replacements.cities];
            var military    = advisorImages.military[modData.replacements.military];
            var research    = advisorImages.research[modData.replacements.research];
            var diplomacy   = advisorImages.diplomacy[modData.replacements.diplomacy];
            css.push(TPL.parse(advisorsStyleRules, {
                citiesNormal        : cities.normal,
                citiesActive        : cities.active,
                citiesMini          : cities.mini,
                citiesFixes         : cities.fixes||'',
                militaryNormal      : military.normal,
                militaryActive      : military.active,
                militaryAlert       : military.alert,
                militaryMini        : military.mini,
                militaryFixes       : military.fixes||'',
                researchNormal      : research.normal,
                researchActive      : research.active,
                researchMini        : research.mini,
                researchFixes       : research.fixes||'',
                diplomacyNormal     : diplomacy.normal,
                diplomacyActive     : diplomacy.active,
                diplomacyMini       : diplomacy.mini,
                diplomacyFixes      : diplomacy.fixes||'',
            }));
            if(cssElement) removeElement(cssElement);
            injectCSS(css.join(''), function(el){cssElement=el;});
        }
        updateCSS();

        TPL.set('ChangeAdvisors_settingsWindow', `
            <div id="mainview">
                <div class='buildingDescription'><h1>{str_ChangeAdvisors_name}</h1></div>
                <div>
                    <div class="contentBox01h" style="z-index: 101;">
                        <h3 class="header hidden"></h3>
                        <div class="content">
                            <table class="table01 left"><tbody>
                                <tr>
                                    <td>
                                        <table id="ChangeAdvisors_advisorSelectTable">
                                            {advisors}
                                        </table>
                                    </td>
                                </tr>
                                <tr>
                                    <th>
                                        <div class="centerButton">
                                            <input id="js_ChangeAdvisors_saveSettingsBtn" type="button" class="button" value="{str_save}" />
                                        </div>
                                    </th>
                                </tr>
                            </tbody></table>
                        </div>
                        <div class="footer"></div>
                    </div>
                </div>
            </div>
        `);

        TPL.set('ChangeAdvisors_advisorTR', `
            <tr>
                <td style="width:100px;">{text}</td>
                <td>{select}</td>
            </tr>
        `);

        IkaTweaks.addSidebarButton('{str_ChangeAdvisors_name}', function(){

            var AdvisorSelects = {};
            advisors.forEach((advisorId) => {
                var selectId = 'ChangeAdvisors_advisorSelect_'+advisorId;
                var options = {};
                Object.keys(advisorImages[advisorId]).forEach((key) => {
                    options[key] = `{str_ChangeAdvisors_${key}}`;
                });
                var selected = modData.replacements[advisorId];
                AdvisorSelects[advisorId] = new SelectDropDown(selectId, 300, options, selected);
            });

            IkaTweaks.changeHTML('ChangeAdvisors', TPL.get('ChangeAdvisors_settingsWindow', {
                advisors: TPL.each(advisorImages, function(advisorData, advisorId){
                    return ['ChangeAdvisors_advisorTR', {
                        text    : '{str_ChangeAdvisors_'+advisorId+'}',
                        select  : AdvisorSelects[advisorId].tpl(),
                    }];
                }),

            })).then(() => {
                $('#js_ChangeAdvisors_saveSettingsBtn').click(function(){
                    forEach(advisorImages, (_, advisorId) => {
                        modData.replacements[advisorId] = AdvisorSelects[advisorId].val();
                    });
                    modDataSave();
                    updateCSS();
                    IkaTweaks.success();
                });

                // preview
                var $img = $('<img>');
                $('<td>',{rowspan:4,width:90}).appendTo($('#ChangeAdvisors_advisorSelectTable tr').first()).append($img);
                function optionMove(event) {
                    var tar = $(event.target);
                    $img.attr('src', advisorImages[tar.closest('ul').attr('advisor')][tar.closest('li').attr('selectValue')].normal);
                }
                function optionEnter(event) {
                    $img.fadeIn();
                }
                function optionLeave(event) {
                    $img.fadeOut();
                }
                forEach(advisorImages, (advisorImageList, k) => {
                    $('#dropDown_js_ChangeAdvisors_advisorSelect_'+k+'Container ul').attr('advisor', k).mouseenter(optionEnter).mouseleave(optionLeave);
                    forEach(advisorImageList, (_, n) => {
                        $('#dropDown_js_ChangeAdvisors_advisorSelect_'+k+'Container li[selectValue="'+n+'"]').mousemove(optionMove);
                        $('#dropDown_js_ChangeAdvisors_advisorSelect_'+k+'Container li[selectValue="'+n+'"] a').mousemove(optionMove);
                    });
                });

            }).catch(() => {});
        });

    });

    // MODULE: ChangeAdvisors
    //-----------------------------------------------------------------------------

    //-----------------------------------------------------------------------------
    // MODULE: MoveBuildings

    IkaTweaks.setModule('MoveBuildings', function(modData, modDataSave){
        if(typeof modData.customPositions           == 'undefined') modData.customPositions         = {};

        var moveBuildingsBackground = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/moveBuildingsBackground.jpg';
        var moveBuildingsEmptyButton = 'https://raw.githubusercontent.com/YveOne/Userscript-IkaTweaks/master/images/moveBuildingsEmptyButton.png';

        const BUILDING_POSITIONS_COUNT = 20;
        function buildEmptyPositions() {
            return new Array(BUILDING_POSITIONS_COUNT).fill(null).map((v, i)=>(i));
        }

        injectCSS(`
            #MoveBuildings #buildingDetail .building_nav { height: 424px; /*width:674px;*/ overflow: visible; background: url(`+moveBuildingsBackground+`); }
            #MoveBuildings #buildingDetail .button_building { position: absolute; }
            #MoveBuildings #buildingDetail .button_building[position="0"]  {left: 299px;top: 169px;}
            #MoveBuildings #buildingDetail .button_building[position="1"]  {left: 236px;top: 293px;}
            #MoveBuildings #buildingDetail .button_building[position="2"]  {left: 381px;top: 270px;}
            #MoveBuildings #buildingDetail .button_building[position="3"]  {left: 440px;top: 198px;}
            #MoveBuildings #buildingDetail .button_building[position="4"]  {left: 354px;top: 205px;}
            #MoveBuildings #buildingDetail .button_building[position="5"]  {left: 282px;top: 222px;}
            #MoveBuildings #buildingDetail .button_building[position="6"]  {left: 204px;top: 212px;}
            #MoveBuildings #buildingDetail .button_building[position="7"]  {left: 142px;top: 192px;}
            #MoveBuildings #buildingDetail .button_building[position="8"]  {left: 135px;top: 143px;}
            #MoveBuildings #buildingDetail .button_building[position="9"]  {left: 197px;top: 161px;}
            #MoveBuildings #buildingDetail .button_building[position="10"] {left: 369px;top: 151px;}
            #MoveBuildings #buildingDetail .button_building[position="11"] {left: 440px;top: 131px;}
            #MoveBuildings #buildingDetail .button_building[position="12"] {left: 317px;top: 104px;}
            #MoveBuildings #buildingDetail .button_building[position="13"] {left: 258px;top: 123px;}
            #MoveBuildings #buildingDetail .button_building[position="14"] {left: 119px;top:  91px;}
            #MoveBuildings #buildingDetail .button_building[position="15"] {left: 168px;top: 271px;}
            #MoveBuildings #buildingDetail .button_building[position="16"] {left: 387px;top: 102px;}
            #MoveBuildings #buildingDetail .button_building[position="17"] {left: 385px;top: 352px;}
            #MoveBuildings #buildingDetail .button_building[position="18"] {left: 492px;top: 223px;}
            #MoveBuildings #buildingDetail .button_building[position="19"] {left: 492px;top:  54px;}
            #MoveBuildings .building_nav .button_building.groundShore,
            #MoveBuildings .building_nav .button_building.groundLand,
            #MoveBuildings .building_nav .button_building.groundLocked,
            #MoveBuildings .building_nav .button_building.groundWall,
            #MoveBuildings .building_nav .button_building.empty {
                background-image: url(`+moveBuildingsEmptyButton+`);
            }
            #MoveBuildings .building_nav .button_building.empty {
                background-position: -86px 0px;
            }
            #MoveBuildings .building_nav .button_building.empty.hover {
                background-position: -86px -41px;
            }
            #MoveBuildings .building_nav .button_building.groundShore,
            #MoveBuildings .building_nav .button_building.groundLand,
            #MoveBuildings .building_nav .button_building.groundWall {
                background-position: -43px 0px;
            }
            #MoveBuildings .building_nav .button_building.groundShore.hover,
            #MoveBuildings .building_nav .button_building.groundLand.hover,
            #MoveBuildings .building_nav .button_building.groundWall.hover {
                background-position: -43px -41px;
            }
            #MoveBuildings .building_nav .button_building.groundLocked {
                background-position: 0px 0px;
            }
            #MoveBuildings .building_nav .button_building.groundLocked.hover {
                background-position: 0px -41px;
            }
        `);

        var positionCSS = `
            #city #locations .position{0} {left: 884px;top:462px}
            #city #locations .position{1} {left: 730px;top:738px}
            #city #locations .position{2} {left:1085px;top:717px}
            #city #locations .position{3} {left:1209px;top:535px}
            #city #locations .position{4} {left:1010px;top:556px}
            #city #locations .position{5} {left: 851px;top:596px}
            #city #locations .position{6} {left: 649px;top:577px}
            #city #locations .position{7} {left: 491px;top:537px}
            #city #locations .position{8} {left: 490px;top:416px}
            #city #locations .position{9} {left: 650px;top:457px}
            #city #locations .position{10}{left:1051px;top:418px}
            #city #locations .position{11}{left:1207px;top:376px}
            #city #locations .position{12}{left: 908px;top:312px}
            #city #locations .position{13}{left: 770px;top:359px}
            #city #locations .position{14}{left: 452px;top:284px}
            #city #locations .position{15}{left: 528px;top:717px}
            #city #locations .position{16}{left:1088px;top:319px}
            #city #locations .position{17}{left:1088px;top:892px}
            #city #locations .position{18}{left:1332px;top:585px}
            #city #locations .position{19}{left:1320px;top:203px}
            #city #locations .position{1}.port .buildingimg{background-image:url(skin/img/city/port_r.png)}
            #city #locations .position{2}.port .buildingimg{background-image:url(skin/img/city/port_l.png)}
            #city #locations .position{1}.port.busy .buildingimg{background-image:url(skin/img/city/port_r_mit_schiff.png)}
            #city #locations .position{2}.port.busy .buildingimg{background-image:url(skin/img/city/port_l_mit_schiff.png)}
            #city #locations .position{1}.shipyard .buildingimg{background-image:url(skin/img/city/shipyard_r.png)}
            #city #locations .position{2}.shipyard .buildingimg{background-image:url(skin/img/city/shipyard_l.png)}
            #city #locations .position{1}.port .hover{background-image:url(//gf1.geo.gfsrv.net/cdn00/1e6f6c206e6bf006790b2062fba85d.png)}
            #city #locations .position{2}.port .hover{background-image:url(//gf2.geo.gfsrv.net/cdn48/5f95f6e13b94a5fedae8aca896d3d1.png)}
            #city #locations .position{1}.shipyard .hover{background-image:url(//gf1.geo.gfsrv.net/cdn3f/9f9aabfcb932d03424c287a9743315.png)}
            #city #locations .position{2}.shipyard .hover{background-image:url(//gf2.geo.gfsrv.net/cdn44/c2e872f477f8bb8e49eaab4e154012.png)}
            #city #locations .position{1}.port .img_pos{left:-45px;top:-48px;width:209px;height:148px}
            #city #locations .position{2}.port .img_pos{left:-57px;top:-43px;width:209px;height:148px}
            #city #locations .shipyard .img_pos{left:-70px;top:-64px;width:191px;height:126px}
            #city .animated #locations .position{1}.port.busy .buildingimg{background-image:url(skin/img/city/hafen_r_neu.png)}
            #city .animated #locations .position{2}.port.busy .buildingimg{background:url(skin/img/city/hafen_l_neu.png) 5px 0px}
        `;

        var cssPositionsElement;
        function updatePositionsCSS(cityKey) {
            var aliases = modData.customPositions[cityKey];
            if(!aliases) return;
            var css = [];
            css.push(TPL.parse(positionCSS, aliases));
            if (cssPositionsElement) removeElement(cssPositionsElement);
            injectCSS(css.join(''), function(el){ cssPositionsElement = el; });
        }

        TPL.set('MoveBuildings_positionsWindow', `
            <div id="mainview">
                <div class='buildingDescription'><h1>{str_MoveBuildings_name}</h1></div>
                <div class="contentBox01h" style="z-index: 101;">
                    <h3 class="header hidden"></h3>
                    <div class="content">
                        <table class="table01 left"><tbody>
                            <tr>
                                <th style="width:190px;">{select}</th>
                                <th class="left">
                                    <input id="js_MoveBuildings_savePositionsButton" type="button" class="button" value="{str_MoveBuildings_SavePositions}" />
                                </th>
                            </tr>
                            <tr><td colspan="2" style="padding:0;">
                                <div id="buildingDetail"><div class="building_nav" style="position:relative;"></div></div>
                            </td></tr>
                            <tr><th colspan="2"><i>{str_MoveBuildings_DragDropHint}</i></th></tr>
                        </tbody></table>
                    </div>
                    <div class="footer"></div>
                </div>
            </div>
        `);

        TPL.set('MoveBuildings_dragableButton', `
            <div position="{position}"
                class="button_building empty"
                draggable="true"
                onmouseover="$(this).addClass('hover');"
                onmouseout="$(this).removeClass('hover');">
            </div>
        `);

        function buildPositionsData(relatedCityData) {
            forEach(relatedCityData, (relatedCity, cityKey) => {
                if(relatedCity && relatedCity.relationship == 'ownCity') {
                    if(!modData.customPositions[cityKey]) {
                        modData.customPositions[cityKey] = buildEmptyPositions();
                    }
                    for(var i=0; i<BUILDING_POSITIONS_COUNT; i+=1) {
                        if(typeof modData.customPositions[cityKey][i] == 'undefined') modData.customPositions[cityKey][i] = i;
                    }
                }
            });
            // TODO: cleanup deleted cities
        }

        var locationHideStyleEle;
        injectCSS('#locations {display: none;}', function(el){ locationHideStyleEle = el; });

        // fast update after page load
        // no need to check if we are in city view because the expandslot is only visible in city view
        waitFor(() => {
            return document.querySelector('li.expandable.slot0.military').getAttribute('onclick').match(/cityId=(\d+)/)[1];
        }).then((cityId) => {
            updatePositionsCSS('city_'+cityId);
            if(locationHideStyleEle) removeElement(locationHideStyleEle);
        }).catch(() => {});

        // later updates after city change
        waitFor(() => {
            jshintUnused = dataSetForView.relatedCityData;
            jshintUnused = ikariam.backgroundView.screen.update;
            return true;
        }).then(() => {
            if(ikariam.backgroundView.id != 'city') return;
            buildPositionsData(dataSetForView.relatedCityData);
            hookFunction(ikariam.backgroundView.screen, 'update', function(){
                updatePositionsCSS(ikariam.model.relatedCityData.selectedCity);
            });
        }).catch(() => {});

        var showPositionsWindow;

        showPositionsWindow = function() {

            var relatedCityData = ikariam.model.relatedCityData;
            buildPositionsData(relatedCityData);

            var buildingSpawn;
            var buildingButtons = {};
            var workingPositionAliases = [];
            var workingConfirmTownChange = false;
            var workingCityKey;

            var restricted = [];
            restricted[1] = [1,2];
            restricted[2] = [1,2];
            restricted[14] = [14];

            function allowDrop(event) {
                event = event.originalEvent;
                event.preventDefault();
            }

            function drag(event) {
                event = event.originalEvent;
                event.dataTransfer.setData('position', event.target.getAttribute('position'));
            }

            function drop(event) {
                event = event.originalEvent;
                event.preventDefault();
                workingConfirmTownChange = true;
                var ele1 = $('#buildingDetail .building_nav .button_building[position="'+event.target.getAttribute('position')+'"]');
                var ele2 = $('#buildingDetail .building_nav .button_building[position="'+event.dataTransfer.getData('position')+'"]');
                var pos1 = parseInt(ele1.attr('position'));
                var pos2 = parseInt(ele2.attr('position'));
                if((restricted[pos1] && restricted[pos1].indexOf(pos2) == -1) || (restricted[pos2] && restricted[pos2].indexOf(pos1) == -1)) {
                    IkaTweaks.warning(LANG('str_MoveBuildings_restrictedPosition'));
                    return;
                }
                // swap working positions
                workingPositionAliases[pos1] = [workingPositionAliases[pos2], workingPositionAliases[pos2] = workingPositionAliases[pos1]][0];
                // swap element attributes
                ele1.attr('position', pos2);
                ele2.attr('position', pos1);
            }

            function buildWorkingButtons() {
                buildingSpawn = $('#buildingDetail .building_nav');
                for (var i=0; i<BUILDING_POSITIONS_COUNT; i++) {
                    buildingButtons[i] = $(TPL.get('MoveBuildings_dragableButton',{position:i}));
                    buildingButtons[i].on('dragstart', drag);
                    buildingButtons[i].on('dragover', allowDrop);
                    buildingButtons[i].on('drop', drop);
                    buildingButtons[i].appendTo(buildingSpawn);
                }
            }

            var citySelectOptions = {};
            forEach(relatedCityData, (data, cityKey) => {
                if (data.relationship === 'ownCity') {
                    citySelectOptions[cityKey] = data.name;
                }
            });
            var citySelect = new SelectDropDown('MoveBuildings_citySelect', 175, citySelectOptions, relatedCityData.selectedCity);

            function updateWorkingPositions() {
                workingConfirmTownChange = false;
                forEach(buildingButtons, (_, k) => {
                    buildingButtons[k].attr('class', 'button_building empty').attr('title', '');
                });
                var cityId = forceInt(citySelect.val());

                var cityKey = 'city_'+cityId;
                if(!modData.customPositions[cityKey]) return;
                workingCityKey = cityKey;

                workingPositionAliases = (function(p){
                    var l=[];
                    for(var i=0; i<BUILDING_POSITIONS_COUNT; i++) {
                        l[i]=(typeof p[i] !== null) ? p[i] : i;
                    }
                    return l;
                })(modData.customPositions[cityKey]);

                $.ajax({
                    async:true,
                    type:'GET',
                    url:'index.php?action=header&function=changeCurrentCity&currentCityId='+cityId+'&cityId='+cityId+'&backgroundView=city&oldView=city&ajax=1',
                    data:null,
                    beforeSend:function(){},
                    error:function(){},
                    success:function(data){

                        data = (function(c){for(var i=0; i<c.length; i++) if(c[i][0]=='updateGlobalData') return c[i][1].backgroundData;})(JSON.parse(data));
                        for(var i=0; i<data.position.length; i++)
                        {
                            var position = data.position[workingPositionAliases[i]];
                            if(position.building.startsWith('buildingGround'))
                            {
                                if(data.lockedPosition[i])
                                {
                                    buildingButtons[i].attr('class', 'button_building groundLocked').attr('title', data.lockedPosition[i]);
                                }
                                else
                                {
                                    buildingButtons[i].attr('class', 'button_building groundLand').attr('title', LocalizationStrings.free_building_space);
                                }
                            }
                            else
                            {
                                buildingButtons[i].attr('class', 'button_building '+position.building).attr('title', position.name+' ('+position.level+')');
                            }
                            buildingButtons[i].attr('position', i);
                        }
                    }
                });
            }

            function saveWorking() {
                workingConfirmTownChange = false;
                for(var i=0; i<BUILDING_POSITIONS_COUNT; i+=1) modData.customPositions[workingCityKey][i] = workingPositionAliases[i];
                modDataSave();
                if (citySelect.val() === ikariam.model.relatedCityData.selectedCity) {
                    updatePositionsCSS(citySelect.val());
                }
                IkaTweaks.success();
            }

            IkaTweaks.changeHTML('MoveBuildings', TPL.get('MoveBuildings_positionsWindow', {
                select : citySelect.tpl(),
            })).then(() => {
                $('#js_tab_MoveBuildings_positionsWindow').addClass('selected');
                buildWorkingButtons();
                citySelect.change(function(){
                    if(workingConfirmTownChange && confirm(LANG('str_MoveBuildings_confirmSaveChanged'))) saveWorking();
                    updateWorkingPositions();
                });
                updateWorkingPositions();
                $('#js_MoveBuildings_savePositionsButton').click(saveWorking);
            }).catch(() => {});

        };

        IkaTweaks.addSidebarButton('{str_MoveBuildings_name}', showPositionsWindow);

    });

    // MODULE: MoveBuildings
    //-----------------------------------------------------------------------------

    LANG.alias('us', 'en');
    LANG('en', 'English', {

        'str_IkaTweaks'             : 'IkaTweaks',
        'str_IkaTweaks_menu'        : 'IkaTweaks Menu',
        'str_IkaTweaks_tabModules'  : 'Modules',
        'str_IkaTweaks_tabAbout'    : 'About & Credits',
        'str_IkaTweaks_version'     : 'Version',
        'str_IkaTweaks_changelog'   : 'Changelog',

        'str_modules'           : 'Modules',
        'str_enabled'           : 'Enabled',
        'str_name'              : 'Name',
        'str_description'       : 'Description',
        'str_save'              : 'Save',
        'str_saveLanguage'      : 'Save language',
        'str_success0'          : 'Success',
        'str_success1'          : 'It\s an honor',
        'str_success2'          : 'At your command',
        'str_success3'          : 'Hallelujah',
        'str_success4'          : 'That\'s how it should be',

        'str_IkaTweaks_aboutHeader'     : 'About',
        'str_IkaTweaks_creditsHeader'   : 'Credits',

        'str_ClearStorageText'  : 'Clear LocalStorage',
        'str_ClearStorageInfo'  : 'Here you can delete all saved data for IkaTweaks out of your Browser',

        'str_ToGreasyForkText'  : 'IkaTweaks @ Greasy Fork',
        'str_ToOpenUserJSText'  : 'IkaTweaks @ OpenUserJS',
        'str_ToGitHubRepoText'  : 'IkaTweaks @ GitHub',

        'str_IkaTweaks_aboutText2'  : 'Questions, ideas, bugs or complaints? Email me at <span id="myEmail"></span> or visit me at: ',
        'str_IkaTweaks_aboutCredits': 'The used OnePiece images can be found on: <a id="creditUrl1"></a>',

        'str_versionForceCheck'         : 'Check now',
        'str_versionLastCheckResult'    : 'Last result',
        'str_versionNewAvailable'       : 'New version available',
        'str_versionUpToDate'           : 'Version is up to date',
        'str_versionGetNewestHere'      : 'Get the newest versions here',
        'str_versionCheckFailed'        : 'Checking failed',
        'str_versionAutoCheck'          : 'Activate automatic checks',

        // -- GeneralTweaks
        'str_GeneralTweaks_name'                : 'General tweaks',
        'str_GeneralTweaks_info'                : 'Change CSS rules or remove objects and advertising',
        // -- GeneralTweaks - bugFixes
        'str_GeneralTweaks_bugFixes'                    : 'Bug fixes',
        'str_GeneralTweaks_fixResearchWindowList'           : 'Fix research list in research window',
        'str_GeneralTweaks_fixCitySelectItems'              : 'Fix city select items',
        'str_GeneralTweaks_fixWindowTopPadding'             : 'Fix window top padding (looks nicer)',
        'str_GeneralTweaks_fixScrollbars'                   : 'Fix scrollbars',
        'str_GeneralTweaks_fixHoverEffectSwitching'         : 'Fix hover effects switching while mouseover on page load',
        'str_GeneralTweaks_fixDarkSelectActiveColor'        : 'Fix active font color of dark drop down menus',
        'str_GeneralTweaks_fixWindowTabSizes'               : 'Fix window tab sizes',
        'str_GeneralTweaks_removeBlueLinkBorders'           : 'Remove blue borders from active Links',
        // -- GeneralTweaks - antiAds
        'str_GeneralTweaks_antiAds'                     : 'Anti Advertising',
        'str_GeneralTweaks_adsHideSpeedUpButtons'           : 'Remove speed-up buttons',
        'str_GeneralTweaks_adsHideWindowAds'                : 'Remove window ads',
        'str_GeneralTweaks_adsHideHappyHour'                : 'Remove happy hour timer',
        'str_GeneralTweaks_adsHideOfferBoxes'               : 'Remove premium offer boxes',
        'str_GeneralTweaks_adsHideSlotResourceShop'         : 'Remove slot "Resource Shop"',
        'str_GeneralTweaks_adsHideSlotPremiumTrader'        : 'Remove slot "Premium Trader"',
        'str_GeneralTweaks_adsHideArchiveButtons'           : 'Remove archive buttons in messages window',
        'str_GeneralTweaks_adsHideMilitaryDummies'          : 'Remove military dummy boxes',
        'str_GeneralTweaks_adsHideAdvisorButtons'           : 'Remove advisor premium buttons',
        'str_GeneralTweaks_adsHideToolbarMMOAds'            : 'Remove toolbar MMO ads',
        // -- GeneralTweaks - animations
        'str_GeneralTweaks_animations'                  : 'Animations',
        'str_GeneralTweaks_animHideWalkers'                 : 'Hide all walkers and birds',
        'str_GeneralTweaks_animHideWalkerBubbles'           : 'Hide walker bubbles',
        'str_GeneralTweaks_animHideBirdsOnly'               : 'Hide birds only',
        // -- GeneralTweaks - cities
        'str_GeneralTweaks_cities'                      : 'cities',
        'str_GeneralTweaks_cityIgnoreCapital'               : 'Use normal city background for capital',
        'str_GeneralTweaks_cityUseCustomBackground'         : 'Use custom background for all your cities',
        'str_GeneralTweaks_cityUseCustomBackgroundLevel'    : 'Level {level}',
        'str_GeneralTweaks_cityHidePirateFortress'          : 'Hide pirate fortress',
        'str_GeneralTweaks_cityHideLockedPosition'          : 'Hide locked position',
        'str_GeneralTweaks_cityHideDailyTasks'              : 'Hide daily tasks',
        'str_GeneralTweaks_cityHideRegistrationGifts'       : 'Hide regestration gifts',
        'str_GeneralTweaks_cityHideFlyingShop'              : 'Hide flying premium shop',
        'str_GeneralTweaks_cityHideAmbrosiaFountain'        : 'Hide ambrosia fountain',
        // -- GeneralTweaks - ressources
        'str_GeneralTweaks_resources'                   : 'Cost resources',
        'str_GeneralTweaks_resShowMissing'                  : 'Show missing resources',
        'str_GeneralTweaks_resShowRemaining'                : 'Show remaining resources',
        'str_GeneralTweaks_warehouseShowRemainingSave'      : 'Show remaing secure resources in warehouse',
        // -- GeneralTweaks - interactive
        'str_GeneralTweaks_interactive'                 : 'Interactive',
        'str_GeneralTweaks_actShowMouseoverPlaques'         : 'Show building name on plaque on mouse over',
        'str_GeneralTweaks_actPreventPlaqueScaling'         : 'Show plaques always at full size',
        'str_GeneralTweaks_actHideMouseoverTitles'          : 'Hide default titles on mouse over',
        'str_GeneralTweaks_cityHoverToBackground'           : 'Move mouseover effect into background in cities (fixes display bugs)',
        'str_GeneralTweaks_islandHoverToBackground'         : 'Move mouseover effect into background on islands',
        // -- GeneralTweaks - fleets
        'str_GeneralTweaks_fleets'                      : 'Fleet movements',
        'str_GeneralTweaks_fleetHideSpeedColumn'            : 'Hide speed column',
        'str_GeneralTweaks_fleetHidePremiumFleetInfo'       : 'Hide premium fleet info',
        'str_GeneralTweaks_fleetShowCapacities'             : 'Show Capacities',
        'str_GeneralTweaks_fleetDontBreakLines'             : 'Don\'t break lines (could cause display error on large names)',
        'str_GeneralTweaks_fleetReadableFilterButtons'      : 'Improve appearance of filter buttons (more readable)',
        'str_GeneralTweaks_fleetHideZeroFilterButtons'      : 'Hide filter buttons without movements',
        'str_GeneralTweaks_fleetMouseOverFleetInfo'         : 'Hide magnify icon and show fleet info on mouse over',



        // -- CityListing
        'str_CityListing_name'                  : 'Enhanced City Listing',
        'str_CityListing_info'                  : 'Change city order, show tradegoods or hide coords',
        'str_CityListing_hideCoords'            : 'Hide Coords',
        'str_CityListing_showTradegoods'        : 'Show Tradegoods',
        'str_CityListing_highlightSelected'     : 'Highlight selected city',
        'str_CityListing_sortList'              : 'Use custom sorting',

        // -- ChangeAdvisors
        'str_ChangeAdvisors_name'                    : 'Individual advisors',
        'str_ChangeAdvisors_info'                    : 'Change the appearance of your advisors separately',
        'str_ChangeAdvisors_cities'                  : 'Cities',
        'str_ChangeAdvisors_military'                : 'Military',
        'str_ChangeAdvisors_research'                : 'Research',
        'str_ChangeAdvisors_diplomacy'               : 'Diplomacy',
        'str_ChangeAdvisors_maleMayor'               : 'Mayor',
        'str_ChangeAdvisors_maleMayorPremium'        : 'Mayor (premium)',
        'str_ChangeAdvisors_maleGeneral'             : 'General',
        'str_ChangeAdvisors_maleGeneralPremium'      : 'General (premium)',
        'str_ChangeAdvisors_maleScientist'           : 'Scientist',
        'str_ChangeAdvisors_maleScientistPremium'    : 'Scientist (premium)',
        'str_ChangeAdvisors_maleDiplomat'            : 'Diplomat',
        'str_ChangeAdvisors_maleDiplomatPremium'     : 'Diplomat (premium)',
        'str_ChangeAdvisors_onePieceLuffy'           : 'Monkey D. Luffy (One Piece)',
        'str_ChangeAdvisors_onePieceZoro'            : 'Roronoa Zoro (One Piece)',
        'str_ChangeAdvisors_onePieceUsopp'           : 'Usopp (One Piece)',
        'str_ChangeAdvisors_onePieceNami'            : 'Nami (One Piece)',
        'str_ChangeAdvisors_barbarianMayor'              : 'Barbarian mayor',
        'str_ChangeAdvisors_barbarianMayorPremium'       : 'Barbarian mayor (premium)',
        'str_ChangeAdvisors_barbarianGeneral'            : 'Barbarian general',
        'str_ChangeAdvisors_barbarianGeneralPremium'     : 'Barbarian general (premium)',
        'str_ChangeAdvisors_barbarianScientist'          : 'Barbarian scientist',
        'str_ChangeAdvisors_barbarianScientistPremium'   : 'Barbarian scientist (premium)',
        'str_ChangeAdvisors_barbarianDiplomat'           : 'Barbarian diplomat',
        'str_ChangeAdvisors_barbarianDiplomatPremium'    : 'Barbarian diplomat (premium)',
        'str_ChangeAdvisors_femaleMayor'                 : 'Female mayor',
        'str_ChangeAdvisors_femaleMayorPremium'          : 'Female mayor (premium)',
        'str_ChangeAdvisors_femaleGeneral'               : 'Female general',
        'str_ChangeAdvisors_femaleGeneralPremium'        : 'Female general (premium)',
        'str_ChangeAdvisors_femaleScientist'             : 'Female scientist',
        'str_ChangeAdvisors_femaleScientistPremium'      : 'Female scientist (premium)',
        'str_ChangeAdvisors_femaleDiplomat'              : 'Female diplomat',
        'str_ChangeAdvisors_femaleDiplomatPremium'       : 'Female diplomat (premium)',

        // -- MoveBuildings
        'str_MoveBuildings_name'                : 'Move Buildings',
        'str_MoveBuildings_info'                : 'Change positions of your buildings',
        'str_MoveBuildings_SavePositions'       : 'Save positions',
        'str_MoveBuildings_DragDropHint'        : '(Change positions by drag&drop them onto each other)',
        'str_MoveBuildings_confirmSaveChanged'  : 'Save changed positions?',
        'str_MoveBuildings_restrictedPosition'  : 'That building cannot be placed there',

    });

    LANG('de', 'Deutsch', {

        'str_IkaTweaks'             : 'IkaTweaks',
        'str_IkaTweaks_menu'        : 'IkaTweaks Menü',
        'str_IkaTweaks_tabModules'  : 'Module',
        'str_IkaTweaks_tabAbout'    : 'Info & Credits',
        'str_IkaTweaks_version'     : 'Version',
        'str_IkaTweaks_changelog'   : 'Changelog',

        'str_modules'           : 'Module',
        'str_enabled'           : 'Aktiviert',
        'str_name'              : 'Name',
        'str_description'       : 'Beschreibung',
        'str_save'              : 'Speichern',
        'str_saveLanguage'      : 'Sprache speichern',
        'str_success0'          : 'Erfolg',
        'str_success1'          : 'Es ist mir eine Ehre',
        'str_success2'          : 'Zu Befehl!',
        'str_success3'          : 'Hallelujah',
        'str_success4'          : 'So soll es sein',

        'str_IkaTweaks_aboutHeader'     : 'Info',
        'str_IkaTweaks_creditsHeader'   : 'Credits',

        'str_ClearStorageText'  : 'LocalStorage leeren',
        'str_ClearStorageInfo'  : 'Hier können die gespeicherten Daten für IkaTweaks aus dem Browser gelöscht werden.',

        'str_ToGreasyForkText'  : 'IkaTweaks @ Greasy Fork',
        'str_ToOpenUserJSText'  : 'IkaTweaks @ OpenUserJS',
        'str_ToGitHubRepoText'  : 'IkaTweaks @ GitHub',
        'str_IkaTweaks_aboutText2'  : 'Fragen, Ideen, Fehler gefunden oder eine Beschwerde? Einfach eine Email an <span id="myEmail"></span> oder besuche mich auf: ',
        'str_IkaTweaks_aboutCredits': 'Die hier benutzten OnePiece Bilder sind von: <a id="creditUrl1"></a>',

        'str_versionForceCheck'             : 'Jetzt überprüfen',
        'str_versionLastCheckResult'        : 'Letztes Ergebnis',
        'str_versionNewAvailable'           : 'Neue Version verfügbar',
        'str_versionUpToDate'               : 'Deine Version ist aktuell',
        'str_versionGetNewestHere'          : 'Die neuste Version gibt es immer hier',
        'str_versionCheckFailed'            : 'Konnte Version nicht überprüfen',
        'str_versionAutoCheck'              : 'Überprüfe Version automatisch',

        // -- GeneralTweaks
        'str_GeneralTweaks_name'                : 'Allgemeine Optimierungen',
        'str_GeneralTweaks_info'                : 'Ändere CSS-Regeln oder entferne Objekte und Werbung',
        // -- GeneralTweaks - bugFixes
        'str_GeneralTweaks_bugFixes'                    : 'Bugfixes',
        'str_GeneralTweaks_fixResearchWindowList'           : 'Fix Forschungsliste im Forschungsfenster',
        'str_GeneralTweaks_fixCitySelectItems'              : 'Fix Stadt-Auswahl',
        'str_GeneralTweaks_fixWindowTopPadding'             : 'Fix oberen Abstand vom Fenster',
        'str_GeneralTweaks_fixScrollbars'                   : 'Fix Scrollbalken',
        'str_GeneralTweaks_fixHoverEffectSwitching'         : 'Fix Hover-Effekte-Wechsel',
        'str_GeneralTweaks_fixDarkSelectActiveColor'        : 'Fix Aktive Textfarbe von dunklen Dropdown-Menüs',
        'str_GeneralTweaks_fixWindowTabSizes'               : 'Fix Tab-Größen im Fenster',
        'str_GeneralTweaks_removeBlueLinkBorders'           : 'Entferne blauen Rand von aktivierten Links',
        // -- GeneralTweaks - antiAds
        'str_GeneralTweaks_antiAds'                     : 'Anti-Werbung',
        'str_GeneralTweaks_adsHideSpeedUpButtons'           : 'Entferne Speed-Up-Buttons',
        'str_GeneralTweaks_adsHideWindowAds'                : 'Entferne Werbung',
        'str_GeneralTweaks_adsHideHappyHour'                : 'Entferne Happy-Hour-Timer',
        'str_GeneralTweaks_adsHideOfferBoxes'               : 'Entferne Premium-Angebote',
        'str_GeneralTweaks_adsHideSlotResourceShop'         : 'Entferne Slot "Rohstoffe holen"',
        'str_GeneralTweaks_adsHideSlotPremiumTrader'        : 'Entferne slot "Premium Händler"',
        'str_GeneralTweaks_adsHideArchiveButtons'           : 'Entferne Archivieren-Buttons',
        'str_GeneralTweaks_adsHideMilitaryDummies'          : 'Entferne militärische Attrappen',
        'str_GeneralTweaks_adsHideAdvisorButtons'           : 'Entferne Berater-Premium-Buttons',
        'str_GeneralTweaks_adsHideToolbarMMOAds'            : 'Entferne MMO-Ads in der Toolbar',
        // -- GeneralTweaks - animations
        'str_GeneralTweaks_animations'                  : 'Animationen',
        'str_GeneralTweaks_animHideWalkers'                 : 'Verstecke alle Spaziergänger und Vögel',
        'str_GeneralTweaks_animHideWalkerBubbles'           : 'Verstecke Sprechblasen von Spaziergängern',
        'str_GeneralTweaks_animHideBirdsOnly'               : 'Verstecke nur Vögel',
        // -- GeneralTweaks - cities
        'str_GeneralTweaks_cities'                      : 'Städte',
        'str_GeneralTweaks_cityIgnoreCapital'               : 'Benutze normalen Hintergrund auch für Hauptstadt',
        'str_GeneralTweaks_cityUseCustomBackground'         : 'Benutzerdefinierter Hintergrund für all Deine Städte',
        'str_GeneralTweaks_cityUseCustomBackgroundLevel'    : 'Stufe {level}',
        'str_GeneralTweaks_cityHidePirateFortress'          : 'Verstecke Piratenfestung',
        'str_GeneralTweaks_cityHideLockedPosition'          : 'Verstecke gesperrten Bauplatz',
        'str_GeneralTweaks_cityHideDailyTasks'              : 'Verstecke tägliche Aufgaben',
        'str_GeneralTweaks_cityHideRegistrationGifts'       : 'Verstecke tägliche Geschenke',
        'str_GeneralTweaks_cityHideFlyingShop'              : 'Verstecke fliegenden Shop',
        'str_GeneralTweaks_cityHideAmbrosiaFountain'        : 'Verstecke Ambrosia-Brunnen',
        // -- GeneralTweaks - ressources
        'str_GeneralTweaks_resources'                   : 'Ressourcen-Kosten',
        'str_GeneralTweaks_resShowMissing'                  : 'Zeige fehlende Ressourcen',
        'str_GeneralTweaks_resShowRemaining'                : 'Zeige verbleibende Ressourcen',
        'str_GeneralTweaks_warehouseShowRemainingSave'      : 'Zeige verbleibende sichere Ressourcen im Lagerhaus',
        // -- GeneralTweaks - interactive
        'str_GeneralTweaks_interactive'                 : 'Interaktiv',
        'str_GeneralTweaks_actShowMouseoverPlaques'         : 'Zeige Gebäudenamen auf Plaketten bei Mouse-Over',
        'str_GeneralTweaks_actPreventPlaqueScaling'         : 'Zeige Plaketten immer mit voller Größe',
        'str_GeneralTweaks_actHideMouseoverTitles'          : 'Verstecke Standard-Titel bei MouseOver',
        'str_GeneralTweaks_cityHoverToBackground'           : 'Setze MouseOver-Effekt in Städten in den Hintergrund (Behebt einige Anzeigefehler)',
        'str_GeneralTweaks_islandHoverToBackground'         : 'Setze MouseOver-Effekt auf Inseln in den Hintergrund',
        // -- GeneralTweaks - fleets
        'str_GeneralTweaks_fleets'                      : 'Truppenbewegungen',
        'str_GeneralTweaks_fleetHideSpeedColumn'            : 'Verstecke Spalte für Geschwindigkeit',
        'str_GeneralTweaks_fleetHidePremiumFleetInfo'       : 'Verstecke Premium-Flotten-Info',
        'str_GeneralTweaks_fleetShowCapacities'             : 'Zeige Kapazitäten',
        'str_GeneralTweaks_fleetDontBreakLines'             : 'Zeilen nicht umbrechen (kann bei langen Namen Fehler verursachen)',
        'str_GeneralTweaks_fleetReadableFilterButtons'      : 'Aussehen der Filter-Buttons verbessern (lesbarer machen)',
        'str_GeneralTweaks_fleetHideZeroFilterButtons'      : 'Verstecke Filter-Buttons ohne Truppen/Flotten-Bewegungen',
        'str_GeneralTweaks_fleetMouseOverFleetInfo'         : 'Entferne Lupen-Icon und zeige Flotten-Info bei MouseOver',




        // -- CityListing
        'str_CityListing_name'                  : 'Verbesserte Städteliste',
        'str_CityListing_info'                  : 'Ändere die Reihenfolge der Städte oder lass Dir Luxusgüter anzeigen',
        'str_CityListing_hideCoords'            : 'Keine Koordinaten',
        'str_CityListing_showTradegoods'        : 'Zeige Luxusgüter',
        'str_CityListing_highlightSelected'     : 'Ausgewählte Stadt hervorheben',
        'str_CityListing_sortList'              : 'Eigene Reihenfolge',

        // -- ChangeAdvisors
        'str_ChangeAdvisors_name'                    : 'Individuelle Berater',
        'str_ChangeAdvisors_info'                    : 'Ändere das Aussehen einzelner Berater',
        'str_ChangeAdvisors_cities'                  : 'Städte',
        'str_ChangeAdvisors_military'                : 'Militär',
        'str_ChangeAdvisors_research'                : 'Forschung',
        'str_ChangeAdvisors_diplomacy'               : 'Diplomatie',
        'str_ChangeAdvisors_maleMayor'               : 'Bürgermeister',
        'str_ChangeAdvisors_maleMayorPremium'        : 'Bürgermeister (Premium)',
        'str_ChangeAdvisors_maleGeneral'             : 'General',
        'str_ChangeAdvisors_maleGeneralPremium'      : 'General (Premium)',
        'str_ChangeAdvisors_maleScientist'           : 'Wissenschaftler',
        'str_ChangeAdvisors_maleScientistPremium'    : 'Wissenschaftler (Premium)',
        'str_ChangeAdvisors_maleDiplomat'            : 'Diplomat',
        'str_ChangeAdvisors_maleDiplomatPremium'     : 'Diplomat (Premium)',
        'str_ChangeAdvisors_onePieceLuffy'           : 'Monkey D. Ruffy (One Piece)',
        'str_ChangeAdvisors_onePieceZoro'            : 'Lorenor Zorro (One Piece)',
        'str_ChangeAdvisors_onePieceUsopp'           : 'Lysop (One Piece)',
        'str_ChangeAdvisors_onePieceNami'            : 'Nami (One Piece)',
        'str_ChangeAdvisors_barbarianMayor'              : 'Barbaren-Bürgermeister',
        'str_ChangeAdvisors_barbarianMayorPremium'       : 'Barbaren-Bürgermeister (Premium)',
        'str_ChangeAdvisors_barbarianGeneral'            : 'Barbaren-General',
        'str_ChangeAdvisors_barbarianGeneralPremium'     : 'Barbaren-General (Premium)',
        'str_ChangeAdvisors_barbarianScientist'          : 'Barbaren-Wissenschaftler',
        'str_ChangeAdvisors_barbarianScientistPremium'   : 'Barbaren-Wissenschaftler (Premium)',
        'str_ChangeAdvisors_barbarianDiplomat'           : 'Barbaren-Diplomatin',
        'str_ChangeAdvisors_barbarianDiplomatPremium'    : 'Barbaren-Diplomatin (Premium)',
        'str_ChangeAdvisors_femaleMayor'                 : 'Bürgermeisterin',
        'str_ChangeAdvisors_femaleMayorPremium'          : 'Bürgermeisterin (Premium)',
        'str_ChangeAdvisors_femaleGeneral'               : 'Generalin',
        'str_ChangeAdvisors_femaleGeneralPremium'        : 'Generalin (Premium)',
        'str_ChangeAdvisors_femaleScientist'             : 'Wissenschaftlerin',
        'str_ChangeAdvisors_femaleScientistPremium'      : 'Wissenschaftlerin (Premium)',
        'str_ChangeAdvisors_femaleDiplomat'              : 'Diplomatin',
        'str_ChangeAdvisors_femaleDiplomatPremium'       : 'Diplomatin (Premium)',

        // -- MoveBuildings
        'str_MoveBuildings_name'                : 'Gebäude versetzen',
        'str_MoveBuildings_info'                : 'Ändere die Positionen Deiner Gebäude',
        'str_MoveBuildings_SavePositions'       : 'Positionen speichern',
        'str_MoveBuildings_DragDropHint'        : '(Ändere die Positionen der Gebäude per Drag&Drop)',
        'str_MoveBuildings_confirmSaveChanged'  : 'Geänderte Positionen speichern?',
        'str_MoveBuildings_restrictedPosition'  : 'Dieses Gebäude kann dort nicht platziert werden',

    });


})(window);