// ==UserScript==
// @name MZ Tactics Manager
// @namespace douglaskampl
// @version 10.1.2
// @description Lets you manage your tactics in ManagerZone
// @author Douglas Vieira
// @match https://www.managerzone.com/?p=tactics
// @match https://www.managerzone.com/?p=national_teams&sub=tactics&type=*
// @icon https://yt3.googleusercontent.com/ytc/AIdro_mDHaJkwjCgyINFM7cdUV2dWPPnL9Q58vUsrhOmRqkatg=s160-c-k-c0x00ffffff-no-rj
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @require https://cdnjs.cloudflare.com/ajax/libs/jsSHA/3.3.1/sha256.js
// @require https://cdnjs.cloudflare.com/ajax/libs/i18next/23.7.16/i18next.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.10.2/sweetalert2.min.js
// @license MIT
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(`@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Dancing+Script:wght@500&display=swap"); #mz_tactics_panel {font-family: "Space Grotesk", -apple-system, sans-serif; background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); border-radius: 12px; padding: 20px; margin: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); border: 1px solid #1e293b; transition: opacity 0.3s ease, max-height 0.3s ease; max-height: 1000px; opacity: 1; color: #f8fafc; } .mz-group {background: linear-gradient(135deg, #334155 0%, #1e293b 100%); border-radius: 8px; padding: 16px; margin: 8px; border: 1px solid #334155; position: relative; } .mz-group-main-title {color: #f8fafc; font-size: 16px; font-weight: 500; margin: -4px 0 12px 0; padding-bottom: 8px; border-bottom: 1px solid rgba(248, 250, 252, 0.1); } .mz-main-title {color: #f8fafc; font-family: "Space Grotesk", sans-serif; font-size: 18px; font-weight: 500; margin: 0; padding: 0; text-align: center; letter-spacing: 0.2px; } .mz-version-text {color: #ff9933; font-family: "Dancing Script", cursive; font-size: 0.9em; font-weight: 500; margin-left: 4px; } .mz-divider {width: 40px; height: 2px; background: #64748b; margin: 8px auto 0; opacity: 0.3; } #mz_tactics_panel .mzbtn {display: inline-flex; align-items: center; padding: 8px 14px; margin: 4px; font-family: "Space Grotesk", sans-serif; font-size: 13px; font-weight: 500; color: #f8fafc; background: #334155; border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease; } #mz_tactics_panel .mzbtn:hover {background: #475569; transform: translateY(-1px); } #mz_tactics_panel .mzbtn:focus {outline: 2px solid #94a3b8; outline-offset: 1px; } #mz_tactics_panel select {font-family: "Space Grotesk", sans-serif; font-size: 13px; color: #f8fafc; padding: 8px 14px; border: 1px solid #334155; border-radius: 6px; background-color: #1e293b; cursor: pointer; margin: 4px; transition: all 0.2s ease; } #mz_tactics_panel select:focus {outline: none; border-color: #64748b; box-shadow: 0 0 0 2px rgba(100, 116, 139, 0.1); } #language_flag {height: 15px; width: 25px; margin: 6px 0 6px 6px; border: 1px solid #e2e8f0; border-radius: 2px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } #info_modal, #useful_links_modal {background: #1e293b; padding: 20px; border-radius: 12px; color: #f8fafc; width: 90%; max-width: 500px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); } #info_modal a, #useful_links_modal a {color: #60a5fa; } #info_modal ul, #useful_links_modal ul {list-style: none; padding: 0; } #info_modal ul li, #useful_links_modal ul li {margin: 10px 0; } .swal2-popup.swal-mz-popup {font-family: "Space Grotesk", sans-serif; border-radius: 8px; background: #1e293b; color: #f8fafc; } .swal2-popup.swal-mz-popup .swal2-title {font-size: 18px; color: #f8fafc; } .swal2-popup.swal-mz-popup .swal2-html-container {font-size: 14px; color: #f8fafc; } .swal2-popup.swal-mz-popup .swal2-input {font-size: 14px; border: 1px solid #334155; border-radius: 6px; background: #0f172a; color: #f8fafc; } .swal2-popup.swal-mz-popup .swal2-textarea {background: #0f172a; color: #f8fafc; border: 1px solid #334155; } .swal2-popup.swal-mz-popup .swal2-confirm {background: #475569 !important; } .swal2-popup.swal-mz-popup .swal2-cancel {background: #64748b !important; } .swal2-container.swal2-backdrop-show {background: rgba(0, 0, 0, 0.6); } #hidden_trigger_button {position: absolute; visibility: hidden; pointer-events: none; }`);
const OUTFIELD_PLAYERS_SELECTOR = ".fieldpos.fieldpos-ok.ui-draggable:not(.substitute):not(.goalkeeper):not(.substitute.goalkeeper), .fieldpos.fieldpos-collision.ui-draggable:not(.substitute):not(.goalkeeper):not(.substitute.goalkeeper)";
const GOALKEEPER_SELECTOR = ".fieldpos.fieldpos-ok.goalkeeper.ui-draggable";
const FORMATION_TEXT_SELECTOR = "#formation_text";
const TACTIC_SLOT_SELECTOR = ".ui-state-default.ui-corner-top.ui-tabs-selected.ui-state-active.invalid";
const MIN_OUTFIELD_PLAYERS = 10;
const MAX_TACTIC_NAME_LENGTH = 50;
const DEFAULT_TACTICS_DATA_URL = "https://u18mz.vercel.app/mz/userscript/tactics/json/defaultTactics.json";
const LANG_DATA_BASE_URL = "https://u18mz.vercel.app/mz/userscript/tactics/json/lang/";
const BASE_FLAG_URL = "https://flagcdn.com/w320/";
const LANGUAGES = [
{ code: "en", name: "English", flag: BASE_FLAG_URL + "gb.png" },
{ code: "pt", name: "Português", flag: BASE_FLAG_URL + "br.png" },
{ code: "zh", name: "中文", flag: BASE_FLAG_URL + "cn.png" },
{ code: "sv", name: "Svenska", flag: BASE_FLAG_URL + "se.png" },
{ code: "no", name: "Norsk", flag: BASE_FLAG_URL + "no.png" },
{ code: "da", name: "Dansk", flag: BASE_FLAG_URL + "dk.png" },
{ code: "es", name: "Español", flag: BASE_FLAG_URL + "ar.png" },
{ code: "pl", name: "Polski", flag: BASE_FLAG_URL + "pl.png" },
{ code: "nl", name: "Nederlands", flag: BASE_FLAG_URL + "nl.png" },
{ code: "id", name: "Bahasa Indonesia", flag: BASE_FLAG_URL + "id.png" },
{ code: "de", name: "Deutsch", flag: BASE_FLAG_URL + "de.png" },
{ code: "it", name: "Italiano", flag: BASE_FLAG_URL + "it.png" },
{ code: "fr", name: "Français", flag: BASE_FLAG_URL + "fr.png" },
{ code: "ro", name: "Română", flag: BASE_FLAG_URL + "ro.png" },
{ code: "tr", name: "Türkçe", flag: BASE_FLAG_URL + "tr.png" },
{ code: "ko", name: "한국어", flag: BASE_FLAG_URL + "kr.png" },
{ code: "ru", name: "Русский", flag: BASE_FLAG_URL + "ru.png" },
{ code: "ar", name: "العربية", flag: BASE_FLAG_URL + "sa.png" },
{ code: "jp", name: "日本語", flag: BASE_FLAG_URL + "jp.png" }
];
const VERSION = "10.1.2";
const VERSION_KEY = "mz_tactics_version";
const SWAL_CONSTANTS = {
ICONS: {
SUCCESS: 'success',
ERROR: 'error',
WARNING: 'warning'
}
};
const SWAL_CUSTOM_CLASS = {
popup: 'swal-mz-popup',
title: 'swal-mz-title',
htmlContainer: 'swal-mz-html-container',
input: 'swal-mz-input',
validationMessage: 'swal-mz-validation',
actions: 'swal-mz-actions',
confirmButton: 'swal-mz-confirm',
cancelButton: 'swal-mz-cancel',
closeButton: 'swal-mz-close'
};
const USERSCRIPT_STRINGS = {
addButton: "Add Tactic",
addWithXmlButton: "Add Tactic via XML",
deleteButton: "Delete Tactic",
renameButton: "Rename Tactic",
updateButton: "Update Tactic",
clearButton: "Clear Tactics",
resetButton: "Reset Tactics",
importButton: "Import Tactics",
exportButton: "Export Tactics",
usefulLinksButton: "Useful Links",
aboutButton: "About",
tacticNamePrompt: "Tactic Name:",
addAlert: "Tactic {} added successfully.",
deleteAlert: "Tactic {} deleted successfully.",
renameAlert: "Tactic {} renamed to {} successfully.",
updateAlert: "Tactic {} updated successfully.",
clearAlert: "Tactics cleared successfully.",
resetAlert: "Tactics reset successfully.",
importAlert: "Tactics imported successfully.",
exportAlert: "Tactics exported successfully.",
deleteConfirmation: "Do you really want to delete {}?",
updateConfirmation: "Do you really want to update {}?",
clearConfirmation: "Do you really want to clear tactics?",
resetConfirmation: "Do you really want to reset tactics?",
invalidTacticError: "Invalid tactic.",
noTacticNameProvidedError: "No tactic name provided.",
alreadyExistingTacticNameError: "Tactic name already exists.",
tacticNameMaxLengthError: "Tactic name is too long.",
noTacticSelectedError: "No tactic selected.",
duplicateTacticError: "Duplicate tactic.",
noChangesMadeError: "No changes made.",
invalidImportError: "Invalid import file.",
modalContentInfoText: "This is the tactic selector.",
modalContentFeedbackText: "Send your feedback.",
usefulContent: "Some useful resources:",
tacticsDropdownMenuLabel: "Tactics:",
languageDropdownMenuLabel: "Language:",
errorTitle: "Error",
doneTitle: "Done",
confirmationTitle: "Confirmation",
deleteTacticConfirmButton: "Delete",
cancelConfirmButton: "Cancel",
updateConfirmButton: "Update",
clearTacticsConfirmButton: "Clear",
resetTacticsConfirmButton: "Reset",
addConfirmButton: "Add",
xmlValidationError: "Invalid XML.",
xmlParsingError: "Error parsing XML.",
xmlPlaceholder: "Paste XML here",
tacticNamePlaceholder: "Name",
managerTitle: "MZ Tactics Manager",
tacticActionsTitle: "Actions",
otherActionsTitle: "Other",
welcomeMessage: "Welcome to MZ Tactics Manager! Enjoy using it!<br><br>If you got any questions or suggestions, feel free to message douglaskampl via chat or guestbook.",
welcomeGotIt: "Got it!"
};
const ELEMENT_STRING_KEYS = {
add_tactic_button: "addButton",
add_tactic_with_xml_button: "addWithXmlButton",
delete_tactic_button: "deleteButton",
rename_tactic_button: "renameButton",
update_tactic_button: "updateButton",
clear_tactics_button: "clearButton",
reset_tactics_button: "resetButton",
import_tactics_button: "importButton",
export_tactics_button: "exportButton",
about_button: "aboutButton",
tactics_dropdown_menu_label: "tacticsDropdownMenuLabel",
language_dropdown_menu_label: "languageDropdownMenuLabel",
info_modal_info_text: "modalContentInfoText",
info_modal_feedback_text: "modalContentFeedbackText",
useful_links_button: "usefulLinksButton",
};
let dropdownMenuTactics = [];
let activeLanguage;
let infoModal;
let usefulLinksModal;
function isFootball() {
const element = document.querySelector("div#tactics_box.soccer.clearfix");
return !!element;
}
function sha256Hash(str) {
const shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.update(str);
return shaObj.getHash("HEX");
}
function showAlert(options) {
const defaultOptions = {
customClass: SWAL_CUSTOM_CLASS,
buttonsStyling: true,
showClass: {
popup: 'swal-mz-popup modalFadeIn'
},
hideClass: {
popup: 'modalFadeOut'
},
allowOutsideClick: true,
allowEscapeKey: true,
width: options.html?.includes('swal-xml-input') ? '600px' : '300px',
padding: '20px'
};
if (options.customClass) {
options.customClass = { ...SWAL_CUSTOM_CLASS, ...options.customClass };
}
return Swal.fire({
...defaultOptions,
...options
});
}
function showSuccessMessage(title, text) {
return showAlert({
title,
text,
icon: SWAL_CONSTANTS.ICONS.SUCCESS
});
}
function showErrorMessage(title, text) {
return showAlert({
title,
text,
icon: SWAL_CONSTANTS.ICONS.ERROR
});
}
async function fetchTacticsFromGMStorage() {
const storedTactics = GM_getValue("ls_tactics");
if (storedTactics) {
return storedTactics;
} else {
const jsonTactics = await fetchTacticsFromJson();
storeTacticsInGMStorage(jsonTactics);
return jsonTactics;
}
}
async function fetchTacticsFromJson() {
const response = await fetch(DEFAULT_TACTICS_DATA_URL);
return await response.json();
}
function storeTacticsInGMStorage(data) {
GM_setValue("ls_tactics", data);
}
async function validateDuplicateTactic(id) {
const tacticsData = (await GM_getValue("ls_tactics")) || { tactics: [] };
return tacticsData.tactics.some((tactic) => tactic.id === id);
}
async function saveTacticToStorage(tactic) {
const tacticsData = (await GM_getValue("ls_tactics")) || { tactics: [] };
tacticsData.tactics.push(tactic);
await GM_setValue("ls_tactics", tacticsData);
}
async function validateDuplicateTacticWithUpdatedCoord(newId, selectedTac, tacticsData) {
if (newId === selectedTac.id) {
return "unchanged";
} else if (tacticsData.tactics.some((tac) => tac.id === newId)) {
return "duplicate";
} else {
return "unique";
}
}
function handleTacticsSelection(tactic) {
const outfieldPlayers = Array.from(document.querySelectorAll(OUTFIELD_PLAYERS_SELECTOR));
const selectedTactic = dropdownMenuTactics.find((tacticData) => tacticData.name === tactic);
if (selectedTactic) {
if (outfieldPlayers.length < MIN_OUTFIELD_PLAYERS) {
const hiddenTriggerButton = document.getElementById("hidden_trigger_button");
hiddenTriggerButton.click();
setTimeout(() => rearrangePlayers(selectedTactic.coordinates), 1);
} else {
rearrangePlayers(selectedTactic.coordinates);
}
}
}
function rearrangePlayers(coordinates) {
const outfieldPlayers = Array.from(document.querySelectorAll(OUTFIELD_PLAYERS_SELECTOR));
findBestPositions(outfieldPlayers, coordinates);
for (let i = 0; i < outfieldPlayers.length; ++i) {
outfieldPlayers[i].style.left = coordinates[i][0] + "px";
outfieldPlayers[i].style.top = coordinates[i][1] + "px";
removeCollision(outfieldPlayers[i]);
}
removeTacticSlotInvalidStatus();
updateFormationText(getFormation(coordinates));
}
function findBestPositions(players, coordinates) {
players.sort((a, b) => parseInt(a.style.top) - parseInt(b.style.top));
coordinates.sort((a, b) => a[1] - b[1]);
}
function removeCollision(player) {
if (player.classList.contains("fieldpos-collision")) {
player.classList.remove("fieldpos-collision");
player.classList.add("fieldpos-ok");
}
}
function removeTacticSlotInvalidStatus() {
const slot = document.querySelector(TACTIC_SLOT_SELECTOR);
if (slot) {
slot.classList.remove("invalid");
}
}
function updateFormationText(formation) {
const formationTextElement = document.querySelector(FORMATION_TEXT_SELECTOR);
formationTextElement.querySelector(".defs").textContent = formation.defenders;
formationTextElement.querySelector(".mids").textContent = formation.midfielders;
formationTextElement.querySelector(".atts").textContent = formation.strikers;
}
function getFormation(coordinates) {
let strikers = 0;
let midfielders = 0;
let defenders = 0;
for (const coo of coordinates) {
const y = coo[1];
if (y < 103) {
strikers++;
} else if (y <= 204) {
midfielders++;
} else {
defenders++;
}
}
return { strikers, midfielders, defenders };
}
function validateTacticPlayerCount(outfieldPlayers) {
const isGoalkeeper = document.querySelector(GOALKEEPER_SELECTOR);
outfieldPlayers = outfieldPlayers.filter((player) => !player.classList.contains("fieldpos-collision"));
if (outfieldPlayers.length < MIN_OUTFIELD_PLAYERS || !isGoalkeeper) {
showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.invalidTacticError);
return false;
}
return true;
}
async function addNewTactic() {
const outfieldPlayers = Array.from(document.querySelectorAll(OUTFIELD_PLAYERS_SELECTOR));
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
const tacticCoordinates = outfieldPlayers.map((player) => [parseInt(player.style.left), parseInt(player.style.top)]);
if (!validateTacticPlayerCount(outfieldPlayers)) {
return;
}
const tacticId = generateUniqueId(tacticCoordinates);
const isDuplicate = await validateDuplicateTactic(tacticId);
if (isDuplicate) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.duplicateTacticError);
return;
}
const result = await showAlert({
title: USERSCRIPT_STRINGS.tacticNamePrompt,
input: 'text',
inputValue: '',
inputValidator: (value) => {
if (!value) {
return USERSCRIPT_STRINGS.noTacticNameProvidedError;
}
if (value.length > MAX_TACTIC_NAME_LENGTH) {
return USERSCRIPT_STRINGS.tacticNameMaxLengthError;
}
if (dropdownMenuTactics.some((t) => t.name === value)) {
return USERSCRIPT_STRINGS.alreadyExistingTacticNameError;
}
},
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.addConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
const tacticName = result.value;
if (!tacticName) {
return;
}
const tactic = {
name: tacticName,
coordinates: tacticCoordinates,
id: tacticId
};
await saveTacticToStorage(tactic);
addTacticsToDropdownMenu(tacticsDropdownMenu, [tactic]);
dropdownMenuTactics.push(tactic);
const placeholderOption = tacticsDropdownMenu.querySelector('option[value=""]');
if (placeholderOption) {
placeholderOption.remove();
}
if (tacticsDropdownMenu.disabled) {
tacticsDropdownMenu.disabled = false;
}
tacticsDropdownMenu.value = tactic.name;
const changeEvent = new Event('change', { bubbles: true });
tacticsDropdownMenu.dispatchEvent(changeEvent);
handleTacticsSelection(tactic.name);
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.addAlert.replace("{}", tactic.name));
}
async function addNewTacticWithXml() {
const result = await showAlert({
title: USERSCRIPT_STRINGS.addWithXmlButton,
html:
`<textarea id="swal-xml-input" class="swal2-textarea swal-mz-input" placeholder="${USERSCRIPT_STRINGS.xmlPlaceholder}" style="height: 200px;"></textarea>` +
`<input id="swal-name-input" class="swal2-input swal-mz-input" placeholder="${USERSCRIPT_STRINGS.tacticNamePlaceholder}">`,
focusConfirm: false,
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.addConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton,
preConfirm: () => {
const xml = document.getElementById('swal-xml-input').value;
const name = document.getElementById('swal-name-input').value;
if (!xml) {
Swal.showValidationMessage(USERSCRIPT_STRINGS.xmlValidationError);
return false;
}
if (!name) {
Swal.showValidationMessage(USERSCRIPT_STRINGS.noTacticNameProvidedError);
return false;
}
if (name.length > MAX_TACTIC_NAME_LENGTH) {
Swal.showValidationMessage(USERSCRIPT_STRINGS.tacticNameMaxLengthError);
return false;
}
if (dropdownMenuTactics.some((t) => t.name === name)) {
Swal.showValidationMessage(USERSCRIPT_STRINGS.alreadyExistingTacticNameError);
return false;
}
return { xml, name };
}
});
if (!result.value) {
return;
}
try {
const { xml, name } = result.value;
const newTactic = await convertXmlToTacticJson(xml, name);
const tacticId = generateUniqueId(newTactic.coordinates);
const isDuplicate = await validateDuplicateTactic(tacticId);
if (isDuplicate) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.duplicateTacticError);
return;
}
newTactic.id = tacticId;
await saveTacticToStorage(newTactic);
const tacticsDropdownMenu = document.getElementById('tactics_dropdown_menu');
addTacticsToDropdownMenu(tacticsDropdownMenu, [newTactic]);
dropdownMenuTactics.push(newTactic);
tacticsDropdownMenu.value = newTactic.name;
handleTacticsSelection(newTactic.name);
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.addAlert.replace('{}', newTactic.name));
} catch (e) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.xmlParsingError);
}
}
async function deleteTactic() {
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
const selectedTactic = dropdownMenuTactics.find((tactic) => tactic.name === tacticsDropdownMenu.value);
if (!selectedTactic) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.noTacticSelectedError);
return;
}
const result = await showAlert({
text: USERSCRIPT_STRINGS.deleteConfirmation.replace("{}", selectedTactic.name),
icon: SWAL_CONSTANTS.ICONS.WARNING,
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.deleteTacticConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
if (!result.isConfirmed) {
return;
}
const tacticsData = (await GM_getValue("ls_tactics")) || { tactics: [] };
tacticsData.tactics = tacticsData.tactics.filter((tactic) => tactic.id !== selectedTactic.id);
await GM_setValue("ls_tactics", tacticsData);
dropdownMenuTactics = dropdownMenuTactics.filter((tactic) => tactic.id !== selectedTactic.id);
const selectedOption = Array.from(tacticsDropdownMenu.options).find((option) => option.value === selectedTactic.name);
tacticsDropdownMenu.remove(selectedOption.index);
if (tacticsDropdownMenu.options[0]?.disabled) {
tacticsDropdownMenu.selectedIndex = 0;
}
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.deleteAlert.replace("{}", selectedTactic.name));
}
async function renameTactic() {
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
const selectedTactic = dropdownMenuTactics.find((tactic) => tactic.name === tacticsDropdownMenu.value);
if (!selectedTactic) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.noTacticSelectedError);
return;
}
const oldName = selectedTactic.name;
const result = await showAlert({
title: USERSCRIPT_STRINGS.tacticNamePrompt,
input: 'text',
inputValue: oldName,
inputValidator: (value) => {
if (!value) {
return USERSCRIPT_STRINGS.noTacticNameProvidedError;
}
if (value.length > MAX_TACTIC_NAME_LENGTH) {
return USERSCRIPT_STRINGS.tacticNameMaxLengthError;
}
if (value !== oldName && dropdownMenuTactics.some((t) => t.name === value)) {
return USERSCRIPT_STRINGS.alreadyExistingTacticNameError;
}
},
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.updateConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
const newName = result.value;
if (!newName) {
return;
}
const selectedOption = Array.from(tacticsDropdownMenu.options).find((option) => option.value === selectedTactic.name);
const tacticsData = (await GM_getValue("ls_tactics")) || { tactics: [] };
tacticsData.tactics = tacticsData.tactics.map((tactic) => {
if (tactic.id === selectedTactic.id) {
tactic.name = newName;
}
return tactic;
});
await GM_setValue("ls_tactics", tacticsData);
dropdownMenuTactics = dropdownMenuTactics.map((tactic) => {
if (tactic.id === selectedTactic.id) {
tactic.name = newName;
}
return tactic;
});
selectedOption.value = newName;
selectedOption.textContent = newName;
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.renameAlert.replace("{}", oldName).replace("{}", newName));
}
async function updateTactic() {
const outfieldPlayers = Array.from(document.querySelectorAll(OUTFIELD_PLAYERS_SELECTOR));
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
const selectedTactic = dropdownMenuTactics.find((tactic) => tactic.name === tacticsDropdownMenu.value);
if (!selectedTactic) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.noTacticSelectedError);
return;
}
const updatedCoordinates = outfieldPlayers.map((player) => [parseInt(player.style.left), parseInt(player.style.top)]);
const newId = generateUniqueId(updatedCoordinates);
const tacticsData = (await GM_getValue("ls_tactics")) || { tactics: [] };
const validationOutcome = await validateDuplicateTacticWithUpdatedCoord(newId, selectedTactic, tacticsData);
if (validationOutcome === "unchanged") {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.noChangesMadeError);
return;
} else if (validationOutcome === "duplicate") {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.duplicateTacticError);
return;
}
const result = await showAlert({
text: USERSCRIPT_STRINGS.updateConfirmation.replace("{}", selectedTactic.name),
icon: SWAL_CONSTANTS.ICONS.WARNING,
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.updateConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
if (!result.isConfirmed) {
return;
}
for (const tactic of tacticsData.tactics) {
if (tactic.id === selectedTactic.id) {
tactic.coordinates = updatedCoordinates;
tactic.id = newId;
}
}
for (const tactic of dropdownMenuTactics) {
if (tactic.id === selectedTactic.id) {
tactic.coordinates = updatedCoordinates;
tactic.id = newId;
}
}
await GM_setValue("ls_tactics", tacticsData);
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.updateAlert.replace("{}", selectedTactic.name));
}
async function clearTactics() {
const result = await showAlert({
text: USERSCRIPT_STRINGS.clearConfirmation,
icon: SWAL_CONSTANTS.ICONS.WARNING,
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.clearTacticsConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
if (!result.isConfirmed) {
return;
}
await GM_setValue("ls_tactics", { tactics: [] });
dropdownMenuTactics = [];
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
tacticsDropdownMenu.innerHTML = "";
tacticsDropdownMenu.disabled = true;
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.clearAlert);
}
async function resetTactics() {
const result = await showAlert({
text: USERSCRIPT_STRINGS.resetConfirmation,
icon: SWAL_CONSTANTS.ICONS.WARNING,
showCancelButton: true,
confirmButtonText: USERSCRIPT_STRINGS.resetTacticsConfirmButton,
cancelButtonText: USERSCRIPT_STRINGS.cancelConfirmButton
});
if (!result.isConfirmed) {
return;
}
const response = await fetch(DEFAULT_TACTICS_DATA_URL);
const data = await response.json();
const defaultTactics = data.tactics;
await GM_setValue("ls_tactics", { tactics: defaultTactics });
dropdownMenuTactics = defaultTactics;
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
tacticsDropdownMenu.innerHTML = "";
tacticsDropdownMenu.appendChild(createPlaceholderOption());
addTacticsToDropdownMenu(tacticsDropdownMenu, dropdownMenuTactics);
tacticsDropdownMenu.disabled = false;
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.resetAlert);
}
async function importTactics() {
const input = document.createElement("input");
input.type = "file";
input.accept = ".json";
input.onchange = async function (event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = async function (event) {
let importedData;
try {
importedData = JSON.parse(event.target.result);
} catch (e) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.invalidImportError);
return;
}
if (!importedData || !Array.isArray(importedData.tactics)) {
await showErrorMessage(USERSCRIPT_STRINGS.errorTitle, USERSCRIPT_STRINGS.invalidImportError);
return;
}
const importedTactics = importedData.tactics;
let existingTactics = await GM_getValue("ls_tactics", { tactics: [] });
existingTactics = existingTactics.tactics;
const mergedTactics = [...existingTactics];
for (const importedTactic of importedTactics) {
if (!existingTactics.some((tactic) => tactic.id === importedTactic.id)) {
mergedTactics.push(importedTactic);
}
}
await GM_setValue("ls_tactics", { tactics: mergedTactics });
mergedTactics.sort((a, b) => a.name.localeCompare(b.name));
dropdownMenuTactics = mergedTactics;
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
tacticsDropdownMenu.innerHTML = "";
tacticsDropdownMenu.append(createPlaceholderOption());
addTacticsToDropdownMenu(tacticsDropdownMenu, dropdownMenuTactics);
tacticsDropdownMenu.disabled = false;
await showSuccessMessage(USERSCRIPT_STRINGS.doneTitle, USERSCRIPT_STRINGS.importAlert);
};
reader.readAsText(file);
};
input.click();
}
function exportTactics() {
const tactics = GM_getValue("ls_tactics", { tactics: [] });
const tacticsJson = JSON.stringify(tactics);
const blob = new Blob([tacticsJson], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "tactics.json";
const onFocus = () => {
window.removeEventListener('focus', onFocus);
URL.revokeObjectURL(url);
};
window.addEventListener('focus', onFocus, { once: true });
link.click();
}
async function convertXmlToTacticJson(xmlString, tacticName) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
const parserError = xmlDoc.getElementsByTagName('parsererror');
if (parserError.length > 0) {
throw new Error('Invalid XML');
}
const posElements = Array.from(xmlDoc.getElementsByTagName('Pos'));
const normalPosElements = posElements.filter(el => el.getAttribute('pos') === 'normal');
const coordinates = normalPosElements.map(el => {
const x = parseInt(el.getAttribute('x'));
const y = parseInt(el.getAttribute('y'));
const htmlLeft = x - 7;
const htmlTop = y - 9;
return [htmlLeft, htmlTop];
});
return {
name: tacticName,
coordinates: coordinates
};
}
function createAddNewTacticButton() {
const button = document.createElement("button");
setUpButton(button, "add_tactic_button", USERSCRIPT_STRINGS.addButton);
button.addEventListener("click", function () {
addNewTactic().catch((_) => { });
});
return button;
}
function createAddNewTacticWithXmlButton() {
const button = document.createElement("button");
setUpButton(button, "add_tactic_with_xml_button", USERSCRIPT_STRINGS.addWithXmlButton);
button.addEventListener("click", function () {
addNewTacticWithXml().catch((_) => { });
});
return button;
}
function createDeleteTacticButton() {
const button = document.createElement("button");
setUpButton(button, "delete_tactic_button", USERSCRIPT_STRINGS.deleteButton);
button.addEventListener("click", function () {
deleteTactic().catch((_) => { });
});
return button;
}
function createRenameTacticButton() {
const button = document.createElement("button");
setUpButton(button, "rename_tactic_button", USERSCRIPT_STRINGS.renameButton);
button.addEventListener("click", function () {
renameTactic().catch((_) => { });
});
return button;
}
function createUpdateTacticButton() {
const button = document.createElement("button");
setUpButton(button, "update_tactic_button", USERSCRIPT_STRINGS.updateButton);
button.addEventListener("click", function () {
updateTactic().catch((_) => { });
});
return button;
}
function createClearTacticsButton() {
const button = document.createElement("button");
setUpButton(button, "clear_tactics_button", USERSCRIPT_STRINGS.clearButton);
button.addEventListener("click", function () {
clearTactics().catch((_) => { });
});
return button;
}
function createResetTacticsButton() {
const button = document.createElement("button");
setUpButton(button, "reset_tactics_button", USERSCRIPT_STRINGS.resetButton);
button.addEventListener("click", function () {
resetTactics().catch((_) => { });
});
return button;
}
function createImportTacticsButton() {
const button = document.createElement("button");
setUpButton(button, "import_tactics_button", USERSCRIPT_STRINGS.importButton);
button.addEventListener("click", function () {
importTactics().catch((_) => { });
});
return button;
}
function createExportTacticsButton() {
const button = document.createElement("button");
setUpButton(button, "export_tactics_button", USERSCRIPT_STRINGS.exportButton);
button.addEventListener("click", function () {
exportTactics();
});
return button;
}
async function checkVersion() {
const storedVersion = GM_getValue(VERSION_KEY, null);
if (!storedVersion || storedVersion !== VERSION) {
await showWelcomeMessage();
GM_setValue(VERSION_KEY, VERSION);
}
}
async function showWelcomeMessage() {
await showAlert({
html: `
<div style="text-align: left; margin: 1em 0;">
<p>${USERSCRIPT_STRINGS.welcomeMessage}</p>
</div>
`,
icon: 'info',
confirmButtonText: USERSCRIPT_STRINGS.welcomeGotIt,
customClass: {
popup: 'swal-mz-popup',
confirmButton: 'swal-mz-confirm'
},
showClass: {
popup: 'swal-mz-popup modalFadeIn'
},
hideClass: {
popup: 'modalFadeOut'
}
});
}
function playRandomAudio(audios) {
if (audios.length === 0) {
return;
}
const randomIdx = Math.floor(Math.random() * audios.length);
const activeAudio = audios.splice(randomIdx, 1)[0];
playAudio(activeAudio, audios);
return activeAudio;
}
function playAudio(currAudio, audios) {
currAudio.play();
currAudio.onended = function () {
playRandomAudio(audios);
};
}
function pauseAudio(audio) {
if (audio) {
audio.pause();
audio.currentTime = 0;
}
}
function updateAudioIcon(button, isPlaying) {
button.textContent = isPlaying ? "⏸️" : "🔊";
}
function createAudioButton() {
const button = document.createElement("button");
setUpButton(button, "audio_button", "🔊");
const audioUrls = [
"https://ia801901.us.archive.org/31/items/corp.-palm-mall-01-palm-mall/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20Palm%20Mall%20-%2003%20Special%20Discount.mp3",
"https://ia801901.us.archive.org/31/items/corp.-palm-mall-01-palm-mall/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20Palm%20Mall%20-%2004%20First%20Floor.mp3",
"https://ia801901.us.archive.org/31/items/corp.-palm-mall-01-palm-mall/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20Palm%20Mall%20-%2006%20Second%20Floor.mp3",
"https://ia801901.us.archive.org/7/items/palm-mall-mars-remastered/%E7%8C%AB%20%E3%82%B7%20Corp.%20%26%20SEPHORA%E8%84%B3%E3%83%90%E3%82%A4%E3%83%96%E3%82%B9%20-%20Palm%20Mall%20Mars%20%28remastered%29%20-%2006%20Second%20floor-%20%ED%99%98%EB%8C%80%20%26%20%EC%9D%8C%EC%95%85.mp3",
"https://ia801901.us.archive.org/7/items/palm-mall-mars-remastered/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20Palm%20Mall%20Mars%20%28remastered%29%20-%2001%20%E3%82%B9%E3%82%AD%E3%83%9D%E3%83%BC%E3%83%AB%E7%A9%BA%E6%B8%AFPlaza.mp3",
"https://ia801901.us.archive.org/7/items/palm-mall-mars-remastered/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20Palm%20Mall%20Mars%20%28remastered%29%20-%2009%20Sembikiya%20Restaurant.mp3",
"https://ia804504.us.archive.org/20/items/5-wn9896/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%20%40%20%E3%83%98%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AD%20-%2001%20FORUM%20%E6%B6%88%E8%B2%BB%E8%80%85-kuluttaja-.mp3",
"https://ia904504.us.archive.org/20/items/5-wn9896/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%20%40%20%E3%83%98%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AD%20-%2002%20Pelican%20Self%20Storage%20-Tilaa%20Kaikelle-.mp3",
"https://ia904504.us.archive.org/20/items/5-wn9896/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%20%40%20%E3%83%98%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AD%20-%2003%20%E8%B2%B7%E3%81%86%40JUMBO%20-Kauppakeskus-.mp3",
"https://ia904504.us.archive.org/20/items/5-wn9896/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%20%40%20%E3%83%98%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AD%20-%2005%20Hesburger%20%E6%98%A0%E7%94%BB%E9%A4%A8%20-hampurilainen-.mp3",
"https://ia804504.us.archive.org/20/items/5-wn9896/%E7%8C%AB%20%E3%82%B7%20Corp.%20-%20%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%20%40%20%E3%83%98%E3%83%AB%E3%82%B7%E3%83%B3%E3%82%AD%20-%2006%20%E9%83%BD%E5%B8%82%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A9%E3%83%A0%20Consumer%20-kahvi-.mp3"
];
const audios = audioUrls.map(url => new Audio(url));
let isPlaying = false;
let currentAudio = null;
button.addEventListener("click", function () {
if (!isPlaying) {
currentAudio = playRandomAudio(audios);
isPlaying = true;
} else {
pauseAudio(currentAudio);
isPlaying = false;
}
updateAudioIcon(button, isPlaying);
});
return button;
}
function createMainContainer() {
const container = document.createElement("div");
container.id = "mz_tactics_panel";
container.classList.add("mz-panel");
const tacticGroup = document.createElement("div");
tacticGroup.classList.add("mz-group");
const mainTitle = document.createElement("h2");
mainTitle.classList.add("mz-group-main-title");
const titleText = document.createElement("span");
titleText.textContent = "MZ Tactics Manager ";
mainTitle.appendChild(titleText);
const authorText = document.createElement("span");
authorText.textContent = "v10.1.2";
authorText.classList.add("mz-version-text");
mainTitle.appendChild(authorText);
const dropdownSection = document.createElement("div");
const tacticsDropdownMenuLabel = createDropdownMenuLabel("tactics_dropdown_menu_label");
const tacticsDropdownMenu = createTacticsDropdownMenu();
const tacticsDropdownGroup = createLabelDropdownMenuGroup(tacticsDropdownMenuLabel, tacticsDropdownMenu);
dropdownSection.appendChild(tacticsDropdownGroup);
const buttonsSection = document.createElement("div");
buttonsSection.style.marginTop = "10px";
const addNewTacticBtn = createAddNewTacticButton();
const addNewTacticWithXmlBtn = createAddNewTacticWithXmlButton();
const deleteTacticBtn = createDeleteTacticButton();
const renameTacticBtn = createRenameTacticButton();
const updateTacticBtn = createUpdateTacticButton();
const clearTacticsBtn = createClearTacticsButton();
const resetTacticsBtn = createResetTacticsButton();
const importTacticsBtn = createImportTacticsButton();
const exportTacticsBtn = createExportTacticsButton();
appendChildren(buttonsSection, [
addNewTacticBtn,
addNewTacticWithXmlBtn,
deleteTacticBtn,
renameTacticBtn,
updateTacticBtn,
clearTacticsBtn,
resetTacticsBtn,
importTacticsBtn,
exportTacticsBtn
]);
appendChildren(tacticGroup, [
mainTitle,
dropdownSection,
buttonsSection,
createHiddenTriggerButton()
]);
const otherGroup = document.createElement("div");
otherGroup.classList.add("mz-group");
const otherContainer = document.createElement("div");
otherContainer.style.display = "flex";
otherContainer.style.justifyContent = "space-between";
otherContainer.style.alignItems = "center";
otherContainer.style.width = "100%";
const otherLeftGroup = document.createElement("div");
otherLeftGroup.style.display = "flex";
otherLeftGroup.style.alignItems = "center";
const usefulLinksBtn = createUsefulLinksButton();
const aboutBtn = createAboutButton();
const audioBtn = createAudioButton();
appendChildren(otherLeftGroup, [usefulLinksBtn, aboutBtn, audioBtn]);
const otherRightGroup = document.createElement("div");
otherRightGroup.style.display = "flex";
otherRightGroup.style.alignItems = "center";
const languageDropdownMenuLabel = createDropdownMenuLabel("language_dropdown_menu_label");
const languageDropdownMenu = createLanguageDropdownMenu();
const languageDropdownGroup = createLabelDropdownMenuGroup(languageDropdownMenuLabel, languageDropdownMenu);
const flagImage = createFlagImage();
appendChildren(otherRightGroup, [languageDropdownGroup, flagImage]);
appendChildren(otherContainer, [otherLeftGroup, otherRightGroup]);
appendChildren(otherGroup, [otherContainer]);
appendChildren(container, [tacticGroup, otherGroup]);
return container;
}
function createHiddenTriggerButton() {
const button = document.createElement("button");
button.id = "hidden_trigger_button";
button.textContent = "";
button.style.visibility = "hidden";
button.addEventListener("click", function () {
const tacticsPresetInfo = {
elem: document.getElementById("tactics_preset"),
resetValue: "5-3-2"
}
tacticsPresetInfo.elem.value = tacticsPresetInfo.resetValue;
tacticsPresetInfo.elem.dispatchEvent(new Event("change"));
});
return button;
}
function insertAfterElement(something, element) {
element.parentNode.insertBefore(something, element.nextSibling);
}
function appendChildren(parent, children) {
children.forEach((ch) => {
parent.appendChild(ch);
});
}
function setUpButton(button, id, textContent) {
button.id = id;
button.classList.add('mzbtn');
button.textContent = textContent;
}
function createTacticsDropdownMenu() {
const dropdown = document.createElement("select");
setUpDropdownMenu(dropdown, "tactics_dropdown_menu");
appendChildren(dropdown, [createPlaceholderOption()]);
return dropdown;
}
function createDropdownMenuLabel(labelId) {
const label = document.createElement("span");
setUpDropdownMenuLabel(label, labelId, USERSCRIPT_STRINGS.languageDropdownMenuLabel);
return label;
}
function createLabelDropdownMenuGroup(label, dropdown) {
const group = document.createElement("div");
group.classList.add('dropdown-group');
group.appendChild(label);
group.appendChild(dropdown);
return group;
}
function setUpDropdownMenu(dropdown, id) {
dropdown.id = id;
}
function createPlaceholderOption() {
const placeholderOption = document.createElement("option");
placeholderOption.value = "";
placeholderOption.text = "";
placeholderOption.disabled = true;
placeholderOption.selected = true;
return placeholderOption;
}
function addTacticsToDropdownMenu(dropdown, tactics) {
for (const tactic of tactics) {
const option = document.createElement("option");
option.value = tactic.name;
option.text = tactic.name;
dropdown.appendChild(option);
}
}
function setUpDropdownMenuLabel(description, id, textContent) {
description.id = id;
description.textContent = textContent;
}
function createLanguageDropdownMenu() {
const dropdown = document.createElement("select");
setUpDropdownMenu(dropdown, "language_dropdown_menu");
for (const lang of LANGUAGES) {
const option = document.createElement("option");
option.value = lang.code;
option.textContent = lang.name;
if (lang.code === activeLanguage) {
option.selected = true;
}
dropdown.appendChild(option);
}
dropdown.addEventListener("change", function () {
changeLanguage(this.value).catch((_) => { });
});
return dropdown;
}
function createFlagImage() {
const img = document.createElement("img");
img.id = "language_flag";
const activeLang = LANGUAGES.find((lang) => lang.code === activeLanguage);
if (activeLang) {
img.src = activeLang.flag;
}
return img;
}
function getActiveLanguage() {
let language = GM_getValue("language");
if (!language) {
let browserLanguage = navigator.language || "en";
browserLanguage = browserLanguage.split("-")[0];
const languageExists = LANGUAGES.some((lang) => lang.code === browserLanguage);
language = languageExists ? browserLanguage : "en";
}
return language;
}
function updateTranslation() {
for (const key in USERSCRIPT_STRINGS) {
USERSCRIPT_STRINGS[key] = i18next.t(key);
}
for (const id in ELEMENT_STRING_KEYS) {
const element = document.getElementById(id);
if (id === "info_modal_info_text" || id === "info_modal_feedback_text") {
if (element) element.innerHTML = USERSCRIPT_STRINGS[ELEMENT_STRING_KEYS[id]];
} else if (element) {
element.textContent = USERSCRIPT_STRINGS[ELEMENT_STRING_KEYS[id]];
}
}
}
async function changeLanguage(languageCode) {
try {
const translationDataUrl = LANG_DATA_BASE_URL + languageCode + ".json";
const translations = await (await fetch(translationDataUrl)).json();
i18next.changeLanguage(languageCode);
i18next.addResourceBundle(languageCode, "translation", translations);
GM_setValue("language", languageCode);
updateTranslation();
const language = LANGUAGES.find((lang) => lang.code === languageCode);
if (language) {
const flagImage = document.getElementById("language_flag");
if (flagImage) flagImage.src = language.flag;
}
} catch (_e) { }
}
function generateUniqueId(coordinates) {
const sortedCoordinates = coordinates.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
const coordString = sortedCoordinates.map((coord) => coord[1] + "_" + coord[0]).join("_");
return sha256Hash(coordString);
}
function createUsefulLinksModal() {
const modal = document.createElement("div");
setUpModal(modal, "useful_links_modal");
const modalContent = createUsefulLinksModalContent();
modal.appendChild(modalContent);
return modal;
}
function createUsefulLinksModalContent() {
const modalContent = document.createElement("div");
styleModalContent(modalContent);
const usefulContent = createUsefulContent();
const resources = new Map([
["gewlaht - BoooM", "https://www.managerzone.com/?p=forum&sub=topic&topic_id=11415137&forum_id=49&sport=soccer"],
["taktikskola by honken91", "https://www.managerzone.com/?p=forum&sub=topic&topic_id=12653892&forum_id=4&sport=soccer"],
["peto - mix de dibujos", "https://www.managerzone.com/?p=forum&sub=topic&topic_id=12196312&forum_id=255&sport=soccer"],
["The Zone Chile", "https://www.managerzone.com/thezone/paper.php?paper_id=18036&page=9&sport=soccer"],
["Tactics guide by lukasz87o/filipek4", "https://www.managerzone.com/?p=forum&sub=topic&topic_id=12766444&forum_id=12&sport=soccer&share_sport=soccer"],
["MZExtension/van.mz.playerAdvanced by vanjoge", "https://greasyfork.org/pt-BR/scripts/373382-van-mz-playeradvanced"],
["Mazyar Userscript", "https://greasyfork.org/pt-BR/scripts/476290-mazyar"],
["Stats Xente Userscript", "https://greasyfork.org/pt-BR/scripts/491442-stats-xente-script"],
["More userscripts", "https://greasyfork.org/pt-BR/users/1088808-douglasdotv"]
]);
const usefulLinksList = createLinksList(resources);
modalContent.appendChild(usefulContent);
modalContent.appendChild(usefulLinksList);
return modalContent;
}
function createUsefulContent() {
const usefulContent = document.createElement("p");
usefulContent.id = "useful_content";
usefulContent.textContent = "";
return usefulContent;
}
function createLinksList(hrefs) {
const list = document.createElement("ul");
hrefs.forEach((href, title) => {
const listItem = document.createElement("li");
const link = document.createElement("a");
link.href = href;
link.textContent = title;
listItem.appendChild(link);
list.appendChild(listItem);
});
return list;
}
function setUsefulLinksModal() {
usefulLinksModal = createUsefulLinksModal();
document.body.appendChild(usefulLinksModal);
}
function createInfoModal() {
const modal = document.createElement("div");
setUpModal(modal, "info_modal");
const modalContent = createModalContent();
modal.appendChild(modalContent);
return modal;
}
function createModalContent() {
const modalContent = document.createElement("div");
styleModalContent(modalContent);
const title = createTitle();
const infoText = createInfoText();
const feedbackText = createFeedbackText();
modalContent.appendChild(title);
modalContent.appendChild(infoText);
modalContent.appendChild(feedbackText);
return modalContent;
}
function createTitle() {
const title = document.createElement("h2");
title.id = "info_modal_title";
title.textContent = "";
title.style.fontSize = "24px";
title.style.fontWeight = "bold";
title.style.marginBottom = "20px";
return title;
}
function createInfoText() {
const infoText = document.createElement("p");
infoText.id = "info_modal_info_text";
infoText.innerHTML = USERSCRIPT_STRINGS.modalContentInfoText;
return infoText;
}
function createFeedbackText() {
const feedbackText = document.createElement("p");
feedbackText.id = "info_modal_feedback_text";
feedbackText.innerHTML = USERSCRIPT_STRINGS.modalContentFeedbackText;
return feedbackText;
}
function setInfoModal() {
infoModal = createInfoModal();
document.body.appendChild(infoModal);
}
function setUpModal(modal, id) {
modal.id = id;
modal.style.display = "none";
modal.style.position = "fixed";
modal.style.zIndex = "1";
modal.style.left = "50%";
modal.style.top = "50%";
modal.style.transform = "translate(-50%, -50%)";
modal.style.opacity = "0";
modal.style.transition = "opacity 0.5s ease-in-out";
}
function styleModalContent(content) {
content.classList.add('swal-mz-content');
}
function toggleModal(modal) {
if (modal.style.display === "none" || modal.style.opacity === "0") {
showModal(modal);
} else {
hideModal(modal);
}
}
function showModal(modal) {
modal.style.display = "block";
setTimeout(function () {
modal.style.opacity = "1";
}, 0);
}
function hideModal(modal) {
modal.style.opacity = "0";
setTimeout(function () {
modal.style.display = "none";
}, 500);
}
function setUpModalsWindowClickListener() {
window.addEventListener("click", function (event) {
if (usefulLinksModal.style.display === "block" && !usefulLinksModal.contains(event.target)) {
hideModal(usefulLinksModal);
}
if (infoModal.style.display === "block" && !infoModal.contains(event.target)) {
hideModal(infoModal);
}
});
}
function createUsefulLinksButton() {
const button = document.createElement("button");
setUpButton(button, "useful_links_button", USERSCRIPT_STRINGS.usefulLinksButton);
button.addEventListener("click", function (event) {
event.stopPropagation();
toggleModal(usefulLinksModal);
});
return button;
}
function createAboutButton() {
const button = document.createElement("button");
setUpButton(button, "about_button", USERSCRIPT_STRINGS.aboutButton);
button.addEventListener("click", function (event) {
event.stopPropagation();
toggleModal(infoModal);
});
return button;
}
function initialize() {
const tacticsBox = document.getElementById("tactics_box");
if (tacticsBox) {
activeLanguage = getActiveLanguage();
i18next.init({
lng: activeLanguage,
resources: {
[activeLanguage]: {
translation: {}
}
}
}).then(async () => {
const res = await fetch(LANG_DATA_BASE_URL + activeLanguage + ".json");
const json = await res.json();
i18next.addResourceBundle(activeLanguage, "translation", json);
await checkVersion();
const mainContainer = createMainContainer();
if (isFootball()) {
insertAfterElement(mainContainer, tacticsBox);
}
fetchTacticsFromGMStorage().then((data) => {
const tacticsDropdownMenu = document.getElementById("tactics_dropdown_menu");
tacticsDropdownMenu.addEventListener('click', function () {
if (this.value) {
handleTacticsSelection(this.value);
}
});
dropdownMenuTactics = data.tactics;
dropdownMenuTactics.sort((a, b) => a.name.localeCompare(b.name));
addTacticsToDropdownMenu(tacticsDropdownMenu, dropdownMenuTactics);
tacticsDropdownMenu.addEventListener("change", function () {
handleTacticsSelection(this.value);
});
}).catch((_) => { });
setInfoModal();
setUsefulLinksModal();
setUpModalsWindowClickListener();
updateTranslation();
});
}
}
window.addEventListener("load", function () {
initialize();
});
})();