NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Improved Production Overview // @namespace https://openuserjs.org/users/LukaNebo // @version 1.1.0 // @description Improved OGame's production overview: highlight earliest queue(s) and highlight empty queues. // @author LukaNebo // @license MIT // @copyright 2024, LukaNebo (https://openuserjs.org/users/LukaNebo) // @match https://*.ogame.gameforge.com/game/index.php?page=ingame&component=productionqueue* // @updateURL https://openuserjs.org/meta/LukaNebo/Improved_Production_Overview.meta.js // @downloadURL https://openuserjs.org/install/LukaNebo/Improved_Production_Overview.user.js // @grant GM_addStyle // ==/UserScript== // Config const QUEUE_HEIGHT = 46.25; // [px] const QUEUE_WIDTH = { planet: 24.6, moon: 49.7, mechas: 19.6 }; // [%] const BADGES_SIZE = 18; // [px] const BADGES_MARGIN = 6; // [px] const BUTTON_WIDTH = 38; // [px] const TOGGLE_BUTTON_WIDTH = 28; // [px] const COLORS = { earliest: ["#FF9600", "#804a00", "#663c00"], empty: "#99CC00", emptyRemoved: "#D43635", og: { blue: "#6F9FC8", productionOverview: { border: "rgba(20, 29, 37, 0.8)", highlight: "rgba(9, 13, 18, 0.8)", text: "rgba(168, 168, 168, 0.6)" } } }; const DISABLED_CONSTRUCION_QUEUES_INDEXES = { planet: { nonLF: { ROBOTICS_FACTORY: [1] /* disabled LF Buildings queues */, SHIPYARD: [3] /* disabled Shipyard queues */, RESEARCH_LAB: [] /* disabled Research queueu */, NANITE_FACTORY: [1, 3] /* disabled LF Buildings and Shipyard queue */ }, LF: { LF_RESEARCH_CENTRE: [2] /* disabled LF Development queues */ }, }, moon: { nonLF: { SHIPYARD: [1] /* disabled Shipyard queues */ }, } }; // Calculate height for planet queues segment let planetQueuesHeight = QUEUE_HEIGHT + 26; // [px] // OGame queries /* OGame clock */ const OG_CLOCK_CLASS_NAME = "OGameClock"; // document.getElementsByClassName("OGameClock ")[0] /* Main content */ const OG_PRODUCTION_QUEUE_COMPONENET_ID = "productionqueuecomponent"; // document.getElementById("productionqueuecomponent") // document.getElementsByClassName("maincontent")[0] /* Planeta and moon tab */ /* */ const OG_PLANET_MOON_TABS_CLASS_NAME = "spaceObjectTab"; // document.getElementsByClassName("spaceObjectTab") /* (Only) research queue */ /* */ const OG_RESEARCH_QUEUE_CLASS_NAME = { CLASS_NAME: "planetQueues research", QUERY_SELECTOR: ".planetQueues.research" }; // document.getElementsByClassName("planetQueues research")[0] // document.querySelector(".planetQueues.research") /* Research queue header */ /* */ /* */ const OG_RESEARCH_HEADER_CLASS_NAME = "researchTitle"; // document.getElementsByClassName("researchTitle") /* Research icon (used as empty queue check) */ /* */ /* */ const OG_RESEARCH_ICON_CLASS_NAME = "researchIcon"; // document.getElementsByClassName("researchIcon") /* Planet queues section */ /* */ const OG_PLANET_PRODUCTION_CLASS_NAME = "planetProduction"; // document.getElementsByClassName("planetProduction") /* Moon queues section */ /* */ const OG_MOON_PRODUCTION_CLASS_NAME = "moonProduction"; // document.getElementsByClassName("moonProduction") /* Planet AND moon queues, all 4 (or 2, for moon) types */ /* */ /* */ const OG_PLANET_QUEUES_CLASS_NAME = "planetQueues"; // document.getElementsByClassName("planetQueues") /* Planet/moon title/header */ /* */ /* */ /* */ const OG_PLANET_TITLES_CLASS_NAME = "planetTitle"; // document.getElementsByClassName("planetTitle") /* All planet/moon queues section under planet header #1 */ /* */ /* */ /* */ const OG_QUEUE_INFOS_CLASS_NAME = "queueInfos"; // document.getElementsByClassName("queueInfos") /* All planet/moon queues section unter planet header #2 */ /* */ /* */ /* */ /* */ const OG_QUEUES_CLASS_NAME = "queues"; // document.getElementsByClassName("queues") /* Empty queues (same layer as "singleQueueWithTitle") */ /* */ /* */ /* */ /* */ /* */ const OG_EMPTY_QUEUE_CLASS_NAME = "noOrder"; // document.getElementsByClassName("noOrder") /* Single queues with box title */ /* */ /* */ /* */ /* */ /* */ const OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME = "singleQueueWithTitle"; // document.getElementsByClassName("singleQueueWithTitle") /* Box titles of single queues */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_BOX_TITLES_CLASS_NAME = "boxTitle"; // document.getElementsByClassName("boxTitle") /* Single queues #1 (without the box title) */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_SINGLE_QUEUES_CLASS_NAME = "singleQueue"; // document.getElementsByClassName("singleQueue") /* Single queues #2 inside ("singleQueue") */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_CLASS_NAME = "production"; // document.getElementsByClassName("production") /* Production icons */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_ICONS_CLASS_NAME = "productionIcon"; // document.getElementsByClassName("productionIcon") /* Production icon tag name */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_ICONS_TAG_NAME = "technology-icon"; // document.getElementsByTagName("technology-icon") /* Attribute names for all constructions */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_CONSTRUCTION_ATTRIBUTE_NAMES = { nonLF: { ROBOTICS_FACTORY: "roboticsfactory", SHIPYARD: "shipyard", RESEARCH_LAB: "researchlaboratory", NANITE_FACTORY: "nanitefactory" }, LF: { LF_RESEARCH_CENTRE: /^lifeformTech(11103|12103|13103|14103)$/i /* <--- Humans: "lifeformtech11103", Rock'tal: "lifeformtech12103", Mechas: "lifeformtech13103", and Kaelesh: "lifeformtech14103". */ } }; /* All of text strings inside single queues */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_TEXTS_CLASS_NAME = "singleQueueTexts"; // document.getElementsByClassName("singleQueueTexts") /* Production details (names, levels, and timer) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_DETAILS_CLASS_NAME = "productionDetails"; // document.getElementsByClassName("productionDetails") /* Production names */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_NAMES_CLASS_NAME = "productionName"; // document.getElementsByClassName("productionName") /* Production level (inside "Production names) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_LEVELS_CLASS_NAME = "productionLevel"; // document.getElementsByClassName("productionLevel") /* Production timers (including Research) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_PRODUCTION_TIMERS_CLASS_NAME = "productionTimer"; // document.getElementsByClassName("productionTimer") /* Countdown timer (including Eventbox and Research) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_COUNTDOWN_CLASS_NAME = "countdown"; // document.getElementsByClassName("countdown") /* Link details (hover over the queue with mouse) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_LINK_DETAILS_CLASS_NAME = "linkDetails"; // document.getElementsByClassName("linkDetails") /* Links */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_LINKS_CLASS_NAME = "link"; // document.getElementsByClassName("link") /* No entry (when there is no construction active) */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ const OG_NO_ENTRY = "noEntry"; // document.getElementsByClassName("noEntry"); // OGame dataset queries /* OGame clock datasets (requires OGLight!) */ const OGL_CLOCK_TIMESTAMP_DATASETS = { DATE: "outputDate", TIME: "outputTime", TIME_UTC: "timeUtc", TIME_SERVER: "timeServer", TIME_CLIENT: "timeClient" }; /* OGame countdown dataset */ const OG_COUNTDOWN_DATASET = "end"; // My queries const IPO_TIMESTAMP_DATASET = "data-ipo-timestamp"; const IPO_EARLIEST_QUEUE_CLASS_NAME = "IPO_earliestQueue"; const IPO_SOON_FINISHED_COUNTDOWN_CLASS_NAME = "IPO_earliestQueueCountdown"; const IPO_EMPTY_QUEUE_CLASS_NAME = "IPO_emptyQueue"; const IPO_TAB_BADGES_CLASS_NAME = "IPO_tabBadges"; const IPO_TAB_BADGES_EMPTY_QUEUES_CLASS_NAME = "IPO_tabBadgesEmptyQueues"; const IPO_SETTINGS_ID = "IPO_settings"; /* */ const IPO_SETTINGS_HEADER_CLASS_NAME = "IPO_settingsHeader"; /* */ const IPO_SETTINGS_BUTTON_LABEL_WRAPPER_CLASS_NAME = "IPO_settingsButtonWrapper"; /* */ /* */ const IPO_BUTTONS_CLASS_NAME = "IPO_settingButton"; /* */ /* */ const IPO_LABEL_CLASS_NAME = "IPO_settingsLabel"; /* */ /* */ const IPO_TOGGLE_BUTTONS_CLASS_NAME = "IPO_settingsToggleButton"; const IPO_REFRESH_MESSAGE_ID = "IPO_refreshMessage"; // Locales const LOCALES = { en: { earliestQueue: { title: "Select the number of earliest queues." }, soonFinished: { title: "Select the time threshold below which the countdown timers of soon-to-be-finished constructions are highlighted." }, emptyQueues: { title: "Highlight construction queues when their are empty" /* without "." */ }, refreshMessage: "Refresh the page to see new settings!" }, de: { earliestQueue: { title: "Wählen Sie die Anzahl der frühesten Warteschlangen." }, soonFinished: { title: "Wählen Sie die Zeitgrenze, unter der die Countdown-Timer von bald fertiggestellten Bauwerken hervorgehoben werden." }, emptyQueues: { title: "Markieren Sie Bauwarteschlangen, wenn sie leer sind" /* without "." */ }, refreshMessage: "Aktualisieren Sie die Seite, um die neuen Einstellungen zu sehen!" }, fr: { earliestQueue: { title: "Sélectionnez le nombre de files d'attente les plus anciennes." }, soonFinished: { title: "Sélectionnez le seuil de temps en dessous duquel les minuteries des constructions bientôt terminées sont mises en évidence." }, emptyQueues: { title: "Mettre en surbrillance les files d'attente de construction lorsqu'elles sont vides" /* without "." */ }, refreshMessage: "Rafraîchissez la page pour voir les nouveaux paramètres !" }, si: { earliestQueue: { title: "Izberite število najzgodnejših čakalnih vrst." }, soonFinished: { title: "Izberite časovni prag, pod katerim so označeni odštevalni časi gradnj, ki bodo kmalu končane." }, emptyQueues: { title: "Označi prazne čakalne vrste gradenj" /* without "." */ }, refreshMessage: "Osvežite stran, da vidite nove nastavitve!" }, // __: { earliestQueue: { title: "" }, soonFinished: { title: "" }, emptyQueues: { title: "" /* without "." */ }, refreshMessage: "" }, }; // Add style sheets GM_addStyle(` /*** (NEW) OGame STYLES ***/ .${OG_PLANET_MOON_TABS_CLASS_NAME} { position: relative !important; display: flex !important; justify-content: center !important; align-items: center !important; } .${OG_PLANET_QUEUES_CLASS_NAME}:not(.research) { height: ${planetQueuesHeight}px !important; } .${OG_QUEUE_INFOS_CLASS_NAME}, .${OG_QUEUES_CLASS_NAME}, .${OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME} { gap: 0.5% !important; height: ${QUEUE_HEIGHT}px !important; min-height: ${QUEUE_HEIGHT}px !important; } .${OG_PLANET_PRODUCTION_CLASS_NAME} .${OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME} { width: ${QUEUE_WIDTH.planet}% !important; } .${OG_MOON_PRODUCTION_CLASS_NAME} .${OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME} { width: ${QUEUE_WIDTH.moon}% !important; } .${OG_SINGLE_QUEUES_CLASS_NAME} { overflow: hidden !important; } .${OG_LINK_DETAILS_CLASS_NAME}, .${OG_LINKS_CLASS_NAME} { overflow: hidden !important; /* <-------------- hides overflow content */ text-overflow: ellipsis !important; /* <------- add ellipsis, "...", to represent cliped text */ white-space: nowrap !important; /* <----------- prevents the text from wrapping to a new line */ justify-content: flex-start !important; /* <--- horizontal alignment <flex> */ align-items: center !important; /* <----------- vertical alignment <flex> */ } .${OG_PRODUCTION_LEVELS_CLASS_NAME} { color: ${COLORS.earliest[0]} !important; } .${OG_BOX_TITLES_CLASS_NAME} { white-space: nowrap !important; overflow: hidden !important; max-width: 94% !important; } .${OG_BOX_TITLES_CLASS_NAME}::before, .${OG_BOX_TITLES_CLASS_NAME}::after { min-width: 0px !important; margin-right: 2px !important; margin-left: 2px !important; } /*** IPO TAB BADGES ***/ .${IPO_TAB_BADGES_CLASS_NAME}, .${IPO_TAB_BADGES_EMPTY_QUEUES_CLASS_NAME} { position: absolute; display: flex; top: -25%; height: ${BADGES_SIZE}px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3); justify-content: center; align-items: center; color: white; font-size: ${BADGES_SIZE - 7}px; font-weight: bold; text-shadow: 0 0 2px rgba(0, 0, 0, 0.5); } .${IPO_TAB_BADGES_CLASS_NAME} { width: ${BADGES_SIZE}px; border-radius: 50%; } .${IPO_TAB_BADGES_EMPTY_QUEUES_CLASS_NAME} { left: ${BADGES_MARGIN + 1}px; border-radius: 3px; padding: 0 6px; } /*** IPO SETTINGS ***/ #${IPO_SETTINGS_ID} { margin: 14px 7px 0 7px; border-radius: 3px; border: 2px solid ${COLORS.og.productionOverview.border}; padding: 10px 10px 8px 10px; background: linear-gradient(to top, ${COLORS.og.productionOverview.border} 0%, ${COLORS.og.productionOverview.highlight} 100%); box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); color: ${COLORS.og.productionOverview.text}; } #${IPO_SETTINGS_ID} table { /*width: 100%;*/ } #${IPO_SETTINGS_ID} table td { vertical-align: middle; align-items: center; } .${IPO_SETTINGS_HEADER_CLASS_NAME} { position: absolute; top: -18px; left: 0; color: ${COLORS.og.blue}; text-shadow: 0 0 2px rgba(0, 0, 0, 0.5); } .${IPO_SETTINGS_BUTTON_LABEL_WRAPPER_CLASS_NAME} { display: flex; align-items: center; margin-left: 10px; } .${IPO_LABEL_CLASS_NAME} { margin-right: 4px; cursor: default; } .${IPO_BUTTONS_CLASS_NAME} { display: inline-flex; cursor: pointer; margin-right: 5px; width: ${BUTTON_WIDTH}px; height: 18px; border: 1px solid ${COLORS.og.productionOverview.text}; border-radius: 3px; background-color: transparent; justify-content: center; align-items: center; color: white; } .${IPO_BUTTONS_CLASS_NAME}:hover { border-color: white; } .${IPO_TOGGLE_BUTTONS_CLASS_NAME} { display: inline-flex; cursor: pointer; margin-right: 5px; width: ${TOGGLE_BUTTON_WIDTH}px; height: 18px; border: 1px solid ${COLORS.og.productionOverview.text}; border-radius: 3px; } .${IPO_TOGGLE_BUTTONS_CLASS_NAME}:hover { border-color: white; } .${IPO_TOGGLE_BUTTONS_CLASS_NAME}.active { background-color: ${COLORS.empty}; } .${IPO_TOGGLE_BUTTONS_CLASS_NAME}.inactive { background-color: transparent; } #${IPO_REFRESH_MESSAGE_ID} { display: flex; margin: 4px 7px 0 7px; border-radius: 3px; border: 2px solid ${COLORS.og.productionOverview.border}; padding: 10px; background: linear-gradient(to top, ${COLORS.og.productionOverview.border} 0%, ${COLORS.og.productionOverview.highlight} 100%); box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2); color: ${COLORS.og.productionOverview.text}; transition: max-height 0.5s ease-out, opacity 0.5s ease-out; opacity: 0; } `); // Initialize local storage object with default values if it does not already exist const IPO_HIGHLIGHT_EMPTY_QUEUES_LOCAL_STORAGE = "IPO_settings"; const DEFAULT_IPO_SETTINGS = { version: 1, n: 1, soonFinished: 1, // [h] highlightEmpty: { research: [true], planet: [true, true, false, false], moon: [true, false], }, }; let ipoSettingsCheck = localStorage.getItem(IPO_HIGHLIGHT_EMPTY_QUEUES_LOCAL_STORAGE); if (!ipoSettingsCheck || JSON.parse(ipoSettingsCheck).version !== DEFAULT_IPO_SETTINGS.version) { setIpoSettings (DEFAULT_IPO_SETTINGS); } // Set settings from local storage let ipoSettings = getIpoSettings(); // Get (user selected) language from cookies; if there is no locales for that languge, use default language (English) const cookieMatch = document.cookie.match(/oglocale=([^;]+)/); let lang = cookieMatch ? cookieMatch[1] : "en"; if (!LOCALES[lang]) { lang = "en"; } // Get and assign locales from OGame page let locales = getLocales(); //* DEBUG */ console.log("LOCALES:", LOCALES[lang], "\nlocales:", locales); // Get current time/timestamp const currentTimestamp = getCurrentTimestamp(); //* DEBUG */ console.debug("Current timestamp:", currentTimestamp); // Remove <span> element from production timer while keeping production timer intact let productionTimers = document.getElementsByClassName(OG_PRODUCTION_TIMERS_CLASS_NAME); for (let i = 0; i < productionTimers.length; i++) { productionTimers[i].getElementsByTagName("span")[0].remove(); } // Remove (planet) production names while keeping its levels intact let planetProduction = document.getElementsByClassName(OG_PLANET_PRODUCTION_CLASS_NAME)[0]; let productionNames = planetProduction ? planetProduction.getElementsByClassName(OG_PRODUCTION_NAMES_CLASS_NAME) : undefined; if (productionNames) { for (let i = 0; i < productionNames.length; i++) { let productionName = productionNames[i]; for (let j = 0; j < productionName.childNodes.length; j++) { let node = productionName.childNodes[j]; if (node.nodeType === Node.TEXT_NODE) { node.nodeValue = ""; } } // Change styles productionName.style.color = COLORS.earliest[0]; } } // Selector for countdown timer (inside production queue component, since first countdown timer is in eventbox) const countdownTimers = document.getElementById(OG_PRODUCTION_QUEUE_COMPONENET_ID).getElementsByClassName(OG_COUNTDOWN_CLASS_NAME); // Assign all countdown timers (their timestamps) into one array const timestamps = assignTimestamps(countdownTimers); //* DEBUG */ console.debug("timestamps:", timestamps); // Initialize an object to store earliest queues let earliestQueues_planetOrMoon = { planet: [], moon: [] }; // Call functions shrinkMechasQueues(); if (ipoSettings.n >= 1) highlightEarliestQueue(1); if (ipoSettings.n >= 2) highlightEarliestQueue(2); if (ipoSettings.n >= 3) highlightEarliestQueue(3); if (ipoSettings.soonFinished !== 0) soonFinishedTimers(); if (ipoSettings.highlightEmpty.research[0] == true) highlightEmptyResearchQueue(COLORS.empty); highlightEmptyQueues("planet"); highlightEmptyQueues("moon"); highlightDisabledConstructionQueues("planet"); highlightDisabledConstructionQueues("moon"); addTabBadges(); createSettings(); // Function to get my local storage function getIpoSettings () { return JSON.parse(localStorage.getItem(IPO_HIGHLIGHT_EMPTY_QUEUES_LOCAL_STORAGE)); } // Function to set my local storage function setIpoSettings (ipoSettings) { localStorage.setItem(IPO_HIGHLIGHT_EMPTY_QUEUES_LOCAL_STORAGE, JSON.stringify(ipoSettings)); } // Function to get current time (timestamp) from OGame clock function getCurrentTimestamp () { // OGame selectior const ogClock = document.getElementsByClassName(OG_CLOCK_CLASS_NAME)[0] // Return CLIENT timestamp from OGLight if it exists (since client time is used in countdown timestamps) let currentTimestamp; let ogl_currentTimestamp = parseInt(ogClock.dataset[OGL_CLOCK_TIMESTAMP_DATASETS.TIME_CLIENT], 10); //* DEBUG */ console.debug("currentTimestamp:", currentTimestamp); if (ogl_currentTimestamp) { currentTimestamp = ogl_currentTimestamp; } else { currentTimestamp = assignCurrentTimestamp(ogClock); //* DEBUG */ console.debug("OGLight is not present! Assigning current timestamp from OGame Clock...\n\ncurrentTimestamp:", currentTimestamp); } return { client: currentTimestamp, seconds: currentTimestamp / 1000 }; } // Function to update the <li> element with timestamp function assignCurrentTimestamp (ogClock) { if (ogClock) { // Extract date and time from the OGame Clock let dateStr = ogClock.textContent.split(" ")[0]; let timeStr = ogClock.querySelector("span").textContent; // Parse date and time let [d, m, y] = dateStr.split(".").map(Number); let [h, min, s] = timeStr.split(":").map(Number); // Create a date object ("m - 1" because months are 0-indexed) let date = new Date(y, m - 1, d, h, min, s); // Convert to timestamp [ms] let timestamp = date.getTime(); // Add new attribute to OGame Clock element ogClock.setAttribute(IPO_TIMESTAMP_DATASET, timestamp); //* DEBUG */ console.debug("Parsed date (date):", date, "\nUnix timestamp [ms] (timestamp):", timestamp, "\n\nUpdated OGame Clock element:", ogClock); // Return timestamp return timestamp; } } // Function to get locales directly from OGame page function getLocales () { // OGame selectors const ogBar = document.getElementById("bar"); const ogBar_options = ogBar.getElementsByTagName("li")[5] || undefined; const ogTabs = document.getElementsByClassName(OG_PLANET_MOON_TABS_CLASS_NAME); const ogResearchHeader = document.getElementsByClassName(OG_RESEARCH_HEADER_CLASS_NAME)[0]; const ogBoxTitles = document.getElementsByClassName(OG_BOX_TITLES_CLASS_NAME); // Assign locales object let locales = {}; locales.options = ogBar_options.textContent || "Options"; locales.planet = ogTabs[0].textContent || "Planets"; locales.moon = ogTabs[1].textContent || "Moons"; locales.researchQueue = ogResearchHeader.textContent || "Research"; locales.planetQueues = [ ogBoxTitles[0].textContent || "Buildings", ogBoxTitles[1].textContent.replace(/\n/g, "") || "Lifeform Buildings", ogBoxTitles[2].textContent.replace(/\n/g, "") || "Lifeform Development", ogBoxTitles[3].textContent || "Shipyard" ]; locales.moonQueues = [ ogBoxTitles[0].textContent || "Buildings", ogBoxTitles[3].textContent || "Shipyard" ]; return locales; } // *** MECHAS QUEUES *** // Function to change width for queues where there is extra MECHAS SHIPYARD queue function shrinkMechasQueues () { // OGame selector for all queues (for all planets) let planetProduction = document.getElementsByClassName(OG_PLANET_PRODUCTION_CLASS_NAME)[0]; let queues = planetProduction ? planetProduction.getElementsByClassName(OG_QUEUES_CLASS_NAME) : undefined; // Assign array with indexes of Mechas Shipyard queues let mechasQueuesIndexes = getMechasQueuesIndex(queues); //* DEBUG */ console.debug("mechasIndexes:", mechasQueuesIndexes); // Overwritestyles (width) for single queues for a planet with mechas queues mechasQueuesIndexes.forEach(index => { let singleQueueWithTitles = queues[index].getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME); //* DEBUG */ console.debug(singleQueueWithTitles); for (let i = 0; i < singleQueueWithTitles.length; i++) { singleQueueWithTitles[i].style.setProperty("width", QUEUE_WIDTH.mechas+"%", "important"); } }); } // Function to get indexes of queues with MECHAS SHIPYARD function getMechasQueuesIndex (queues) { // Push indexes of queues where there are 5 elements (i.e. where there is extra Mechas Shipyard queue) into new array if (queues) { let mechasQueuesIndexes = []; for (let q = 0; q < queues.length; q++) { let singleQueueWithTitles = queues[q].getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME); if (singleQueueWithTitles.length == 5) { mechasQueuesIndexes.push(q); } } return mechasQueuesIndexes; } else { return []; } } // *** HIGHLIGHT QUEUES *** // Function to highlight earliest construction that will be completed function highlightEarliestQueue (n) { // Find smallest value in the array and its index let { earliestTimestamp, earliestIndex } = findEarliestTimestamp(timestamps, n); // Get the parent element of the countdown with the earliest timestamp and change its styles let earliestParentElement = countdownTimers[earliestIndex]?.closest(`.${OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME}, .${OG_PLANET_QUEUES_CLASS_NAME}`); if (earliestParentElement) { //* DEBUG */ if (n == 1) console.debug("timestamps:", timestamps, "\n\nearliestTimestamp:", earliestTimestamp, "\nearliestIndex:", earliestIndex, "\n\nearliestSingleQueueParent", earliestParentElement); // Highlight individual queues (planet AND moon queues) highlightEarliestQueue_styles (earliestParentElement, n); // Determine if the parent element is under planet or moon section let parentPlanet = earliestParentElement.closest(`.${OG_PLANET_PRODUCTION_CLASS_NAME}`); let parentMoon = earliestParentElement.closest(`.${OG_MOON_PRODUCTION_CLASS_NAME}`); if (parentPlanet) { earliestQueues_planetOrMoon.planet.push(n); //* DEBUG */ console.debug("#"+n+" earliest queue is under PLANET constructions"); } else if (parentMoon) { earliestQueues_planetOrMoon.moon.push(n); //* DEBUG */ console.debug("#"+n+" earliest queue is under MOON constructions"); } } } // Function to highlight countdown timers of construction near completion function soonFinishedTimers () { //* DEBUG */ console.log("Current timestamp:", currentTimestamp.seconds); timestamps.forEach(entry => { //* DEBUG */ console.log("Entry: ", entry); if (entry.soonFinished) { let element = countdownTimers[entry.index]; let parentResearch = element.closest(OG_RESEARCH_QUEUE_CLASS_NAME.QUERY_SELECTOR); // Style Research queue differently from all others if (parentResearch) { element.style.padding = "2px 6px"; element.style.background = COLORS.earliest[0]; element.style.color = "white"; element.style.textShadow = "0 0 2px rgba(0, 0, 0, 0.5)"; } else { let bgColor = COLORS.earliest[0] + "26"; // 15% transparency element.style.padding = "0px 3px"; element.style.background = bgColor; element.style.boxShadow = "0 0 3px " + bgColor; element.style.color = COLORS.earliest[0]; element.style.textShadow = "0 0 2px rgba(0, 0, 0, 0.5)"; } element.style.borderRadius = "3px"; // Add class name element.classList.add(IPO_SOON_FINISHED_COUNTDOWN_CLASS_NAME); } }); } // Function to assign all countdown timers into one array function assignTimestamps (countdownTimers) { let timestamps = []; for (let i = 0; i < countdownTimers.length; i++) { let timestamp = parseInt(countdownTimers[i].dataset[OG_COUNTDOWN_DATASET], 10); let timestampDifference = timestamp - currentTimestamp.seconds; let soonFinished = timestampDifference <= ipoSettings.soonFinished * 3600; timestamps.push({ index: i, timestamp: timestamp, timestampDifference: timestampDifference, soonFinished: soonFinished }); } return timestamps; } // Function to find nth earliest timestamp and its index function findEarliestTimestamp (timestamps, n) { let sortedTimestamps = [...timestamps].sort((a, b) => a.timestamp - b.timestamp); let rarliestTimestamp = sortedTimestamps[n-1]?.timestamp; let earliestIndex = sortedTimestamps[n-1]?.index; return { rarliestTimestamp, earliestIndex }; } // Function to highlight individual elements (nth earliest completed construction) function highlightEarliestQueue_styles (parentElement, n) { // Assign child element and change its styles (excluding Research) let singleQueue = parentElement.getElementsByClassName(OG_SINGLE_QUEUES_CLASS_NAME)[0]; if (singleQueue) { highlightEarliestQueue_styles_frame(singleQueue, n); // Assign another child element and change its styles let boxTitle = parentElement.getElementsByClassName(OG_BOX_TITLES_CLASS_NAME)[0]; if (boxTitle) { highlightEarliestQueue_styles_title(boxTitle, n) } } // ... else, including Research else { highlightEarliestQueue_styles_frame(parentElement, n); }; } // Function to highlight and style title (nth earliest completed construction) function highlightEarliestQueue_styles_title (title, n) { // Use last defined color in "COLORS.earliest" array if "n" is bigger than the number of elements in this array let colorIndex = Math.min(n-1, COLORS.earliest.length-1); // Styles title.style.top = "-9%"; title.style.left = "3%"; title.style.width = "auto"; title.style.maxWidth = "94%"; title.style.marginBottom = "-4%"; title.style.borderRadius = "4px"; title.style.backgroundColor = COLORS.earliest[colorIndex]; title.style.lineHeight = "15px"; title.style.color = "white"; title.style.textShadow = "0 0 2px rgba(0, 0, 0, 0.5)"; // Text content title.textContent = "#"+n+": " + title.textContent; } // Function to highlight frame (nth earliest completed construction) function highlightEarliestQueue_styles_frame (frame, n) { // Use last defined color in "COLORS.earliest" array if "n" is bigger than the number of elements in this array let colorIndex = Math.min(n-1, COLORS.earliest.length-1); frame.id = `${IPO_EARLIEST_QUEUE_CLASS_NAME}_${n}`; frame.classList.add(IPO_EARLIEST_QUEUE_CLASS_NAME); frame.style.border = "1px solid " + COLORS.earliest[colorIndex] + "80"; // Aplha/transparency component, HEX values: 100% = "FF"; 95% = "F2"; 90% = "E6"; 85% = "D9"; 80% = "CC"; 75% = "BF"; 70% = "B3"; 65% = "A6"; 60% = "99"; 55% = "8C"; 50% = "80"; 45% = "73"; 40% = "66"; 35% = "59"; 30% = "4D"; 25% = "40"; 20% = "33"; 15% = "26"; 10% = "1A"; 5% = "0D"; 0% = "00". frame.style.boxShadow = "inset 0 0 3px " + COLORS.earliest[colorIndex] + "A6"; // 65% transparency frame.style.background = COLORS.earliest[colorIndex] + "33"; // 20% transparency } // *** EMPTY QUEUES *** // Function to highlight empty planet or moon queues function highlightEmptyQueues (type) { // Determine what to highlight, "planet" or "moon" queues section let queuesSectionClassName = type === "planet" ? OG_PLANET_PRODUCTION_CLASS_NAME : OG_MOON_PRODUCTION_CLASS_NAME; // Assign planet or moon queues let queuesSection = document.getElementsByClassName(queuesSectionClassName)[0]; if (queuesSection) { // Assign queues within planet or moon section separately let queues = queuesSection.getElementsByClassName(OG_PLANET_QUEUES_CLASS_NAME); for (let p = 0; p < queues.length; p++) { // Assign "single" queues inside a given queue let singleQueues = queues[p].getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME); // Highlight empty construction queues (Buildings, Lifeform Buildings, Lifeform Development, and/or Shipyard) let ipoSettings = getIpoSettings(); for (let i = 0; i < ipoSettings.highlightEmpty[type].length; i++) { highlightEmptyQueue_styles(type, i, singleQueues); }; } } } // Function to highlight individual elements (empty queues) function highlightEmptyQueue_styles (type, constructionIndex, queuesSelector) { let ipoSettings = getIpoSettings(); if (ipoSettings.highlightEmpty[type][constructionIndex] && queuesSelector[constructionIndex].classList.contains(OG_EMPTY_QUEUE_CLASS_NAME)) { queuesSelector[constructionIndex].classList.add(IPO_EMPTY_QUEUE_CLASS_NAME); let frame = queuesSelector[constructionIndex].getElementsByClassName(OG_SINGLE_QUEUES_CLASS_NAME)[0]; frame.style.border = "1px solid " + COLORS.empty + "40"; // 25% transparency frame.style.boxShadow = "inset 0 0 3px " + COLORS.empty + "59"; // 35% transparency frame.style.background = COLORS.empty + "1A"; // 10% transparency let title = queuesSelector[constructionIndex].getElementsByClassName(OG_BOX_TITLES_CLASS_NAME)[0]; title.style.color = COLORS.empty; let text = queuesSelector[constructionIndex].getElementsByClassName(OG_NO_ENTRY)[0]; text.style.color = COLORS.empty + "BF"; // 75% transparency } } // Function to highlight empty research queue function highlightEmptyResearchQueue (color) { let queueCheck = document.getElementsByClassName(OG_RESEARCH_ICON_CLASS_NAME); if (queueCheck.length == 0) { let queue = document.getElementsByClassName(OG_RESEARCH_QUEUE_CLASS_NAME.CLASS_NAME)[0]; if (queue) { queue.style.border = "1px solid " + color + "40"; queue.style.boxShadow = "inset 0 0 4px " + color; queue.style.background = color + "1A"; // 10% transparency let header = document.getElementsByClassName(OG_RESEARCH_HEADER_CLASS_NAME)[0]; header.style.color = color; let text = queue.getElementsByClassName(OG_NO_ENTRY)[0]; text.style.color = color + "BF"; // 75% transparency } } } // *** DISABLED CONSTRUCTION QUEUES *** // Function to check if there is any active construction that prevents other queues (like Robotics Factory that prevents Lifeform buildings, etc.) function highlightDisabledConstructionQueues (planetType) { // Determine what to highlight, "planet" or "moon" queues section let planetProductionClassName = planetType === "planet" ? OG_PLANET_PRODUCTION_CLASS_NAME : OG_MOON_PRODUCTION_CLASS_NAME; // OGame selector for all queues (for all planets) let planetProduction = document.getElementsByClassName(planetProductionClassName)[0]; let queues = planetProduction ? planetProduction.getElementsByClassName(OG_QUEUES_CLASS_NAME) : undefined; // Loop through all planets for (let q = 0; q < queues.length; q++) { // Define the queues to check (differently for planet and moon) const queuesToCheck = [ { queue: queues[q] ? queues[q].getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME)[0] : undefined, constructionType: "nonLF" } ]; if (planetType === "planet") queuesToCheck.push({ queue: queues[q] ? queues[q].getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME)[1] : undefined, constructionType: "LF" }); // Loop through each queue queuesToCheck.forEach(({ queue, constructionType }) => { if (queue) { // OGame selecotor for element that contains construciton icon let techIcon = queue.getElementsByTagName(OG_PRODUCTION_ICONS_TAG_NAME)[0]; if (techIcon && techIcon.attributes.length > 1) { // Fetch name of second (!) attribute (second attribute is the name of the construction) let attributeName = techIcon.attributes[1].name; //* DEBUG */ console.debug("Planet index:", q, "| Second/construction attribute name:", attributeName); // Check for nonLF and LF construction types let isNonLF_match = Object.values(OG_CONSTRUCTION_ATTRIBUTE_NAMES.nonLF).includes(attributeName); let isLF_match = planetType === "planet" && constructionType === "LF" && OG_CONSTRUCTION_ATTRIBUTE_NAMES.LF.LF_RESEARCH_CENTRE.test(attributeName); let ifLaboratory_match = planetType === "planet" && constructionType === "nonLF" && attributeName === OG_CONSTRUCTION_ATTRIBUTE_NAMES.nonLF.RESEARCH_LAB; //* DEBUG */ if (ifLaboratory_match) console.debug("--> ifLaboratory_match (attribute name: \""+OG_CONSTRUCTION_ATTRIBUTE_NAMES.nonLF.RESEARCH_LAB+"\"):", ifLaboratory_match,); if (isNonLF_match || isLF_match) { let disabledQueueIndexes = []; if (isNonLF_match) { let nonLFKey = Object.keys(OG_CONSTRUCTION_ATTRIBUTE_NAMES.nonLF).find(key => OG_CONSTRUCTION_ATTRIBUTE_NAMES.nonLF[key] === attributeName); disabledQueueIndexes = disabledQueueIndexes.concat(DISABLED_CONSTRUCION_QUEUES_INDEXES[planetType].nonLF[nonLFKey] || []); } if (isLF_match) { disabledQueueIndexes = disabledQueueIndexes.concat(DISABLED_CONSTRUCION_QUEUES_INDEXES[planetType].LF.LF_RESEARCH_CENTRE || []); } // Process all disabled indexes disabledQueueIndexes.forEach(index => { let removedEmptyQueue = queues[q]?.getElementsByClassName(OG_SINGLE_QUEUE_WITH_BOX_TITLE_CLASS_NAME)[index]; if (removedEmptyQueue) { removeClassName(removedEmptyQueue, IPO_EMPTY_QUEUE_CLASS_NAME); highlightDisabledConstructionQueues_styles(removedEmptyQueue, COLORS.emptyRemoved); } }); } // Research check if (ifLaboratory_match) { highlightEmptyResearchQueue(COLORS.emptyRemoved); } } } }); } } // Function to remove the class name function removeClassName (element, className) { if (element.classList.contains(className)) { element.classList.remove(className); //* DEBUG */ console.debug("--> Class \""+className+"\" removed from element. (removeClassName())"); } } // Function to highlight individual elements (empty queues) function highlightDisabledConstructionQueues_styles (element, color) { //* DEBUG */ console.debug("--> element (highlightDisabledConstructionQueues_styles()):", element); let frame = element.getElementsByClassName(OG_SINGLE_QUEUES_CLASS_NAME)[0]; frame.style.border = "1px solid " + color + "40"; // 25% transparency frame.style.boxShadow = "inset 0 0 3px " + color + "59"; // 35% transparency frame.style.background = color + "1A"; // 10% transparency let title = element.getElementsByClassName(OG_BOX_TITLES_CLASS_NAME)[0]; title.style.color = color; let text = element.getElementsByClassName(OG_NO_ENTRY)[0]; text.style.color = color + "BF"; // 75% transparency } // *** TAB BADGES *** // Function to add a badge in planet/moon tabs function addTabBadges () { // OGame selector const ogTabs = document.getElementsByClassName(OG_PLANET_MOON_TABS_CLASS_NAME); // Planet for (let i = earliestQueues_planetOrMoon.planet.length - 1; i >= 0; i--) { //* DEBUG */ console.debug("earliestQueues_planetOrMoon.planet[" + i + "]:", earliestQueues_planetOrMoon.planet[i]); createBadgeForTab(ogTabs[0], earliestQueues_planetOrMoon.planet[i]); } createBadgeForTab_emptyQueues(ogTabs[0], "planet"); // Moon for (let i = earliestQueues_planetOrMoon.moon.length - 1; i >= 0; i--) { //* DEBUG */ console.debug("earliestQueues_planetOrMoon.moon[" + i + "]:", earliestQueues_planetOrMoon.moon[i]); createBadgeForTab(ogTabs[1], earliestQueues_planetOrMoon.moon[i]); } createBadgeForTab_emptyQueues(ogTabs[1], "moon"); } // Function to create badge with number (earliest construction) to the tab function createBadgeForTab (tab, n) { // Early check if tab exists at all if (!tab) { console.error("IPO error: OGame tabs are not found!"); return; } // Use last defined color in "COLORS.earliest" array if "n" is bigger than the number of elements in this array let colorIndex = Math.min(n-1, COLORS.earliest.length-1); // Create the badge element const badge = document.createElement("div"); badge.className = IPO_TAB_BADGES_CLASS_NAME; badge.style.backgroundColor = COLORS.earliest[colorIndex]; badge.textContent = n; // Adjust the position if there is already badge present const BADGE_SPACING = BADGES_SIZE + 4; let existingBadges = tab.getElementsByClassName(IPO_TAB_BADGES_CLASS_NAME); if (existingBadges) { badge.style.right = `${BADGES_MARGIN + existingBadges.length * BADGE_SPACING}px`; } // Append the badge to the tab tab.appendChild(badge); } // Function to create badge for number of empty queues function createBadgeForTab_emptyQueues (tab, type) { // Early check if tab exists at all if (!tab) { console.error("IPO error: OGame tabs are not found!"); return; } // Get how many empty queeus are there, seperately on planets and moons let emptyQueues = type === "planet" ? document.getElementsByClassName(OG_PLANET_PRODUCTION_CLASS_NAME)[0].getElementsByClassName(IPO_EMPTY_QUEUE_CLASS_NAME).length : document.getElementsByClassName(OG_MOON_PRODUCTION_CLASS_NAME)[0].getElementsByClassName(IPO_EMPTY_QUEUE_CLASS_NAME).length; // Create the badge elements if there are any empty queues if (emptyQueues > 0) { const badge = document.createElement("div"); badge.className = IPO_TAB_BADGES_EMPTY_QUEUES_CLASS_NAME; badge.style.backgroundColor = COLORS.empty; badge.textContent = emptyQueues; // Append the badge to the tab tab.appendChild(badge); } } // *** SETTINGS *** // Function to create settings function createSettings () { // Create the main <div> element let div = document.createElement("div"); div.id = IPO_SETTINGS_ID; // Create the <table>, <tbody>, and <tr> element let table = document.createElement("table"); let tbody = document.createElement("tbody"); let tr = document.createElement("tr"); // *** SETTINGS HEADER *** // Create <td> element, zero width table cell let settingsHeader_td = document.createElement("td"); settingsHeader_td.style.position = "relative"; // Create <span> element let settingsHeader_span = document.createElement("span"); settingsHeader_span.className = IPO_SETTINGS_HEADER_CLASS_NAME; settingsHeader_span.textContent = locales.options; // Append elements settingsHeader_td.appendChild(settingsHeader_span); tr.appendChild(settingsHeader_td); // *** NUMBER OF EARLIEST QUEUES *** // Create <td> element let earliestQueue_td = document.createElement("td"); earliestQueue_td.style.position = "relative"; // Create <span> element let earliestQueue_span = document.createElement("span"); earliestQueue_span.className = IPO_BUTTONS_CLASS_NAME; earliestQueue_span.dataset.value = ipoSettings.n; earliestQueue_span.textContent = "#" + ipoSettings.n; earliestQueue_span.title = LOCALES[lang].earliestQueue.title; // Onclick funciton earliestQueue_span.addEventListener("click", () => { toggleEarliestQueueValue(earliestQueue_span); showRefreshMessage(); }); // Append elements earliestQueue_td.appendChild(earliestQueue_span); tr.appendChild(earliestQueue_td); // *** HIHGLIGHT CONSTRUCTION NEAR COMPLETION *** // Create <td> element let soonFinished_td = document.createElement("td"); // Create <span> element let soonFinished_span = document.createElement("span"); soonFinished_span.className = IPO_BUTTONS_CLASS_NAME; soonFinished_span.dataset.value = ipoSettings.soonFinished; soonFinished_span.textContent = ipoSettings.soonFinished + "h"; soonFinished_span.title = LOCALES[lang].soonFinished.title; // Onclick function soonFinished_span.addEventListener("click", () => { toggleSoonFinishedValue(soonFinished_span); showRefreshMessage(); }); // Append elements soonFinished_td.appendChild(soonFinished_span); tr.appendChild(soonFinished_td); // *** EMPTY QUEUES *** // *** RESEARCH (EMPTY QUEUE) *** // Create <td> element let researchEmptyQueue_td = document.createElement("td"); researchEmptyQueue_td.style.position = "relative"; // Create a wrapper <div> element [flex] let researchEmptyQueue_wrapper = document.createElement("div"); researchEmptyQueue_wrapper.className = IPO_SETTINGS_BUTTON_LABEL_WRAPPER_CLASS_NAME; // Create <span> element for label let researchEmptyQueue_label = document.createElement("span"); researchEmptyQueue_label.className = IPO_LABEL_CLASS_NAME; researchEmptyQueue_label.style.marginLeft = "10px"; researchEmptyQueue_label.textContent = locales.researchQueue + ": "; researchEmptyQueue_label.title = LOCALES[lang].emptyQueues.title + " (" + locales.researchQueue + ")."; // Create <button> element for toggle let researchEmptyQueue_toggleButton = document.createElement("button"); researchEmptyQueue_toggleButton.className = IPO_TOGGLE_BUTTONS_CLASS_NAME; researchEmptyQueue_toggleButton.dataset.type = "research"; researchEmptyQueue_toggleButton.title = locales.researchQueue; // Update color for the toggle button updateToggleButtonColor(researchEmptyQueue_toggleButton, "research", 0); // Onclick function researchEmptyQueue_toggleButton.addEventListener("click", () => { toggleQueueState(researchEmptyQueue_toggleButton, "research", 0); showRefreshMessage(); }); // Append elements researchEmptyQueue_wrapper.appendChild(researchEmptyQueue_label); researchEmptyQueue_wrapper.appendChild(researchEmptyQueue_toggleButton); researchEmptyQueue_td.appendChild(researchEmptyQueue_wrapper); tr.appendChild(researchEmptyQueue_td); // *** PLANETS (EMPTY QUEUES) *** // Create <td> element let planetEmptyQueues_td = document.createElement("td"); // Create a wrapper <div> element [flex] let planetEmptyQueues_wrapper = document.createElement("div"); planetEmptyQueues_wrapper.className = IPO_SETTINGS_BUTTON_LABEL_WRAPPER_CLASS_NAME; // Create <span> element for label and append it let planetsEmptyQueues_label = document.createElement("span"); planetsEmptyQueues_label.className = IPO_LABEL_CLASS_NAME; planetsEmptyQueues_label.textContent = locales.planet + ": "; planetsEmptyQueues_label.title = LOCALES[lang].emptyQueues.title + " (" + locales.planet + ")."; planetEmptyQueues_wrapper.appendChild(planetsEmptyQueues_label); // Create <button> elements (4 times) for (let i = 0; i < 4; i++) { let button = document.createElement("button"); button.className = IPO_TOGGLE_BUTTONS_CLASS_NAME; button.dataset.index = i; button.dataset.type = "planet"; button.title = locales.planetQueues[i]; updateToggleButtonColor(button, "planet", i); button.addEventListener("click", () => { toggleQueueState(button, "planet", i); showRefreshMessage(); }); planetEmptyQueues_wrapper.appendChild(button); } // Append elements planetEmptyQueues_td.appendChild(planetEmptyQueues_wrapper); tr.appendChild(planetEmptyQueues_td); // *** MOONS (EMPTY QUEUES) *** // Create <td> element let moonEmptyQueues_td = document.createElement("td"); // Create a wrapper <div> element [flex] let moonEmptyQueues_wrapper = document.createElement("div"); moonEmptyQueues_wrapper.className = IPO_SETTINGS_BUTTON_LABEL_WRAPPER_CLASS_NAME; // Create <span> element for label and append it let moonEmptyQueues_label = document.createElement("span"); moonEmptyQueues_label.className = IPO_LABEL_CLASS_NAME; moonEmptyQueues_label.textContent = locales.moon + ": "; moonEmptyQueues_label.title = LOCALES[lang].emptyQueues.title + " (" + locales.moon + ")."; moonEmptyQueues_wrapper.appendChild(moonEmptyQueues_label); // Create <button> elements (2 times) for (let i = 0; i < 2; i++) { let button = document.createElement("button"); button.className = IPO_TOGGLE_BUTTONS_CLASS_NAME; button.dataset.index = i; button.dataset.type = "moon"; button.title = locales.moonQueues[i] + " (" + locales.moon + ")"; updateToggleButtonColor(button, "moon", i); button.addEventListener("click", () => { toggleQueueState(button, "moon", i); showRefreshMessage(); }); moonEmptyQueues_wrapper.appendChild(button); } // Append elements moonEmptyQueues_td.appendChild(moonEmptyQueues_wrapper); tr.appendChild(moonEmptyQueues_td); // *** REFRESH MESSAGE *** // Add the refresh message element (hidden by default) let refreshMessage = document.createElement("div"); refreshMessage.id = IPO_REFRESH_MESSAGE_ID; refreshMessage.textContent = LOCALES[lang].refreshMessage; // Append table elements to main <div> tbody.appendChild(tr); table.appendChild(tbody); div.appendChild(table); // Append main <div> element and "refresh message" to the parent OGame element let appendLocation = document.getElementById(OG_PRODUCTION_QUEUE_COMPONENET_ID); appendLocation.appendChild(div); appendLocation.appendChild(refreshMessage); } // Function to show the refresh message after any button is pressed function showRefreshMessage () { let refreshMessage = document.getElementById(IPO_REFRESH_MESSAGE_ID); if (refreshMessage) { refreshMessage.style.opacity = "1"; } } // Function to toggle the queue state in local storage function toggleQueueState (button, type, index) { // Get local storage object let ipoSettings = getIpoSettings(); // Toggle settings ipoSettings.highlightEmpty[type][index] = !ipoSettings.highlightEmpty[type][index]; setIpoSettings(ipoSettings) updateToggleButtonColor(button, type, index); } // Function to update the button color based on its state function updateToggleButtonColor (button, type, index) { // Get local storage object let ipoSettings = getIpoSettings(); // Change styles if (ipoSettings.highlightEmpty[type][index]) { button.classList.add("active"); button.classList.remove("inactive"); } else { button.classList.add("inactive"); button.classList.remove("active"); } } // Function to toggle the value of n (nth earliest queue) function toggleEarliestQueueValue (span) { // Get current settings let ipoSettings = getIpoSettings(); // Advance to the next value; possible values: 0, 1, 2, and 3 ipoSettings.n = (ipoSettings.n + 1) % 4; // Save new settings to local storage setIpoSettings(ipoSettings); // Display new value span.dataset.value = ipoSettings.n; span.textContent = "#" + ipoSettings.n; } // Function to toggle the value of "soonFinished" function toggleSoonFinishedValue (span) { // Get current settings let ipoSettings = getIpoSettings(); // Define possible values of "soonFinished" [hours] let options = [0, 1, 8, 24]; // Advace value of the "soonFinished" to the next index let currentIndex = options.indexOf(ipoSettings.soonFinished); ipoSettings.soonFinished = options[(currentIndex + 1) % options.length]; // Save new settings to local storage setIpoSettings(ipoSettings); // Display new value span.dataset.value = ipoSettings.soonFinished; span.textContent = ipoSettings.soonFinished + "h"; }