Wplace-BlueMarble/docs/apiManager.js.html
2026-03-17 01:31:43 +00:00

363 lines
33 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>apiManager.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="ApiManager.html">ApiManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="ConfettiManager.html">ConfettiManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Observers.html">Observers</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Overlay.html">Overlay</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="SettingsManager.html">SettingsManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Template.html">Template</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TemplateManager.html">TemplateManager</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowCredits.html">WindowCredits</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowFilter.html">WindowFilter</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowMain.html">WindowMain</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowSettings.html">WindowSettings</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowTelemetry.html">WindowTelemetry</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="WindowWizard.html">WindowWizard</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="module.exports_module.exports.html">exports</a></span></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addBr">addBr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addButton">addButton</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addButtonHelp">addButtonHelp</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addCaption">addCaption</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addCheckbox">addCheckbox</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDetails">addDetails</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDiv">addDiv</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addDragbar">addDragbar</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addFieldset">addFieldset</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addForm">addForm</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addHeader">addHeader</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addHr">addHr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addImg">addImg</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addInput">addInput</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addInputFile">addInputFile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addLegend">addLegend</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addLi">addLi</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addMenu">addMenu</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addOl">addOl</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addOption">addOption</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addP">addP</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSelect">addSelect</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSmall">addSmall</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSpan">addSpan</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addSummary">addSummary</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTable">addTable</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTbody">addTbody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTd">addTd</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTextarea">addTextarea</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTfoot">addTfoot</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTh">addTh</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addThead">addThead</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTimer">addTimer</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addTr">addTr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#addUl">addUl</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#base64ToUint8">base64ToUint8</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildElement">buildElement</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildHighlight">buildHighlight</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildOverlay">buildOverlay</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildTemplate">buildTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildWindow">buildWindow</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildWindowed">buildWindowed</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateCanvasTransparency">calculateCanvasTransparency</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateCoordsFromChunked">calculateCoordsFromChunked</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#calculateRelativeLuminance">calculateRelativeLuminance</a></span></li><li class="nav-item"><span class="nav-item-type type-member">M</span><span class="nav-item-name"><a href="global.html#colorpalette">colorpalette</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#colorpaletteForBlueMarble">colorpaletteForBlueMarble</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleError">consoleError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleLog">consoleLog</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#consoleWarn">consoleWarn</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#convertTemplateToBlob">convertTemplateToBlob</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createConfetti">createConfetti</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createJSON">createJSON</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createObserverBody">createObserverBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createTemplate">createTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#createTemplateTiles">createTemplateTiles</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deleteTemplate">deleteTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#disableTemplate">disableTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadAllTemplates">downloadAllTemplates</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadAllTemplatesFromStorage">downloadAllTemplatesFromStorage</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#downloadTemplate">downloadTemplate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#drawTemplateOnTile">drawTemplateOnTile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#encodedToNumber">encodedToNumber</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#escapeHTML">escapeHTML</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getClipboardData">getClipboardData</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getObserverBody">getObserverBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getWplaceVersion">getWplaceVersion</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDisplayError">handleDisplayError</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDisplayStatus">handleDisplayStatus</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleDrag">handleDrag</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#handleMinimization">handleMinimization</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#hexToRGB">hexToRGB</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#importJSON">importJSON</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#inject">inject</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeDate">localizeDate</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeDuration">localizeDuration</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizeNumber">localizeNumber</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#localizePercent">localizePercent</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#negativeSafeModulo">negativeSafeModulo</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#numberToEncoded">numberToEncoded</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#observe">observe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#observeBlack">observeBlack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#rgbToHex">rgbToHex</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#selectAllCoordinateInputs">selectAllCoordinateInputs</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#serverTPtoDisplayTP">serverTPtoDisplayTP</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setApiManager">setApiManager</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setSettingsManager">setSettingsManager</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setTemplatesShouldBeDrawn">setTemplatesShouldBeDrawn</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#setWindowMain">setWindowMain</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#sleep">sleep</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#spontaneousResponseListener">spontaneousResponseListener</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#toggleFlag">toggleFlag</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#uint8ToBase64">uint8ToBase64</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateColorList">updateColorList</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateInnerHTML">updateInnerHTML</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#updateUserStorage">updateUserStorage</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#viewCanvasInNewTab">viewCanvasInNewTab</a></span></li>
</nav>
<div id="main">
<h1 class="page-title">apiManager.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/** ApiManager class for handling API requests, responses, and interactions.
* Note: Fetch spying is done in main.js, not here.
* @class ApiManager
* @since 0.11.1
*/
import TemplateManager from "./templateManager.js";
import { consoleError, escapeHTML, localizeNumber, numberToEncoded, serverTPtoDisplayTP } from "./utils.js";
export default class ApiManager {
/** Constructor for ApiManager class
* @param {TemplateManager} templateManager
* @since 0.11.34
*/
constructor(templateManager) {
this.templateManager = templateManager;
this.disableAll = false; // Should the entire userscript be disabled?
this.chargeRefillTimerID = ''; // Contains the Charge refill timer element ID attribute so we can update the timer.
this.coordsTilePixel = []; // Contains the last detected tile/pixel coordinate pair requested
this.templateCoordsTilePixel = []; // Contains the last "enabled" template coords
}
/** Determines if the spontaneously received response is something we want.
* Otherwise, we can ignore it.
* Note: Due to aggressive compression, make your calls like `data['jsonData']['name']` instead of `data.jsonData.name`
*
* @param {Overlay} overlay - The Overlay class instance
* @since 0.11.1
*/
spontaneousResponseListener(overlay) {
// Triggers whenever a message is sent
window.addEventListener('message', async (event) => {
const data = event.data; // The data of the message
const dataJSON = data['jsonData']; // The JSON response, if any
// Kills itself if the message was not intended for Blue Marble
if (!(data &amp;&amp; data['source'] === 'blue-marble')) {return;}
// Kills itself if the message has no endpoint (intended for Blue Marble, but not this function)
if (!data['endpoint']) {return;}
// Trims endpoint to the second to last non-number, non-null directoy.
// E.g. "wplace.live/api/pixel/0/0?payload" -> "pixel"
// E.g. "wplace.live/api/files/s0/tiles/0/0/0.png" -> "tiles"
const endpointText = data['endpoint']?.split('?')[0].split('/').filter(s => s &amp;&amp; isNaN(Number(s))).filter(s => s &amp;&amp; !s.includes('.')).pop();
console.log(`%cBlue Marble%c: Recieved message about "%s"`, 'color: cornflowerblue;', '', endpointText);
// Each case is something that Blue Marble can use from the fetch.
// For instance, if the fetch was for "me", we can update the overlay stats
switch (endpointText) {
case 'me': // Request to retrieve user data
// If the game can not retrieve the userdata...
if (dataJSON['status'] &amp;&amp; dataJSON['status']?.toString()[0] != '2') {
// The server is probably down (NOT a 2xx status)
overlay.handleDisplayError(`You are not logged in or Wplace is offline!\nCould not fetch userdata.`);
return; // Kills itself before attempting to display null userdata
}
const nextLevelPixels = Math.ceil(Math.pow(Math.floor(dataJSON['level']) * Math.pow(30, 0.65), (1/0.65)) - dataJSON['pixelsPainted']); // Calculates pixels to the next level
console.log(dataJSON['id']);
if (!!dataJSON['id'] || dataJSON['id'] === 0) {
console.log(numberToEncoded(
dataJSON['id'],
'!#$%&amp;\'()*+,-./0123456789:;&lt;=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'
));
}
this.templateManager.userID = dataJSON['id'];
// Obtains the refill timer for charges
if (this.chargeRefillTimerID.length != 0) {
const chargeRefillTimer = document.querySelector('#' + this.chargeRefillTimerID);
// If the refill timer exists...
if (chargeRefillTimer) {
/** Obtains the information about the user's charges @type {{cooldownMs: number, count: number, max: number}} */
const chargeData = dataJSON['charges'];
// Date that the user's charges will be refilled
chargeRefillTimer.dataset['endDate'] = Date.now() + ((chargeData['max'] - chargeData['count']) * chargeData['cooldownMs']);
}
}
// Updates displayed droplet information
overlay.updateInnerHTML('bm-user-droplets', `Droplets: &lt;b>${localizeNumber(dataJSON['droplets'])}&lt;/b>`); // Updates the text content of the droplets field
overlay.updateInnerHTML('bm-user-nextlevel', `Next level in &lt;b>${localizeNumber(nextLevelPixels)}&lt;/b> pixel${nextLevelPixels == 1 ? '' : 's'}`); // Updates the text content of the next level field
break;
case 'pixel': // Request to retrieve pixel data
const coordsTile = data['endpoint'].split('?')[0].split('/').filter(s => s &amp;&amp; !isNaN(Number(s))); // Retrieves the tile coords as [x, y]
const payloadExtractor = new URLSearchParams(data['endpoint'].split('?')[1]); // Declares a new payload deconstructor and passes in the fetch request payload
const coordsPixel = [payloadExtractor.get('x'), payloadExtractor.get('y')]; // Retrieves the deconstructed pixel coords from the payload
// Don't save the coords if there are previous coords that could be used
if (this.coordsTilePixel.length &amp;&amp; (!coordsTile.length || !coordsPixel.length)) {
overlay.handleDisplayError(`Coordinates are malformed!\nDid you try clicking the canvas first?`);
return; // Kills itself
}
this.coordsTilePixel = [...coordsTile, ...coordsPixel]; // Combines the two arrays such that [x, y, x, y]
const displayTP = serverTPtoDisplayTP(coordsTile, coordsPixel); // Retrieves the coordinates that Wplace displays for this region
const spanElements = document.querySelectorAll('span'); // Retrieves all span elements
// For every span element, find the one we want (pixel numbers when canvas clicked)
for (const element of spanElements) {
// We use the pixel numbers to find this element because it is the only identifiable piece of information, assuming the website can load in non-Engligh languages.
const elementTextTrimmed = element.textContent.trim(); // Stores the text of the span element, without leading or trailing spaces
// If the text content of the element includes both coordinates seperatly (avoids failure when the comma seperator changes due to localization)
if (elementTextTrimmed.includes(displayTP[0]) &amp;&amp; elementTextTrimmed.includes(displayTP[1])) {
let displayCoords = document.querySelector('#bm-display-coords'); // Find the additional pixel coords span
const text = `(Tl X: ${coordsTile[0]}, Tl Y: ${coordsTile[1]}, Px X: ${coordsPixel[0]}, Px Y: ${coordsPixel[1]})`;
// All 4 coordinate labels, IDs, and values
const coordsLabel = ['Tl X:', 'Tl Y:', 'Px X:', 'Px Y:'];
const coordsID = ['bm-tile-x', 'bm-tile-y', 'bm-pixel-x', 'bm-pixel-y'];
const coordsCombined = [...coordsTile, ...coordsPixel];
// If we could not find the addition coord span, we make it then update the textContent with the new coords
if (!displayCoords) {
displayCoords = document.createElement('span');
displayCoords.id = 'bm-display-coords';
displayCoords.style = 'display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;';
// For each of the 4 coordinates...
for (const [coordIndex, coordValue] of coordsCombined.entries()) {
const coordElement = document.createElement('span'); // Creates a `&lt;span>` element
coordElement.id = coordsID[coordsCombined.indexOf(coordValue) ?? '']; // Applys the ID to the coord element
// Outputs something like "Tl X: 483"
coordElement.textContent = `${coordsLabel[coordIndex] ?? '??:'} ${coordValue}`;
// Or if the amount of labels is less than the provided values, it outputs something like "??: 483" instead of failing
displayCoords.appendChild(coordElement); // Adds the span coordinate as a child for the flexbox container
}
// Adds the display coordinate flexbox container to the pixel info menu
element.parentNode.parentNode.parentNode.insertAdjacentElement('afterend', displayCoords);
} else {
// For each of the 4 coordinates...
for (const [coordIndex, coordID] of coordsID.entries()) {
const coordElement = document.getElementById(coordID); // Obtains the coordinate element
// Outputs something like "Tl X: 483"
coordElement.textContent = `${coordsLabel[coordIndex] ?? '??:'} ${coordsCombined[coordIndex]}`;
// Or if the amount of labels is less than the provided values, it outputs something like "??: 483" instead of failing
}
}
}
}
break;
case 'tile':
case 'tiles':
let tileCoordsTile = data['endpoint'].split('/');
tileCoordsTile = [parseInt(tileCoordsTile[tileCoordsTile.length - 2]), parseInt(tileCoordsTile[tileCoordsTile.length - 1].replace('.png', ''))];
const blobUUID = data['blobID'];
const blobData = data['blobData'];
const timer = Date.now();
const templateBlob = await this.templateManager.drawTemplateOnTile(blobData, tileCoordsTile);
console.log(`Finished loading the tile in ${(Date.now() - timer) / 1000} seconds!`);
window.postMessage({
source: 'blue-marble',
blobID: blobUUID,
blobData: templateBlob,
blink: data['blink']
});
break;
case 'robots': // Request to retrieve what script types are allowed
this.disableAll = dataJSON['userscript']?.toString().toLowerCase() == 'false'; // Disables Blue Marble if site owner wants userscripts disabled
break;
}
});
}
// Sends a heartbeat to the telemetry server
async sendHeartbeat(version) {
console.log('Sending heartbeat to telemetry server...');
let userSettings = GM_getValue('bmUserSettings', '{}')
userSettings = JSON.parse(userSettings);
if (!userSettings || !userSettings.telemetry || !userSettings.uuid) {
console.log('Telemetry is disabled, not sending heartbeat.');
return; // If telemetry is disabled, do not send heartbeat
}
const ua = navigator.userAgent;
let browser = await this.getBrowserFromUA(ua);
let os = this.getOS(ua);
GM_xmlhttpRequest({
method: 'POST',
url: 'https://telemetry.thebluecorner.net/heartbeat',
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify({
uuid: userSettings.uuid,
version: version,
browser: browser,
os: os,
}),
onload: (response) => {
if (response.status !== 200) {
consoleError('Failed to send heartbeat:', response.statusText);
}
},
onerror: (error) => {
consoleError('Error sending heartbeat:', error);
}
});
}
async getBrowserFromUA(ua = navigator.userAgent) {
ua = ua || "";
// Opera
if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
// Edge (Chromium-based uses "Edg/")
if (ua.includes("Edg/")) return "Edge";
// Vivaldi
if (ua.includes("Vivaldi")) return "Vivaldi";
// Yandex
if (ua.includes("YaBrowser")) return "Yandex";
// Kiwi (not guaranteed, but typically shows "Kiwi")
if (ua.includes("Kiwi")) return "Kiwi";
// Brave (doesn't expose in UA by default; heuristic via Brave/ token in some versions)
if (ua.includes("Brave")) return "Brave";
// Firefox
if (ua.includes("Firefox/")) return "Firefox";
// Chrome (catch-all for Chromium browsers)
if (ua.includes("Chrome/")) return "Chrome";
// Safari (must be after Chrome check)
if (ua.includes("Safari/")) return "Safari";
// Brave special check
if (navigator.brave &amp;&amp; typeof navigator.brave.isBrave === "function") {
if (await navigator.brave.isBrave()) return "Brave";
}
// Fallback
return 'Unknown';
}
getOS(ua = navigator.userAgent) {
ua = ua || "";
if (/Windows NT 11/i.test(ua)) return "Windows 11";
if (/Windows NT 10/i.test(ua)) return "Windows 10";
if (/Windows NT 6\.3/i.test(ua)) return "Windows 8.1";
if (/Windows NT 6\.2/i.test(ua)) return "Windows 8";
if (/Windows NT 6\.1/i.test(ua)) return "Windows 7";
if (/Windows NT 6\.0/i.test(ua)) return "Windows Vista";
if (/Windows NT 5\.1|Windows XP/i.test(ua)) return "Windows XP";
if (/Mac OS X 10[_\.]15/i.test(ua)) return "macOS Catalina";
if (/Mac OS X 10[_\.]14/i.test(ua)) return "macOS Mojave";
if (/Mac OS X 10[_\.]13/i.test(ua)) return "macOS High Sierra";
if (/Mac OS X 10[_\.]12/i.test(ua)) return "macOS Sierra";
if (/Mac OS X 10[_\.]11/i.test(ua)) return "OS X El Capitan";
if (/Mac OS X 10[_\.]10/i.test(ua)) return "OS X Yosemite";
if (/Mac OS X 10[_\.]/i.test(ua)) return "macOS"; // Generic fallback
if (/Android/i.test(ua)) return "Android";
if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
if (/Linux/i.test(ua)) return "Linux";
return "Unknown";
}
}
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>