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.2
// @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.querySelector('a[href*="component=preferences"]') || 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];
if (!text) {
// console.warn(`"text" not found.\n\nFunction's parameters:`, arguments);
return;
}
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";
}