// ==UserScript== // @name GalaxyViewPlus // @description I hab scho mit 3 im Wald mid mein Vadder usernamne gefärbdne und Ränggne gschriebne // @version 1.7.0 // @author Architekt Apx aka. Dr. Architekt1510, Altschauerberg 8, 91448 Emskirchen // @homepage https://github.com/ArchitektApx/GalaxyViewPlus // @match https://pr0game.com/*/game.php?*page=galaxy* // @connect pr0game.com // @downloadURL https://raw.githubusercontent.com/ArchitektApx/GalaxyViewPlus/master/GalaxyViewPlus.user.js // @grant GM_addStyle // @grant GM.xmlHttpRequest // @updateURL https://raw.githubusercontent.com/ArchitektApx/GalaxyViewPlus/master/GalaxyViewPlus.user.js // ==/UserScript== (()=>{"use strict";class e{static DEBUG="debug";static ERROR="error";static INFO="info";static WARN="warn"}class t{static CLEANUP_INTERVAL=168;static DEBUG_LOG_MAX_ENTRIES=20;static DEFAULT_CONFIG={configVersion:"1.0.8",features:[{active:!0,dataType:"ValueTable",description:"Recolor the username of the user.",displayName:"Username recolor",feature:"userRecolor",htmlPrefix:"userRecolor",keyDefault:"ExampleUserName",keyDescription:"The username which should be recolored",keyDisplayName:"Username",keyInputType:"text",keyName:"Username",valueDefault:"#ffa700",valueDescription:"The color which should be used for the username",valueDisplayName:"Color",valueInputType:"color",valueName:"Color",data:[{key:"ExampleUserName",value:"#ffa700"}]},{active:!0,dataType:"ValueTable",description:"Display and recolor the rank for each user in the galaxy view.",displayName:"Rank recolor",feature:"rankRecolor",htmlPrefix:"rankRecolor",keyDefault:0,keyDescription:"Recolor ranks higher than this value with the given color",keyDisplayName:"Rank",keyInputType:"number",keyName:"Rank",valueDefault:"#2cba00",valueDescription:"The color which should be used for the rank",valueDisplayName:"Color",valueInputType:"color",valueName:"Color",data:[{key:0,value:"#2cba00"},{key:200,value:"#a3ff00"},{key:500,value:"#fff400"},{key:600,value:"#ffa700"},{key:700,value:"#ff0000"}]},{active:!1,dataType:"ValueList",description:"Recolor inactive users",displayName:"Inactive recolor",feature:"inactiveRecolor",htmlPrefix:"inactiveRecolor",data:[{displayName:"Inaktiv/Inactive (i)",inputType:"color",key:"inactiveColor",value:"#f70fff",valueDescription:"Inaktiv/Inactive (i)"},{displayName:"Lange/Long i (i l)",inputType:"color",key:"longInactiveColor",value:"#ff00ff",valueDescription:"Lange/Long i (i l)"}]},{active:!0,dataType:"ValueList",description:"Shows a count of near moons and planets within the near range. Needs sync data to work.",displayName:"RangeInfo",feature:"rangeInfo",htmlPrefix:"rangeInfo",data:[{displayName:"Radius",inputType:"number",key:"nearRange",value:20,valueDescription:"Radius in which moons and planets are counted."},{displayName:"Min. Sys.",inputType:"number",key:"minSys",value:1,valueDescription:"First system in the Galaxy."},{displayName:"Max. Sys.",inputType:"number",key:"maxSys",value:400,valueDescription:"Last system in the Galaxy."}]},{active:!1,dataType:"ValueList",description:"Use different Rrank for Rank Recoloring.",displayName:"Select a rank for recoloring",feature:"rankSelector",htmlPrefix:"rankSelector",data:[{checked:!0,displayName:"Total",inputType:"radio",key:"rankSelect",value:"rank",valueDescription:"rankSelect"},{checked:!1,displayName:"Buildings",inputType:"radio",key:"rankSelect",value:"buildingRank",valueDescription:"rankSelect"},{checked:!1,displayName:"Research",inputType:"radio",key:"rankSelect",value:"researchRank",valueDescription:"rankSelect"},{checked:!1,displayName:"Fleet",inputType:"radio",key:"rankSelect",value:"fleetRank",valueDescription:"rankSelect"},{checked:!1,displayName:"Defense",inputType:"radio",key:"rankSelect",value:"defensiveRank",valueDescription:"rankSelect"}]},{active:!0,dataType:"ValueList",description:"Miscellaneous settings.",displayName:"Misc Settings",feature:"generalsettings",htmlPrefix:"generalsettings",data:[{checked:!0,displayName:"Sync Shortcut on E",inputType:"checkbox",key:"syncbutton",value:"syncbutton",valueDescription:"Sync Button will be added to the e key"},{checked:!1,displayName:"Show Debug Infos",inputType:"checkbox",key:"debugInfo",value:"debugInfo",valueDescription:"Shows Debug Infos"},{checked:!1,displayName:"Show Debug Log",inputType:"checkbox",key:"debugLog",value:"debugLog",valueDescription:"Shows Debug Logs"},{checked:!1,displayName:"Keep Config Open",inputType:"checkbox",key:"configOpen",value:"configOpen",valueDescription:"Keeps the config settings open"}]}],userInterface:{css:'\n .settings-interface-wrapper {\n margin-top: 10px;\n margin-bottom: 10px;\n width: 100%;\n }\n\n .feature-settings-container {\n width: calc(50% - 20px);\n margin-left: 10px;\n margin-right: 10px;\n display: inline-block;\n vertical-align: top;\n }\n\n .feature-header-title {\n font-weight: bold;\n }\n\n .feature-header-container {\n margin-bottom: 10px;\n }\n\n .feature-header-container label,\n .feature-header-container input[type="checkbox"] {\n display: inline-block;\n vertical-align: top; /* Align label and checkbox at the top of their line */\n }\n\n .feature-header-container label:nth-of-type(1)\n .feature-header-container input[type="checkbox"]:nth-of-type(1) {\n float: left;\n }\n\n .feature-header-container label:nth-of-type(2){\n clear: left;\n margin-left: 5%;\n }\n\n .feature-header-container p,\n .feature-header-container label {\n text-align: left;\n }\n\n .feature-header-container label {\n width: 20%;\n }\n\n .feature-body-container {\n width: 80%;\n }\n\n .feature-body-container table {\n width: 100%;\n }\n\n .feature-body-container button {\n margin-top: 10px;\n }\n\n .ValueList-row {\n margin-bottom: 10px;\n }\n\n .ValueList-row label {\n display: inline-block;\n }\n\n .hidden {\n display: none;\n }\n\n #settings-interface-footer {\n margin-top: 20px;\n width: 100%;\n text-align: center;\n } \n ',title:"Galaxy View Plus Config"}};static JSON_PATCH_USERDATA=[{op:"remove",path:["features",5,"data"]},{op:"remove",path:["features",5,"active"]},{op:"remove",path:["features",4,"data"]},{op:"remove",path:["features",4,"active"]},{op:"remove",path:["features",3,"data"]},{op:"remove",path:["features",3,"active"]},{op:"remove",path:["features",2,"data"]},{op:"remove",path:["features",2,"active"]},{op:"remove",path:["features",1,"data"]},{op:"remove",path:["features",1,"active"]},{op:"remove",path:["features",0,"data"]},{op:"remove",path:["features",0,"active"]}];static STORAGE_KEYS={CLEANUP_STATUS:"GalaxyViewPlus_CleanupStatus",DEBUG_LOG:"GalaxyViewPlus_DebugLog",STATS_DATA:"GalaxyViewPlus_StatsData",UPDATE_STATUS:"GalaxyViewPlus_UpdateStatus",USER_CONFIG:"GalaxyViewPlus_Config"};static STORAGE_TYPE="localStorage";static UPDATE_INTERVAL=6;static UPDATE_INTERVAL_DELAY=2}class a{static defaultTo(e,t=""){return a.isSomething(e)?e:t}static forceArray(e){return Array.isArray(e)?e:[e]}static hasZeroLength({length:e}){return 0===e}static isArrayOrObject(e){return Array.isArray(e)||a.isType(e,{})}static isEmptyArray(e){return!!e&&Array.isArray(e)&&a.hasZeroLength(e)}static isEmptyObject(e){return!!e&&a.hasZeroLength(Object.keys(e))&&e.constructor===Object}static isNullOrEmptyString(e){return null==e||"string"==typeof e&&""===e.trim()}static isSomething(e){return!!e&&!a.isEmptyArray(e)&&!a.isEmptyObject(e)}static isThisAndThat(e,t,a){return e===t&&e===a}static isThisOrThat(e,t,a){return e===t||e===a}static isThisOrThatType(e,t,s){return a.isType(e,t)||a.isType(e,s)}static isType(e,t){return a.isThisOrThat(e,null)||a.isThisOrThat(t,null)?e===t:e.constructor===t.constructor}static pathFromKeys(e){let t="";return e.forEach(((e,a)=>{t+="number"==typeof e?`[${e}]`:a?`.${e}`:e})),t}static pathToKeys(e){if("string"!=typeof e)throw new TypeError("Path must be a string.");return a.#e(e)}static prepareInput(e,t=!1){return t&&"object"==typeof e?Object.entries(e):a.forceArray(a.defaultTo(e,[]))}static#e(e){const t=[];return e.split(".").forEach((e=>{if(e.includes("[")){const[a,...s]=e.split("[");t.push(a,...s.map((e=>e.replace("]",""))))}else t.push(e)})),t}}class s{static arrayAction(e,t,s,r=!1){const n=a.prepareInput(t,r);if("function"!=typeof n[e])throw new TypeError(`Unsupported action: ${e}`);return n[e](s)}static filterAny(e,t,a=!1){return s.arrayAction("filter",e,t,a)}static findAny(e,t,a=!1){return s.arrayAction("find",e,t,a)}static forAny(e,t,a=!1){return s.arrayAction("forEach",e,t,a)}static mapAny(e,t,a=!1){return s.arrayAction("map",e,t,a)}}const r=["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"];class n{static escapeString(e){const t={'"':""","&":"&","'":"'","/":"/","<":"<",">":">"};return e.replaceAll(/["&'/<>]/g,(e=>t[e]))}static sanitizeStrings(e){return Array.isArray(e)?e.map((e=>n.escapeString(e))):n.escapeString(e)}static validateColorInput(e,t=!0){const a=/^#[\dA-Fa-f]{6}$/;return t?a.test(e):r.includes(e.toLowerCase())||a.test(e)}static validateNumberInput(e){return/^\d*$/.test(e)}static validateStringInput(e){return/^[\p{L}\p{N}_\-. ]*$/u.test(e)}}class i{static deepClone(e){return JSON.parse(JSON.stringify(e))}static getNestedValueOrDefault(e,t,s){const r=a.pathToKeys(t);return i.#t(e,r,s)}static hasThisAndThatProp(e,t,a){return Object.keys(e).includes(t)&&Object.keys(e).includes(a)}static hasThisOrThatProp(e,t,a){return Object.keys(e).includes(t)||Object.keys(e).includes(a)}static mergeObjects(e,t){return{...e,...t}}static pick(e,t){return Object.fromEntries(Object.entries(e).filter((([e])=>t.includes(e))))}static pickFromArray(e,t,a){const s={};return e.forEach((e=>{const r=e[t],n=i.pick(e,a);s[r]=n})),s}static setNestedValue(e,t,s){const r=a.pathToKeys(t);let n=e;r.slice(0,-1).forEach(((e,t)=>{void 0===n[e]&&(n[e]=this.#a(r[t+1])?[]:{}),n=n[e]})),n[r.at(-1)]=s}static#t(e,t,a){if(0===t.length)return e;const s=t[0];return Object.hasOwn(e,s)?i.#t(e[s],t.slice(1),a):a}static#a(e){return/^\d+$/.test(e)}}class o{static{[a,s,i,n].forEach((e=>{Object.getOwnPropertyNames(e).filter((t=>!["length","prototype","name","constructor"].includes(t)&&"function"==typeof e[t])).forEach((t=>{o[t]=e[t].bind(e)}))}))}}class c{static#s="StorageInterface";static deleteStorageItem(t){try{return localStorage.removeItem(t),!0}catch(t){return c.log("Failed to delete a key from Storage",e.ERROR,t),!1}}static getStorageItem(t){try{return JSON.parse(localStorage.getItem(t))}catch(t){return c.log("Failed to get a key from Storage",e.ERROR,t),{}}}static log(t,a=e.INFO,s=""){c.writeLog(t,a,c.#s,s)}static setStorageItem(t,a){try{return localStorage.setItem(t,JSON.stringify(a)),!0}catch(t){return c.log("Failed to save a key from Storage",e.ERROR,t),!1}}static writeLog(t,a=e.INFO,s="",r=""){const n=c.#r(t,a,s);c.#n(n,a,r),c.#i(n)}static#r(e,t,a){return`[${a} ${new Date(Date.now()).toISOString()}] [${t}]: ${e}`}static#n(t,a,s){a===e.ERROR&&(console.log(t),console.error(s))}static#i(e){try{let a=o.defaultTo(c.getStorageItem(t.STORAGE_KEYS.DEBUG_LOG),[]);a.push(e),a.length>t.DEBUG_LOG_MAX_ENTRIES&&(a=a.slice(-t.DEBUG_LOG_MAX_ENTRIES)),c.setStorageItem(t.STORAGE_KEYS.DEBUG_LOG,a)}catch(e){console.error("Critical error in saveToStorage:",e)}}}var l="remove",u="replace",d="move";function h(e,t){if(e){if(t=e(t),!Array.isArray(t))throw new Error(["pathConverter must return an array, returned:",t].join(" "))}else if(!Array.isArray(t))throw new Error(["diff path",t,"must be an array, consider supplying a path converter"].join(" "));return t}function p(e){if("__proto__"==e||"constructor"==e||"prototype"==e)throw new Error("setting of prototype values not supported")}class g{static getFilteredConfig(e){const a=g.getObjectDeepCopy(e);return function(e,t,a){if(!e||"object"!=typeof e)throw new Error("base object must be an object or an array");if(!Array.isArray(t))throw new Error("diff must be an array");for(var s=t.length,r=0;r{const t=s.features.find((t=>e.feature===t.feature));t&&g.#o(e,t)})),g.getObjectDeepCopy(s)}static uniqueItems(e){return Array.isArray(e)?[...new Set(e)]:e}static validateConfig(e,a=t.DEFAULT_CONFIG){let s;try{s=g.getFilteredConfig(e)}catch{return!1}const r=g.getFilteredConfig(a);return g.isObjectEqual(s,r)}static#o(e,t){t.data=e.data||t.data,t.active=e.active||t.active}}class m{constructor(e,t){this.config=e,this.inputData=t}execute(){const e=Array.isArray(this.inputData)?this.inputData[0].id.split("-")[0]:this.inputData.id.split("-")[0],{data:t}=this.config.features.find((t=>t.htmlPrefix===e));Array.isArray(this.inputData)?m.updateKeyValueData(t,this.inputData[0],this.inputData[1]):m.updateValueData(t,this.inputData)}static updateKeyValueData(e,t,a){const s=e.find((e=>e.key===t.lastvalue&&e.value===a.lastvalue));s?(s.key=t.value,s.value=a.value):e.push({key:t.value,value:a.value})}static updateValueData(e,t){if("radio"===t.type)return void m.updateValueDataRadio(e,t);const a=e.find((e=>e.key===t.name));"checkbox"===t.type?a.checked=t.checked:a.value=t.value}static updateValueDataRadio(e,t){e.forEach((e=>{e.checked=e.value===t.value}))}}class f{constructor(e,t){this.config=e,this.inputData=t}execute(){const e=this.inputData.id.split("-")[0],t=this.config.features.find((t=>t.htmlPrefix===e));t&&(t.active=this.inputData.checked)}}class y{constructor(e,t){this.config=e,this.removeData=t}execute(){if(Array.isArray(this.removeData)&&2===this.removeData.length){const e=this.removeData[0].id.split("-")[0],t=this.config.features.find((t=>t.htmlPrefix===e));t.data=this.#c(t.data)}}#c(e){return e.filter((e=>!(e.key===this.removeData[0].value&&e.value===this.removeData[1].value)))}}class v{constructor(e){this.resetFunction=e}execute(){this.resetFunction()}}class b{constructor(e){this.saveFunction=e}execute(){this.saveFunction()}}class S{static load(e,t,a){return{changeData:{class:m,systemInput:e},changeStatus:{class:f,systemInput:e},removeRow:{class:y,systemInput:e},resetConfig:{class:v,systemInput:t},saveConfig:{class:b,systemInput:a}}}}class k{static#s="ConfigLoader";static load(){const e=k.#l();return o.isSomething(e)?g.validateConfig(e)?k.#u(e):k.#d(e):k.#h()}static log(t,a=e.INFO,s=""){c.writeLog(t,a,k.#s,s)}static#h(){k.log("config is empty. loading default config.",e.WARN);const a=t.DEFAULT_CONFIG;return k.#p(a),a}static#d(a){k.log("config is invalid or needs an update. starting migration",e.WARN);const s=confirm("Config was deprecated or invalid and was migrated. Save and reload migrated Config?"),r=g.migrateConfig(a,t.DEFAULT_CONFIG);return k.#p(r,s),r}static#u(t){return k.log("using valid config from storage",e.DEBUG),t}static#l(){k.log("loading config from storage",e.DEBUG);const a=t.STORAGE_KEYS;return c.getStorageItem(a.USER_CONFIG)}static#p(e,a=!0){c.setStorageItem(t.STORAGE_KEYS.USER_CONFIG,e),a&&document.location.reload()}}class T{static#g;static#s="ConfigManager";#m;constructor(){if(T.#g)return T.#g;T.#g=this,this.#m=k.load(),this.commandMap=S.load(this.#m,this.#f.bind(this),this.#y.bind(this))}actionCallback(t,a){Object.keys(this.commandMap).includes(t)&&void 0!==this.commandMap[t].class?this.#v(t,a):T.log(`Invalid Command ${t}`,e.ERROR)}getActionCallback(){return this.actionCallback.bind(this)}getCurrentConfig(){return this.#m}#v(t,a){const s=this.commandMap[t].class,r=new s(...[this.commandMap[t].systemInput,a]);try{r.execute()}catch(t){T.log(`Error executing command ${s.name}:`,e.ERROR,t)}}#f(){T.log("Restoring default config",e.WARN),this.#m=t.DEFAULT_CONFIG,this.#p()}#p(){c.setStorageItem(t.STORAGE_KEYS.USER_CONFIG,this.#m),document.location.reload()}#y(){g.validateConfig(this.#m)?this.#p():alert("Invalid config, aborting update")}static log(t,a=e.INFO,s=""){c.writeLog(t,a,T.#s,s)}static _resetInstance(){T.#g=void 0}}class D{constructor(e,t={}){const a={attributes:{},children:[],classList:[],eventListeners:{},id:"",textContent:"",...t};Object.assign(this,a),this.tag=e,this.element=document.createElement(e)}addClass(e){this.element.classList.add(e)}addEventListener(e,t){this.element.addEventListener(e,t)}after(e){this.#b(e,"after")}append(e){this.#b(e,"append")}appendChildren(e){o.forAny(e,(e=>this.append(e)))}getElement(){return this.#S(this.attributes),this.#k(this.classList),this.appendChildren(this.children),this.#T(this.eventListeners),this.setId(this.id),this.setTextContent(this.textContent),this.element}setAttribute(e,t){this.element.setAttribute(e,t)}setId(e){e&&(this.element.id=e)}setTextContent(e){e&&0===this.element.children?.length&&(this.element.textContent=e)}#k(e){const t=Array.isArray(e)?e:o.forceArray(e.split(" "));o.forAny(t,(e=>this.addClass(e.trim())))}#T(e){o.forAny(e,(e=>{o.hasThisAndThatProp(e,"eventType","callback")&&this.addEventListener(e.eventType,e.callback)}))}#b(e,t){e instanceof D?this.element[t](e.getElement()):e instanceof HTMLElement?this.element[t](e):console.error(`${t} expects an instance of BaseElement or HTMLElement`)}#S(e){o.forAny(e,(([e,t])=>{this.element.setAttribute(e,t)}),!0)}}class E{static create(e,t={}){return new D(e,t).getElement()}}class R{static#D=e=>c.getStorageItem(e)??{};static#E=e=>e??"not available";static execute(e){const t=Date.now()-e,a=R.#R(t),s=R.#C(a);document.querySelector("#settings-interface-details").append(s)}static#C(e){const t=E.create("div",{id:"debug-info"});return o.forAny(e,(([e,a])=>{t.append(E.create("p",{textContent:`${e}: ${a}`}))}),!0),t}static#R(e){const a=R.#D(t.STORAGE_KEYS.UPDATE_STATUS),s=R.#D(t.STORAGE_KEYS.STATS_DATA),r=R.#D(t.STORAGE_KEYS.USER_CONFIG);return{currentConfigVersion:R.#E(r.configVersion),defaultConfigVersion:t.DEFAULT_CONFIG.configVersion,executionTime:`${e}ms`,scriptVersion:GM.info.script.version,statsDataCount:`${Object.keys(s).length} Players`,statsUpdateStatus:R.#E(a.status),statsUpdateTimestamp:a.timestamp?new Date(a.timestamp).toISOString():"not available"}}}class C{static execute(){const e=C.#A(),t=C.#I(e);document.querySelector("#settings-interface-details").lastChild.after(t)}static#I(e){const t=E.create("div",{id:"debug-log"});return e.reverse().forEach((e=>{t.append(E.create("p",{textContent:`${e}`}))})),t}static#A(){return c.getStorageItem(t.STORAGE_KEYS.DEBUG_LOG)}}class A{static execute(){document.querySelector("#settings-interface-details").open=!0}}class I{static execute(){document.addEventListener("keydown",(e=>{"e"===e.key&&(location.assign("javascript:syncsys()"),document.querySelector("#gala_sync").value="Synced")}))}}class x{static execute({features:e},t){const a={configOpen:{Class:A,params:void 0},debugInfo:{Class:R,params:t},debugLog:{Class:C,params:void 0},syncbutton:{Class:I,params:void 0}},s=e.find((e=>"generalsettings"===e.feature));s?.data&&s.data.filter((e=>e.checked)).forEach((e=>{a[e.key].Class.execute(a[e.key].params)}))}}class w{static inactiveSelector=".galaxy-short-inactive,.galaxy-short-longinactive";constructor([{value:e},{value:t}]){this.inactiveColor=e,this.longInactiveColor=t}execute(e){const t=e.parentNode.querySelectorAll(w.inactiveSelector);if(!t)return;const a=1===t.length?this.inactiveColor:this.longInactiveColor;t.forEach((e=>{e.style.color=a,e.style.fontWeight="bold"}))}}class N{static coordsRegex=/(?[1-4]):(?\d{1,3}):(?\d{1,2})(?] M)?/g;static createNearElement(e){const t=E.create("span",{});return t.append(E.create("br",{})),t.append(E.create("span",{textContent:e})),t}static formatNearText(e){return`NearP: ${e.nearPlanets} NearM: ${e.nearMoons}`}static getCircularNumberRange(e,t,a,s){const r=s-a+1;return Array.from({length:(t-e+r)%r},((t,s)=>(e+s-a)%r+a))}static getCircularPosition(e,t,a){const s=a-t+1;return((e-t)%s+s)%s+t}static getCoordsFromToolTip(e){return e.matchAll(N.coordsRegex)}static getTotalRange(e,t,a,s){const r=N.getCircularPosition(s-e,t,a),n=N.getCircularPosition(s+e,t,a);return[...r<=s?N.getCircularNumberRange(r,s,t,a):[...N.getCircularNumberRange(r,a+1,t,a),...N.getCircularNumberRange(t,s,t,a)],...n>=s?N.getCircularNumberRange(s,n+1,t,a):[...N.getCircularNumberRange(s,a+1,t,a),...N.getCircularNumberRange(t,n+1,t,a)]]}}class O{constructor([{value:e},{value:t},{value:a}],{currentSystem:s,currentGalaxy:r}){this.nearRange=Number.parseInt(e,10),this.minSys=Number.parseInt(t,10),this.maxSys=Number.parseInt(a,10),this.currentSystem=Number.parseInt(s,10),this.currentGalaxy=r}execute(e){const t=N.getTotalRange(this.nearRange,this.minSys,this.maxSys,this.currentSystem),a=N.getCoordsFromToolTip(e.dataset.tooltipContent),s=this.getNearbyCounts(a,t),r=N.createNearElement(N.formatNearText(s));e.parentNode.append(r)}getNearbyCounts(e,t){return this.nearPlanets=-1,this.nearMoons=0,[...e].forEach((([,e,a,,s])=>{this.#x(e,a,s,t)})),-1===this.nearPlanets&&(this.nearPlanets=0),{nearMoons:this.nearMoons,nearPlanets:this.nearPlanets}}#w(e){e&&this.nearMoons++}#x(e,t,a,s){const r=Number.parseInt(t,10);e===this.currentGalaxy&&s.includes(r)&&(this.nearPlanets++,this.#w(a))}static getCurrentGalaxyAndSystem(){const[e,t]=document.evaluate("//table/tbody/tr/th[contains(text(), 'System ')]",document,null,XPathResult.STRING_TYPE,null).stringValue.split(" ")[1].split(":");return{currentGalaxy:e,currentSystem:t}}}class L{static getParams(e,t){return{params:{rank:L.getRankSelectorData(e),stats:t}}}static getRankSelectorData(e){const t=o.findAny(e,(e=>"rankSelector"===e.feature));if(t?.data&&Array.isArray(t.data))return t.data}static userRankFallback(e){return e.dataset.tooltipContent.split("!0===e.checked))||{};this.rankType=e,this.rankDisplayName=t}parseParameters(e,t){this.rankRecolorData=o.isType(e,[])?e:[],this.statsInstance=o.defaultTo(t?.stats,{}),this.rankTypeData=o.forceArray(o.defaultTo(t?.rank,[]))}#O(e){const t=E.create("span",{});let a=`${this.rankDisplayName}: ${e}`;return this.usedFallback&&(a+=" *"),t.append(E.create("br",{})),t.append(E.create("span",{textContent:a})),t}#N(e,t){let a;return this.statsInstance.getPlayerRank&&(a=this.statsInstance.getPlayerRank(Number.parseInt(e,10),this.rankType)),Number.isInteger(a)?a:(this.rankDisplayName="Gesamt",this.usedFallback=!0,L.userRankFallback(t))}#L(e,t){const a=this.rankRecolorData.sort(((e,t)=>t.key-e.key)).find((e=>t>=e.key));return a&&(e.style.color=a.value),e}}class P{constructor(e){this.userRecolorData=e}execute(e){if(0===e.children.length)return;const t=e.children[0],a=t.textContent.trim(),s=this.userRecolorData.find((e=>e.key===a));s&&(t.style.color=s.value)}}class G{static selector="a.tooltip_sticky > span.galaxy-username";static#s="IteratorModule";constructor({features:t},a){G.log("Starting feature iterator ",e.DEBUG),this.featureMap={inactiveRecolor:{Class:w,params:void 0},rangeInfo:{Class:O,params:O.getCurrentGalaxyAndSystem()},rankRecolor:{Class:_,params:L.getParams(t,a)},userRecolor:{Class:P,params:void 0}},this.invokeFeatures(t),G.log("Finished running feature iterator",e.DEBUG)}invokeFeatures(e){const t=G.#_();e.filter((e=>this.featureMap[e.feature]&&e.active)).forEach((e=>{const a=new this.featureMap[e.feature].Class(e.data,this.featureMap[e.feature].params);t.forEach((e=>a.execute(e)))}))}static log(t,a=e.INFO,s=""){c.writeLog(t,a,G.#s,s)}static#_(){return o.mapAny([...document.querySelectorAll(G.selector)],(e=>e.parentNode))}}class U{static HTTP_METHOD="GET";static HTTP_REQUEST_TIMEOUT=5e3;static STATS_URL="https://pr0game.com/stats_Universe_2.json";static async fetchStatsJson(){return new Promise(((e,t)=>{GM.xmlHttpRequest({method:U.HTTP_METHOD,onerror:t,onload:e,timeout:U.HTTP_REQUEST_TIMEOUT,url:U.STATS_URL})}))}}class ${static calculate(){const e=(new Date).getHours(),a=e-e%t.UPDATE_INTERVAL,s=new Date;return s.setHours(a,t.UPDATE_INTERVAL_DELAY,0,0),s}}class F{static STATUS_FINISHED="finished";static STATUS_IN_PROGRESS="inProgress";static fromStorage(e=!1){const a=c.getStorageItem(t.STORAGE_KEYS.UPDATE_STATUS),s=c.getStorageItem(t.STORAGE_KEYS.STATS_DATA);return(e?F.isDataValid(s):F.isDataValidWithTimestamp(a,s))?s:void 0}static isDataValid(e){return!o.isEmptyObject(e||{})}static isDataValidWithTimestamp(e,t){return F.isDataValid(t)&&e?.status===F.STATUS_FINISHED&&e?.timestamp>$.calculate()}}class M{static STATUS_FINISHED="finished";static STATUS_IN_PROGRESS="inProgress";static#s="StatsDataParser";static extractFromStatsObject(a){const s={status:M.STATUS_IN_PROGRESS,timestamp:g.getTimestamp()};c.setStorageItem(t.STORAGE_KEYS.UPDATE_STATUS,s),M.log("Parsing StatsData",e.DEBUG);const r=o.pickFromArray(a,"playerId",["allianceId","allianceName","buildingRank","defensiveRank","fleetRank","playerName","rank","researchRank"]);c.setStorageItem(t.STORAGE_KEYS.STATS_DATA,r);const n={status:M.STATUS_FINISHED,timestamp:g.getTimestamp()};return c.setStorageItem(t.STORAGE_KEYS.UPDATE_STATUS,n),M.log("StatsData was parsed and written to storage",e.DEBUG),r}static log(t,a=e.INFO,s=""){c.writeLog(t,a,M.#s,s)}static processStatsData(t){const a=JSON.parse(t.responseText);return M.log("Starting StatsData parsing",e.DEBUG),M.extractFromStatsObject(a)}}class V{static STATUS_FINISHED="finished";static STATUS_IN_PROGRESS="inProgress";statsAvailable=!1;statsData;static#g;static#s="StatsDataInterface";constructor(){if(V.#g)return V.#g;V.#g=this}getPlayerIDByName(e){if(this.statsAvailable&&this.statsData)return Object.keys(this.statsData).find((t=>this.statsData[t]?.playerName===e))}getPlayerNameById(e){if(this.statsAvailable&&this.statsData)return this.statsData[e]?.playerName}getPlayerRank(e,t="rank"){if(this.statsAvailable&&this.statsData)return this.statsData[e]?.[t]}getPlayerRankData(e){if(this.statsAvailable&&this.statsData)return this.statsData[e]}async initialize(){await this.#P()}async#G(){try{const t=await U.fetchStatsJson();200===t.status?(V.log("StatsData was successfully downloaded",e.DEBUG),this.statsData=M.processStatsData(t),this.statsAvailable=!0):this.#U(t)}catch(e){this.#U(e)}}#U(t){V.log("Error while downloading StatsData:",e.WARN),V.log("Response was:",e.WARN,t),V.log("Try using old StatsData as fallback if available",e.DEBUG);const a=F.fromStorage(!0);this.statsData=a,this.statsAvailable=!0}async#P(){const t=F.fromStorage();if(t)return this.statsData=t,this.statsAvailable=!0,void V.log("StatsData successfully loaded from storage",e.DEBUG);V.log("StatsData will be refreshed",e.DEBUG),await this.#G()}static log(t,a=e.INFO,s=""){c.writeLog(t,a,V.#s,s)}static _resetInstance(){V.#g=void 0}}class j{static create(e,t={}){const a={addRow:{classList:["add-row-btn"],textContent:"Add"},button:{},removeRow:{classList:["remove-row-btn"],textContent:"Delete"},reset:{classList:["reset-config-btn"],textContent:"Reset"},save:{classList:["save-config-btn"],textContent:"Save"}}[e];if(a){const e={...a,...t};return new D("button",e).getElement()}console.error(`Button type '${e}' is not supported.`)}}class B{static changeTableVisibility(e={}){const{checked:t=!1,parentElement:{nextSibling:a}={}}=e;a?.classList&&(!0===t?a.classList.remove("hidden"):a.classList.add("hidden"))}static refreshLastValue(e={}){const{dataset:t,checked:a,value:s,type:r}=e;t?.lastvalue&&(t.lastvalue=o.isThisOrThat(r,"checkbox","radio")?a:s)}static removeRow(e){e.remove()}}class K{static extractInputData(e){const{checked:t,className:a,dataset:{lastvalue:s}={},id:r,name:n,type:i,value:o}=e,c="number"===i?Number.parseInt(o,10):o;return{checked:t,className:a,id:r,lastvalue:"number"===i?Number.parseInt(s,10):s,name:n,type:i,value:c}}static extractInputPairData(e){if(e.children&&e.children.length>2){const{children:[{firstChild:t},{firstChild:a}]}=e;return[t,a].map((e=>K.extractInputData(e)))}}static extractParentRow(e){const{parentElement:{parentElement:t}}=e;return t}static extractTableBody(e){const{parentElement:{children:[,t]}}=e;return t}}class q{static AddRowButton(e,t,a){a(K.extractTableBody(e.target),t)}static GenericInput(e,t,a){t(a,K.extractInputData(e.target))}static InputPairWithRefresh(e,t,a){const s=K.extractParentRow(e.target);t(a,K.extractInputPairData(s)),B.refreshLastValue(e.target)}static InputWithRefresh(e,t,a){q.GenericInput(e,t,a),B.refreshLastValue(e.target)}static RemoveRowButton(e,t,a){const s=K.extractParentRow(e.target);t(a,K.extractInputPairData(s)),B.removeRow(s)}static StatusCheckbox(e,t,a){q.InputWithRefresh(e,t,a),B.changeTableVisibility(e.target)}}class H{static getWrapper(e,t,a,s){return{callback:e=>{t(e,a,s)},eventType:e}}}class W{static actionMethodMap={AddRowButton:{actionMethod:q.AddRowButton,callbackAction:"addRow",eventType:"click"},Input:{actionMethod:q.InputWithRefresh,callbackAction:"changeData",eventType:"change"},InputPair:{actionMethod:q.InputPairWithRefresh,callbackAction:"changeData",eventType:"change"},RemoveRowButton:{actionMethod:q.RemoveRowButton,callbackAction:"removeRow",eventType:"click"},ResetConfig:{actionMethod:q.GenericInput,callbackAction:"resetConfig",eventType:"click"},SaveConfig:{actionMethod:q.GenericInput,callbackAction:"saveConfig",eventType:"click"},SortCheckbox:{actionMethod:q.GenericInput,callbackAction:"changeSorting",eventType:"click"},StatusCheckbox:{actionMethod:q.StatusCheckbox,callbackAction:"changeStatus",eventType:"click"}};static create(e,t,a=""){const s=W.actionMethodMap[e];if(!s?.actionMethod)return void console.error(`CallbackWrapperFactory.create: Type ${e} not found in actionMethodMap`);const{actionMethod:r,eventType:n}=s,i=a||s.callbackAction;return H.getWrapper(n,r,t,i)}}class Y extends D{constructor(e="text",t={}){const{checked:a=!1,name:s="",placeholder:r="",value:n=""}=t;super("input",t),this.element.type=e,this.element.name=s,this.element.placeholder=r,this.element.value=n,"checkbox"!==e&&"radio"!==e||(this.element.checked=a)}}class J{static create(e,t={},a=!1){const s=new Y(e,t);return a&&s.addEventListener("keydown",(e=>{e.stopPropagation(),e.stopImmediatePropagation()})),s.getElement()}}class z{constructor(e,t){this.prefix=`${e.htmlPrefix}`,this.element=E.create("table",{classList:[`${this.prefix}`,`${e.dataType}`],id:`${this.prefix}-tablecontainer`,children:this.#$(e,e.dataType,t)})}getElement(){return this.element}#F(e,t){return[E.create("td",{children:E.create("label",{attributes:{for:`${this.prefix}-input-${e.key}`,title:e.valueDescription},classList:`${this.prefix}-${e.key}-label`,textContent:e.displayName})}),E.create("td",{children:[J.create(e.inputType,{attributes:{"data-lastvalue":e.value},checked:e.checked,classList:`${this.prefix}-${e.key}-input`,eventListeners:W.create("Input",t),id:`${this.prefix}-input-${e.key}`,name:`${e.key}`,value:e.value},!0)]})]}#$(e,t,a){return e.data.map((e=>E.create("tr",{classList:`${t} ${t}-row`,id:`${this.prefix}-row`,children:this.#F(e,a)})))}}class X{constructor(e,t){this.prefix=`${e.htmlPrefix}`,this.keyInputType=e.keyInputType,this.valueInputType=e.valueInputType,this.keyDefault=e.keyDefault,this.valueDefault=e.valueDefault,this.element=E.create("table",{classList:[`${this.prefix}`,`${e.dataType}`],id:`${this.prefix}-tablecontainer`,children:[this.buildTableHeader("valuetable-header",[e.keyDisplayName,e.valueDisplayName,"Delete"]),this.buildTableBody(e,t),X.buildAddRowButton(this.addDefaultTableRow.bind(this),t)]})}addDefaultTableRow(e,t){e.append(X.buildTableRow(this.prefix,this.keyInputType,this.valueInputType,this.keyDefault,this.valueDefault,t))}buildTableBody(e,t){return E.create("tbody",{classList:"valuetable-body",id:`${this.prefix}-tablebody`,children:e.data.map((a=>X.buildTableRow(this.prefix,e.keyInputType,e.valueInputType,a.key,a.value,t)))})}buildTableHeader(e,t=["","",""],a=["key","value","delete"]){return E.create("thead",{classList:"valuetable-header",id:`${this.prefix}-tableheader`,children:E.create("tr",{classList:`${e} ${e}-row`,id:`${this.prefix}row`,children:[0,1,2].map((e=>E.create("th",{id:`${this.prefix}cell-${a[e]}`,textContent:t[e]})))})})}getElement(){return this.element}static buildAddRowButton(e,t){return j.create("addRow",{eventListeners:W.create("AddRowButton",t,e)})}static buildInputCell(e,t,a,s,r){return E.create("td",{children:[X.buildInputElement(t,{classList:`${e}-${a}`,id:`${e}-${a}-${s}`,name:`${a}input`,value:s},r)]})}static buildInputElement(e,{name:t,value:a,id:s,classList:r},n){return J.create(e,{attributes:{"data-lastvalue":a},classList:r,eventListeners:W.create("InputPair",n),id:s,name:t,value:a},!0)}static buildRemoveRowButton(e){return j.create("removeRow",{eventListeners:W.create("RemoveRowButton",e)})}static buildTableRow(e,t,a,s,r,n){return E.create("tr",{children:X.buildTableRowCells(e,t,a,s,r,n)})}static buildTableRowCells(e,t,a,s,r,n){return[X.buildInputCell(e,t,"key",s,n),X.buildInputCell(e,a,"value",r,n),E.create("td",{children:[X.buildRemoveRowButton(n)]})]}}class Z{static create(e,t){const a={ValueList:z,ValueTable:X}[e.dataType];if(a)return new a(e,t).getElement();console.error(`DataType '${e.dataType}' is not supported.`)}}class Q{constructor(e,t){this.prefix=`${e.htmlPrefix}`,this.baseClass="feature-body",this.element=E.create("div",{classList:`${this.baseClass}-container`,id:`${this.prefix}-body-container`}),e.active||this.element.classList.add("hidden"),this.element.append(Z.create(e,t))}getElement(){return this.element}}class ee{constructor(e,t){this.prefix=`${e.htmlPrefix}-header-`,this.baseClass="feature-header",this.element=E.create("div",{classList:[`${this.baseClass}-container`],id:`${this.prefix}container`,children:[E.create("p",{attributes:{title:e.description},classList:`${this.baseClass}-title`,id:`${this.prefix}title`,textContent:e.displayName}),E.create("br",{}),E.create("label",{attributes:{for:`${this.prefix}status-checkbox`},textContent:"Active:"}),J.create("checkbox",{attributes:{"data-lastvalue":e.active},checked:e.active,classList:[`${this.baseClass}-statusCheckbox`],eventListeners:W.create("StatusCheckbox",t),id:`${this.prefix}status-checkbox`,name:`${this.prefix}status-checkbox`})]})}getElement(){return this.element}}class te{static create(e,t){const a=E.create("div",{classList:"feature-settings-container",id:`${e.htmlPrefix}settings-container`}),s=new ee(e,t),r=new Q(e,t);return a.append(s.getElement()),a.append(r.getElement()),a}}class ae{constructor(e){const t=e.getCurrentConfig(),a=e.getActionCallback(),{features:s,userInterface:{title:r}}=t,n="settings-interface";this.element=E.create("div",{id:`${n}-wrapper`});const i=E.create("details",{id:`${n}-details`,children:[E.create("summary",{id:`${n}-summary`,textContent:r}),...s.map((e=>te.create(e,a))),E.create("div",{id:"settings-interface-footer",children:[j.create("save",{classList:"settings-save-button",eventListeners:W.create("SaveConfig",a),id:"save-button",textContent:"Save"}),j.create("reset",{classList:"settings-reset-button",eventListeners:W.create("ResetConfig",a),id:"reset-button",textContent:"Reset"})]})]});this.element.append(i)}getElement(){return this.element}}class se{constructor(e){this.SettingsInterface=new ae(e).getElement(),this.galaxyTable=document.querySelector(".table569"),this.SettingsInterface=se.#M(this.SettingsInterface,this.galaxyTable),se.#V(e.getCurrentConfig().userInterface.css),this.galaxyTable.after(this.SettingsInterface)}static#V(e){GM.addStyle(e)}static#M(e,t){const a=window.getComputedStyle(t),s=e;return s.style.margin=a.margin,s.style.alignSelf=a.alignSelf,s.style.width=a.width,s}}class re{static async run(){const t=Date.now();c.writeLog("Starting script",e.INFO,"Main");const a=new T,s=new V;await s.initialize();new se(a),new G(a.getCurrentConfig(),s);x.execute(a.getCurrentConfig(),t),c.writeLog("Finished script",e.INFO,"Main")}}"undefined"!=typeof process&&void 0!==process.env.JEST_WORKER_ID||re.run()})();