Updated Telemetry window

This commit is contained in:
SwingTheVine 2026-02-22 00:07:58 -05:00
parent 741fafa401
commit 4ee76a700a
15 changed files with 280 additions and 160 deletions

View file

@ -95,7 +95,7 @@
font-weight: 700;
vertical-align: middle;
}
.bm-container.bm-center-vertically:has(> :where(h1, h2, h3, h4, h5, h6)) {
.bm-container.bm-center-vertically {
width: fit-content;
margin-left: auto;
margin-right: auto;
@ -216,6 +216,14 @@ input[type=file] {
font-size: x-small;
color: lightgray;
}
.bm-window ul li {
list-style: disc;
margin-left: 5ch;
}
.bm-window .bm-container.bm-scrollable {
max-height: calc(80vh - 150px);
overflow: auto;
}
.bm-flex-between {
display: flex;
align-content: center;
@ -235,10 +243,6 @@ input[type=file] {
height: 1em;
fill: white;
}
#bm-window-filter .bm-container.bm-scrollable {
max-height: calc(80vh - 150px);
overflow: auto;
}
.bm-filter-flex {
display: flex;
flex-direction: row;

View file

@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.88.338
// @version 0.88.355
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @description:en A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @author SwingTheVine
@ -1301,6 +1301,11 @@
};
// src/utils.js
function escapeHTML(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
function serverTPtoDisplayTP(tile, pixel) {
return [parseInt(tile[0]) % 4 * 1e3 + parseInt(pixel[0]), parseInt(tile[1]) % 4 * 1e3 + parseInt(pixel[1])];
}
@ -1961,14 +1966,12 @@ There are ${pixelsCorrectTotal} correct pixels.`);
};
// src/apiManager.js
var _ApiManager_instances, getBrowserFromUA_fn, getOS_fn;
var ApiManager = class {
/** Constructor for ApiManager class
* @param {TemplateManager} templateManager
* @since 0.11.34
*/
constructor(templateManager2) {
__privateAdd(this, _ApiManager_instances);
this.templateManager = templateManager2;
this.disableAll = false;
this.chargeRefillTimerID = "";
@ -2079,8 +2082,8 @@ Did you try clicking the canvas first?`);
return;
}
const ua = navigator.userAgent;
let browser = await __privateMethod(this, _ApiManager_instances, getBrowserFromUA_fn).call(this, ua);
let os = __privateMethod(this, _ApiManager_instances, getOS_fn).call(this, ua);
let browser = await this.getBrowserFromUA(ua);
let os = this.getOS(ua);
GM_xmlhttpRequest({
method: "POST",
url: "https://telemetry.thebluecorner.net/heartbeat",
@ -2103,44 +2106,43 @@ Did you try clicking the canvas first?`);
}
});
}
};
_ApiManager_instances = new WeakSet();
getBrowserFromUA_fn = async function(ua = navigator.userAgent) {
ua = ua || "";
if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
if (ua.includes("Edg/")) return "Edge";
if (ua.includes("Vivaldi")) return "Vivaldi";
if (ua.includes("YaBrowser")) return "Yandex";
if (ua.includes("Kiwi")) return "Kiwi";
if (ua.includes("Brave")) return "Brave";
if (ua.includes("Firefox/")) return "Firefox";
if (ua.includes("Chrome/")) return "Chrome";
if (ua.includes("Safari/")) return "Safari";
if (navigator.brave && typeof navigator.brave.isBrave === "function") {
if (await navigator.brave.isBrave()) return "Brave";
async getBrowserFromUA(ua = navigator.userAgent) {
ua = ua || "";
if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
if (ua.includes("Edg/")) return "Edge";
if (ua.includes("Vivaldi")) return "Vivaldi";
if (ua.includes("YaBrowser")) return "Yandex";
if (ua.includes("Kiwi")) return "Kiwi";
if (ua.includes("Brave")) return "Brave";
if (ua.includes("Firefox/")) return "Firefox";
if (ua.includes("Chrome/")) return "Chrome";
if (ua.includes("Safari/")) return "Safari";
if (navigator.brave && typeof navigator.brave.isBrave === "function") {
if (await navigator.brave.isBrave()) return "Brave";
}
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";
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";
}
return "Unknown";
};
getOS_fn = function(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";
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";
};
// src/WindowFilter.js
@ -2199,7 +2201,7 @@ Did you try clicking the canvas first?`);
button.ontouchend = () => {
button.click();
};
}).buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": "Color Filter" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-flex-between", "style": "gap: 1.5ch; width: fit-content; margin-left: auto; margin-right: auto;" }).addButton({ "textContent": "Select All" }, (instance, button) => {
}).buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": "Color Filter" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-flex-between bm-center-vertically", "style": "gap: 1.5ch;" }).addButton({ "textContent": "Select All" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, false);
}).buildElement().addButton({ "textContent": "Unselect All" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, true);
@ -2496,6 +2498,8 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
};
_WindowMain_instances = new WeakSet();
/** Displays a new color filter window.
* This is a helper function that creates a new class instance.
* This might cause a memory leak. I pray that this is not the case...
* @since 0.88.330
*/
buildWindowFilter_fn = function() {
@ -2503,6 +2507,70 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
windowFilter.buildWindow();
};
// src/WindowTelemetry.js
var _WindowTelemetry_instances, setTelemetryValue_fn;
var WindowTelemetry = class extends Overlay {
/** Constructor for the telemetry window
* @param {string} name - The name of the userscript
* @param {string} version - The version of the userscript
* @param {number} currentTelemetryVersion - The current "version" of the data collection agreement
* @param {string} uuid - The UUID of the user
* @since 0.88.339
* @see {@link Overlay#constructor}
*/
constructor(name2, version2, currentTelemetryVersion2, uuid) {
super(name2, version2);
__privateAdd(this, _WindowTelemetry_instances);
this.window = null;
this.windowID = "bm-window-telemetry";
this.windowParent = document.body;
this.currentTelemetryVersion = currentTelemetryVersion2;
this.uuid = uuid;
}
/** Spawns a telemetry window.
* If another telemetry window already exists, we DON'T spawn another!
* Parent/child relationships in the DOM structure below are indicated by indentation.
* @since 0.88.339
*/
async buildWindow() {
if (document.querySelector(`#${this.windowID}`)) {
this.handleDisplayError("Telemetry window already exists!");
return;
}
const browser = await this.apiManager.getBrowserFromUA(navigator.userAgent);
const os = this.apiManager.getOS(navigator.userAgent);
this.window = this.addDiv({ "id": this.windowID, "class": "bm-window", "style": "height: 80vh; z-index: 9998;" }).addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": `${this.name} Telemetry` }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-flex-center", "style": "gap: 1.5ch; flex-wrap: wrap;" }).addButton({ "textContent": "Enable Telemetry" }, (instance, button) => {
button.onclick = () => {
__privateMethod(this, _WindowTelemetry_instances, setTelemetryValue_fn).call(this, this.currentTelemetryVersion);
const element = document.getElementById(this.windowID);
element?.remove();
};
}).buildElement().addButton({ "textContent": "Disable Telemetry" }, (instance, button) => {
button.onclick = () => {
__privateMethod(this, _WindowTelemetry_instances, setTelemetryValue_fn).call(this, 0);
const element = document.getElementById(this.windowID);
element?.remove();
};
}).buildElement().addButton({ "textContent": "More Information" }, (instance, button) => {
button.onclick = () => {
window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data", "_blank", "noopener noreferrer");
};
}).buildElement().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).addDiv({ "class": "bm-container" }).addHeader(2, { "textContent": "Legal" }).buildElement().addP({ "textContent": `We collect anonymous telemetry data such as your browser, OS, and script version to make the experience better for everyone. The data is never shared personally. The data is never sold. You can turn this off by pressing the "Disable" button, but keeping it on helps us improve features and reliability faster. Thank you for supporting ${this.name}!` }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container" }).addHeader(2, { "textContent": "Non-Legal Summary" }).buildElement().addP({ "innerHTML": `You can disable telemetry by pressing the "Disable" button. If you would like to read more about what information we collect, press the "More Information" button.<br>This is the data <em>stored</em> on our servers:` }).buildElement().addUl().addLi({ "innerHTML": `A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID.<br>Your UUID is: <b>${escapeHTML(this.uuid)}</b>` }).buildElement().addLi({ "innerHTML": `The version of Blue Marble you are using.<br>Your version is: <b>${escapeHTML(this.version)}</b>` }).buildElement().addLi({ "innerHTML": `Your browser type, which is used to determine Blue Marble outages and browser popularity.<br>Your browser type is: <b>${escapeHTML(browser)}</b>` }).buildElement().addLi({ "innerHTML": `Your OS type, which is used to determine Blue Marble outages and OS popularity.<br>Your OS type is: <b>${escapeHTML(os)}</b>` }).buildElement().addLi({ "innerHTML": `The date and time that Blue Marble sent the telemetry information.` }).buildElement().buildElement().addP({ "innerHTML": `All of the data mentioned above is <b>aggregated every hour</b>. This means every hour, anything that could even remotly be considered "personal data" is deleted from our server. Here, "aggregated" data means things like "42 people used Blue Marble on Google Chrome this hour", which can't be used to identify anyone in particular.` }).buildElement().buildElement().buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
}
};
_WindowTelemetry_instances = new WeakSet();
/** Enables or disables telemetry based on the value passed in.
* A value of zero will always disable telemetry.
* A numeric, non-zero value will enable telemetry until the telemetry agreement is changed.
* @param {number} value - The value to set the telemetry to
* @since 0.88.339
*/
setTelemetryValue_fn = function(value2) {
const userSettings2 = JSON.parse(GM_getValue("bmUserSettings", "{}"));
userSettings2.telemetry = value2;
GM.setValue("bmUserSettings", JSON.stringify(userSettings2));
};
// src/main.js
var name = GM_info.script.name.toString();
var version = GM_info.script.version.toString();
@ -2627,11 +2695,13 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
}));
}
setInterval(() => apiManager.sendHeartbeat(version), 1e3 * 60 * 30);
console.log(`Telemetry is ${!(userSettings?.telemetry == void 0)}`);
if (userSettings?.telemetry == void 0 || userSettings?.telemetry > 1) {
const telemetryOverlay = new Overlay(name, version);
telemetryOverlay.setApiManager(apiManager);
buildTelemetryOverlay(telemetryOverlay);
var currentTelemetryVersion = 1;
var previousTelemetryVersion = userSettings?.telemetry;
console.log(`Telemetry is ${!(previousTelemetryVersion == void 0)}`);
if (previousTelemetryVersion == void 0 || previousTelemetryVersion > currentTelemetryVersion) {
const windowTelemetry = new WindowTelemetry(name, version, currentTelemetryVersion, userSettings?.uuid);
windowTelemetry.setApiManager(apiManager);
windowTelemetry.buildWindow();
}
windowMain.buildWindow();
apiManager.spontaneousResponseListener(windowMain);
@ -2665,31 +2735,4 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
});
observer.observe(document.body, { childList: true, subtree: true });
}
function buildTelemetryOverlay(overlay) {
overlay.addDiv({ "id": "bm-overlay-telemetry", style: "top: 0px; left: 0px; width: 100vw; max-width: 100vw; height: 100vh; max-height: 100vh; z-index: 9999;" }).addDiv({ "id": "bm-contain-all-telemetry", style: "display: flex; flex-direction: column; align-items: center;" }).addDiv({ "id": "bm-contain-header-telemetry", style: "margin-top: 10%;" }).addHeader(1, { "textContent": `${name} Telemetry` }).buildElement().buildElement().addDiv({ "id": "bm-contain-telemetry", style: "max-width: 50%; overflow-y: auto; max-height: 80vh;" }).addHr().buildElement().addBr().buildElement().addDiv({ "style": "width: fit-content; margin: auto; text-align: center;" }).addButton({ "id": "bm-button-telemetry-more", "textContent": "More Information" }, (instance, button) => {
button.onclick = () => {
window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data", "_blank", "noopener noreferrer");
};
}).buildElement().buildElement().addBr().buildElement().addDiv({ style: "width: fit-content; margin: auto; text-align: center;" }).addButton({ "id": "bm-button-telemetry-enable", "textContent": "Enable Telemetry", "style": "margin-right: 2ch;" }, (instance, button) => {
button.onclick = () => {
const userSettings2 = JSON.parse(GM_getValue("bmUserSettings", "{}"));
userSettings2.telemetry = 1;
GM.setValue("bmUserSettings", JSON.stringify(userSettings2));
const element = document.getElementById("bm-overlay-telemetry");
if (element) {
element.style.display = "none";
}
};
}).buildElement().addButton({ "id": "bm-button-telemetry-disable", "textContent": "Disable Telemetry" }, (instance, button) => {
button.onclick = () => {
const userSettings2 = JSON.parse(GM_getValue("bmUserSettings", "{}"));
userSettings2.telemetry = 0;
GM.setValue("bmUserSettings", JSON.stringify(userSettings2));
const element = document.getElementById("bm-overlay-telemetry");
if (element) {
element.style.display = "none";
}
};
}).buildElement().buildElement().addBr().buildElement().addP({ "textContent": "We collect anonymous telemetry data such as your browser, OS, and script version to make the experience better for everyone. The data is never shared personally. The data is never sold. You can turn this off by pressing the 'Disable' button, but keeping it on helps us improve features and reliability faster. Thank you for supporting the Blue Marble!" }).buildElement().addP({ "textContent": 'You can disable telemetry by pressing the "Disable" button below.' }).buildElement().buildElement().buildElement().buildOverlay(document.body);
}
})();

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
.bm-screenreader{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.bm-1n{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;top:75px;left:60px;width:auto;max-height:calc(100vh - 150px);max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-19{max-width:300px}.bm-1l{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue"/></svg>') repeat;cursor:grab;width:100%;height:fit-content}.bm-1l.bm-1g{cursor:grabbing}.bm-1n:has(.bm-1l.bm-1g){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-1l.bm-1g{pointer-events:auto}.bm-1m{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-1n h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-1l h1{font-size:1.2em;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-shadow:3px 0px rgba(21,48,99,.5),-3px 0px rgba(21,48,99,.5),0px 3px rgba(21,48,99,.5),0px -3px rgba(21,48,99,.5),3px 3px rgba(21,48,99,.5),-3px 3px rgba(21,48,99,.5),3px -3px rgba(21,48,99,.5),-3px -3px rgba(21,48,99,.5)}.bm-1l div:has(h1){display:contents}.bm-1n h2{display:inline-block;font-size:larger;font-weight:700;vertical-align:middle}.bm-1f.bm-V:has(>:where(h1,h2,h3,h4,h5,h6)){width:fit-content;margin-left:auto;margin-right:auto}.bm-1f{margin:.5em 0}.bm-1n button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-1n button:hover,.bm-1n button:focus-visible{background-color:#1061e5}.bm-1n button:active,.bm-1n button:disabled{background-color:#2e97ff}.bm-1n button:disabled{text-decoration:line-through}.bm-12{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-1d{vertical-align:middle}.bm-1d svg{width:50%;margin:0 auto;fill:#111}.bm-1n button.bm-16{background-color:unset}.bm-16.bm-T:hover,.bm-16.bm-T:focus{background-color:#ffffff2b}.bm-16.bm-T:active{background-color:#ffffff38}.bm-16.bm-U:hover,.bm-16.bm-U:focus{background-color:#0000002b}.bm-16.bm-U:active{background-color:#00000038}input[type=number].bm-17{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-17::-webkit-outer-spin-button,input[type=number].bm-17::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-1e)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-1e,input[type=file]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}.bm-1n select{color:#fff;background-color:#144eb9;border-radius:1em;padding:0 .5ch}.bm-1n label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch}.bm-1n input[type=checkbox]{width:1em}.bm-Z{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-1n textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-1n small{font-size:x-small;color:#d3d3d3}.bm-14{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-flex-center{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}#bm-11 p svg{display:inline;height:1em;fill:#fff}#bm-11 .bm-1f.bm-1b{max-height:calc(80vh - 150px);overflow:auto}.bm-18{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;gap:1em 3ch}.bm-15{width:fit-content;max-width:35ch;background-color:#153063e6;border-radius:1em;padding:.5em;gap:1ch;transition:background-color .3s ease}.bm-15:hover,.bm-15:focus-within{background-color:#112855e6}.bm-P{display:block;border:thick double darkslategray;width:fit-content;height:fit-content;padding:1ch}.bm-15[data-id="-2"] .bm-P{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}.bm-15[data-id="-1"] .bm-P{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 8 8" width="1em" height="1em"><path d="M0,0V8H16V16H8V0" fill="rgba(0,0,0,0.5)"/></svg>') repeat;background-color:transparent!important}.bm-15[data-id="-1"] .bm-P svg{fill:#fff!important}.bm-15[data-id="0"] .bm-P{background-color:transparent!important}.bm-P button{padding:.75em .5ch}.bm-P svg{width:4ch}.bm-15>.bm-14{flex-direction:column;align-items:flex-start;gap:0}.bm-15 small{font-size:.75em}#bm-11 .bm-15.bm-1c{display:none}
.bm-screenreader{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.bm-1i{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;top:75px;left:60px;width:auto;max-height:calc(100vh - 150px);max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-13{max-width:300px}.bm-1g{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue"/></svg>') repeat;cursor:grab;width:100%;height:fit-content}.bm-1g.bm-1b{cursor:grabbing}.bm-1i:has(.bm-1g.bm-1b){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-1g.bm-1b{pointer-events:auto}.bm-1h{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-1i h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-1g h1{font-size:1.2em;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-shadow:3px 0px rgba(21,48,99,.5),-3px 0px rgba(21,48,99,.5),0px 3px rgba(21,48,99,.5),0px -3px rgba(21,48,99,.5),3px 3px rgba(21,48,99,.5),-3px 3px rgba(21,48,99,.5),3px -3px rgba(21,48,99,.5),-3px -3px rgba(21,48,99,.5)}.bm-1g div:has(h1){display:contents}.bm-1i h2{display:inline-block;font-size:larger;font-weight:700;vertical-align:middle}.bm-1a.bm-Q{width:fit-content;margin-left:auto;margin-right:auto}.bm-1a{margin:.5em 0}.bm-1i button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-1i button:hover,.bm-1i button:focus-visible{background-color:#1061e5}.bm-1i button:active,.bm-1i button:disabled{background-color:#2e97ff}.bm-1i button:disabled{text-decoration:line-through}.bm-Y{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-18{vertical-align:middle}.bm-18 svg{width:50%;margin:0 auto;fill:#111}.bm-1i button.bm-10{background-color:unset}.bm-10.bm-O:hover,.bm-10.bm-O:focus{background-color:#ffffff2b}.bm-10.bm-O:active{background-color:#ffffff38}.bm-10.bm-P:hover,.bm-10.bm-P:focus{background-color:#0000002b}.bm-10.bm-P:active{background-color:#00000038}input[type=number].bm-11{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-11::-webkit-outer-spin-button,input[type=number].bm-11::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-19)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-19,input[type=file]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}.bm-1i select{color:#fff;background-color:#144eb9;border-radius:1em;padding:0 .5ch}.bm-1i label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch}.bm-1i input[type=checkbox]{width:1em}.bm-T{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-1i textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-1i small{font-size:x-small;color:#d3d3d3}.bm-1i ul li{list-style:disc;margin-left:5ch}.bm-1i .bm-1a.bm-16{max-height:calc(80vh - 150px);overflow:auto}.bm--{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-14{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}#bm-X p svg{display:inline;height:1em;fill:#fff}.bm-12{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;gap:1em 3ch}.bm-_{width:fit-content;max-width:35ch;background-color:#153063e6;border-radius:1em;padding:.5em;gap:1ch;transition:background-color .3s ease}.bm-_:hover,.bm-_:focus-within{background-color:#112855e6}.bm-K{display:block;border:thick double darkslategray;width:fit-content;height:fit-content;padding:1ch}.bm-_[data-id="-2"] .bm-K{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}.bm-_[data-id="-1"] .bm-K{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 8 8" width="1em" height="1em"><path d="M0,0V8H16V16H8V0" fill="rgba(0,0,0,0.5)"/></svg>') repeat;background-color:transparent!important}.bm-_[data-id="-1"] .bm-K svg{fill:#fff!important}.bm-_[data-id="0"] .bm-K{background-color:transparent!important}.bm-K button{padding:.75em .5ch}.bm-K svg{width:4ch}.bm-_>.bm--{flex-direction:column;align-items:flex-start;gap:0}.bm-_ small{font-size:.75em}#bm-X .bm-_.bm-17{display:none}

File diff suppressed because one or more lines are too long

View file

@ -51,7 +51,7 @@
<a href="https://discord.gg/tpeBPy46hf" target="_blank" rel="noopener noreferrer"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
<a href="https://bluemarble.lol/" target="_blank" rel="noopener noreferrer"><img alt="Blue Marble Website" src="https://img.shields.io/badge/Blue_Marble_Website-crqch-blue?style=flat&logo=globe&logoColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-169hrs_20mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-836-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-853-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Lines of Code" src="https://img.shields.io/badge/Lines_Of_Code-498-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Comments" src="https://img.shields.io/badge/Lines_Of_Comments-498-blue?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Compression" src="https://img.shields.io/badge/Compression-70.19%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.88.338",
"version": "0.88.355",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.88.338",
"version": "0.88.355",
"devDependencies": {
"esbuild": "^0.25.0",
"jsdoc": "^4.0.5",

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.88.338",
"version": "0.88.355",
"type": "module",
"homepage": "https://bluemarble.lol/",
"repository": {

View file

@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.88.338
// @version 0.88.355
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @description:en A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @author SwingTheVine

View file

@ -2,7 +2,7 @@ import Overlay from "./Overlay";
import { calculateRelativeLuminance } from "./utils";
/** The overlay builder for the color filter Blue Marble window.
* @description This class handles the overlay UI for the coolor filter window of the Blue Marble userscript.
* @description This class handles the overlay UI for the color filter window of the Blue Marble userscript.
* @class WindowFilter
* @since 0.88.329
* @see {@link Overlay} for examples
@ -76,7 +76,7 @@ export default class WindowFilter extends Overlay {
.addHeader(1, {'textContent': 'Color Filter'}).buildElement()
.buildElement()
.addHr().buildElement()
.addDiv({'class': 'bm-container bm-flex-between', 'style': 'gap: 1.5ch; width: fit-content; margin-left: auto; margin-right: auto;'})
.addDiv({'class': 'bm-container bm-flex-between bm-center-vertically', 'style': 'gap: 1.5ch;'})
.addButton({'textContent': 'Select All'}, (instance, button) => {
button.onclick = () => this.#selectColorList(false);
}).buildElement()

View file

@ -170,6 +170,8 @@ export default class WindowMain extends Overlay {
}
/** Displays a new color filter window.
* This is a helper function that creates a new class instance.
* This might cause a memory leak. I pray that this is not the case...
* @since 0.88.330
*/
#buildWindowFilter() {

108
src/WindowTelemetry.js Normal file
View file

@ -0,0 +1,108 @@
import Overlay from "./Overlay";
import { escapeHTML } from "./utils";
/** The overlay builder for the telemetry window for Blue Marble.
* @description This class handles the overlay UI for the telemetry window.
* @class WindowTelemetry
* @since 0.88.339
* @see {@link Overlay} for examples
*/
export default class WindowTelemetry extends Overlay {
/** Constructor for the telemetry window
* @param {string} name - The name of the userscript
* @param {string} version - The version of the userscript
* @param {number} currentTelemetryVersion - The current "version" of the data collection agreement
* @param {string} uuid - The UUID of the user
* @since 0.88.339
* @see {@link Overlay#constructor}
*/
constructor(name, version, currentTelemetryVersion, uuid) {
super(name, version); // Executes the code in the Overlay constructor
this.window = null; // Contains the *window* DOM tree
this.windowID = 'bm-window-telemetry'; // The ID attribute for this window
this.windowParent = document.body; // The parent of the window DOM tree
this.currentTelemetryVersion = currentTelemetryVersion; // The current telemetry "version". Increment this whenever the data collection agreement is changed!
this.uuid = uuid; // The UUID of the user
}
/** Spawns a telemetry window.
* If another telemetry window already exists, we DON'T spawn another!
* Parent/child relationships in the DOM structure below are indicated by indentation.
* @since 0.88.339
*/
async buildWindow() {
// If the telemetry window already exists, throw an error and return early
if (document.querySelector(`#${this.windowID}`)) {
this.handleDisplayError('Telemetry window already exists!');
return;
}
const browser = await this.apiManager.getBrowserFromUA(navigator.userAgent);
const os = this.apiManager.getOS(navigator.userAgent);
this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window', 'style': 'height: 80vh; z-index: 9998;'})
.addDiv({'class': 'bm-window-content'})
.addDiv({'class': 'bm-container bm-center-vertically'})
.addHeader(1, {'textContent': `${this.name} Telemetry`}).buildElement()
.buildElement()
.addHr().buildElement()
.addDiv({'class': 'bm-container bm-flex-center', 'style': 'gap: 1.5ch; flex-wrap: wrap;'})
.addButton({'textContent': 'Enable Telemetry'}, (instance, button) => {
button.onclick = () => {
this.#setTelemetryValue(this.currentTelemetryVersion);
const element = document.getElementById(this.windowID);
element?.remove();
};
}).buildElement()
.addButton({'textContent': 'Disable Telemetry'}, (instance, button) => {
button.onclick = () => {
this.#setTelemetryValue(0);
const element = document.getElementById(this.windowID);
element?.remove();
};
}).buildElement()
.addButton({'textContent': 'More Information'}, (instance, button) => {
button.onclick = () => {
window.open('https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data', '_blank', 'noopener noreferrer');
}
}).buildElement()
.buildElement()
.addDiv({'class': 'bm-container bm-scrollable'})
.addDiv({'class': 'bm-container'})
.addHeader(2, {'textContent': 'Legal'}).buildElement()
.addP({'textContent': `We collect anonymous telemetry data such as your browser, OS, and script version to make the experience better for everyone. The data is never shared personally. The data is never sold. You can turn this off by pressing the "Disable" button, but keeping it on helps us improve features and reliability faster. Thank you for supporting ${this.name}!`}).buildElement()
.buildElement()
.addHr().buildElement()
.addDiv({'class': 'bm-container'})
.addHeader(2, {'textContent': 'Non-Legal Summary'}).buildElement()
.addP({'innerHTML': `You can disable telemetry by pressing the "Disable" button. If you would like to read more about what information we collect, press the "More Information" button.<br>This is the data <em>stored</em> on our servers:`}).buildElement()
.addUl()
.addLi({'innerHTML': `A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID.<br>Your UUID is: <b>${escapeHTML(this.uuid)}</b>`}).buildElement()
.addLi({'innerHTML': `The version of Blue Marble you are using.<br>Your version is: <b>${escapeHTML(this.version)}</b>`}).buildElement()
.addLi({'innerHTML': `Your browser type, which is used to determine Blue Marble outages and browser popularity.<br>Your browser type is: <b>${escapeHTML(browser)}</b>`}).buildElement()
.addLi({'innerHTML': `Your OS type, which is used to determine Blue Marble outages and OS popularity.<br>Your OS type is: <b>${escapeHTML(os)}</b>`}).buildElement()
.addLi({'innerHTML': `The date and time that Blue Marble sent the telemetry information.`}).buildElement()
.buildElement()
.addP({'innerHTML': `All of the data mentioned above is <b>aggregated every hour</b>. This means every hour, anything that could even remotly be considered "personal data" is deleted from our server. Here, "aggregated" data means things like "42 people used Blue Marble on Google Chrome this hour", which can't be used to identify anyone in particular.`}).buildElement()
.buildElement()
.buildElement()
.buildElement()
.buildElement().buildOverlay(this.windowParent);
}
/** Enables or disables telemetry based on the value passed in.
* A value of zero will always disable telemetry.
* A numeric, non-zero value will enable telemetry until the telemetry agreement is changed.
* @param {number} value - The value to set the telemetry to
* @since 0.88.339
*/
#setTelemetryValue(value) {
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}'));
userSettings.telemetry = value;
GM.setValue('bmUserSettings', JSON.stringify(userSettings));
}
}

View file

@ -173,8 +173,8 @@ export default class ApiManager {
}
const ua = navigator.userAgent;
let browser = await this.#getBrowserFromUA(ua);
let os = this.#getOS(ua);
let browser = await this.getBrowserFromUA(ua);
let os = this.getOS(ua);
GM_xmlhttpRequest({
method: 'POST',
@ -199,7 +199,7 @@ export default class ApiManager {
});
}
async #getBrowserFromUA(ua = navigator.userAgent) {
async getBrowserFromUA(ua = navigator.userAgent) {
ua = ua || "";
// Opera
@ -238,7 +238,7 @@ export default class ApiManager {
return 'Unknown';
}
#getOS(ua = navigator.userAgent) {
getOS(ua = navigator.userAgent) {
ua = ua || "";
if (/Windows NT 11/i.test(ua)) return "Windows 11";

View file

@ -8,6 +8,7 @@ import ApiManager from './apiManager.js';
import TemplateManager from './templateManager.js';
import { calculateRelativeLuminance, consoleLog, consoleWarn } from './utils.js';
import WindowMain from './WindowMain.js';
import WindowTelemetry from './WindowTelemetry.js';
const name = GM_info.script.name.toString(); // Name of userscript
const version = GM_info.script.version.toString(); // Version of userscript
@ -214,13 +215,20 @@ if (Object.keys(userSettings).length == 0) {
setInterval(() => apiManager.sendHeartbeat(version), 1000 * 60 * 30); // Sends a heartbeat every 30 minutes
console.log(`Telemetry is ${!(userSettings?.telemetry == undefined)}`);
// The current "version" of the data collection agreement
// Increment by 1 to retrigger the telemetry window
const currentTelemetryVersion = 1;
// The last "version" of the data collection agreement that the user agreed too
const previousTelemetryVersion = userSettings?.telemetry;
console.log(`Telemetry is ${!(previousTelemetryVersion == undefined)}`);
// If the user has not agreed to the current data collection terms, we need to show the Telemetry window.
if ((userSettings?.telemetry == undefined) || (userSettings?.telemetry > 1)) { // Increment 1 to retrigger telemetry notice
const telemetryOverlay = new Overlay(name, version);
telemetryOverlay.setApiManager(apiManager); // Sets the API manager for the telemetry overlay
buildTelemetryOverlay(telemetryOverlay); // Notifies the user about telemetry
if ((previousTelemetryVersion == undefined) || (previousTelemetryVersion > currentTelemetryVersion)) {
const windowTelemetry = new WindowTelemetry(name, version, currentTelemetryVersion, userSettings?.uuid);
windowTelemetry.setApiManager(apiManager);
windowTelemetry.buildWindow(); // Asks the user if they want to enable telemetry
}
windowMain.buildWindow(); // Builds the main Blue Marble window
@ -269,54 +277,3 @@ function observeBlack() {
observer.observe(document.body, { childList: true, subtree: true });
}
function buildTelemetryOverlay(overlay) {
overlay.addDiv({'id': 'bm-overlay-telemetry', style: 'top: 0px; left: 0px; width: 100vw; max-width: 100vw; height: 100vh; max-height: 100vh; z-index: 9999;'})
.addDiv({'id': 'bm-contain-all-telemetry', style: 'display: flex; flex-direction: column; align-items: center;'})
.addDiv({'id': 'bm-contain-header-telemetry', style: 'margin-top: 10%;'})
.addHeader(1, {'textContent': `${name} Telemetry`}).buildElement()
.buildElement()
.addDiv({'id': 'bm-contain-telemetry', style: 'max-width: 50%; overflow-y: auto; max-height: 80vh;'})
.addHr().buildElement()
.addBr().buildElement()
.addDiv({'style': 'width: fit-content; margin: auto; text-align: center;'})
.addButton({'id': 'bm-button-telemetry-more', 'textContent': 'More Information'}, (instance, button) => {
button.onclick = () => {
window.open('https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data', '_blank', 'noopener noreferrer');
}
}).buildElement()
.buildElement()
.addBr().buildElement()
.addDiv({style: 'width: fit-content; margin: auto; text-align: center;'})
.addButton({'id': 'bm-button-telemetry-enable', 'textContent': 'Enable Telemetry', 'style': 'margin-right: 2ch;'}, (instance, button) => {
button.onclick = () => {
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}'));
userSettings.telemetry = 1;
GM.setValue('bmUserSettings', JSON.stringify(userSettings));
const element = document.getElementById('bm-overlay-telemetry');
if (element) {
element.style.display = 'none';
}
}
}).buildElement()
.addButton({'id': 'bm-button-telemetry-disable', 'textContent': 'Disable Telemetry'}, (instance, button) => {
button.onclick = () => {
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}'));
userSettings.telemetry = 0;
GM.setValue('bmUserSettings', JSON.stringify(userSettings));
const element = document.getElementById('bm-overlay-telemetry');
if (element) {
element.style.display = 'none';
}
}
}).buildElement()
.buildElement()
.addBr().buildElement()
.addP({'textContent': 'We collect anonymous telemetry data such as your browser, OS, and script version to make the experience better for everyone. The data is never shared personally. The data is never sold. You can turn this off by pressing the \'Disable\' button, but keeping it on helps us improve features and reliability faster. Thank you for supporting the Blue Marble!'}).buildElement()
.addP({'textContent': 'You can disable telemetry by pressing the "Disable" button below.'}).buildElement()
.buildElement()
.buildElement()
.buildOverlay(document.body);
}

View file

@ -25,7 +25,7 @@
top: 75px;
left: 60px;
width: auto;
max-height: calc(100vh - 150px);
max-height: fit-content;
max-width: calc(100% - 135px);
/* Font stack is as follows:
* Highest Priority (Roboto Mono)
@ -123,7 +123,7 @@
}
/* Container with a vertically centered header 1-6 */
.bm-container.bm-center-vertically:has(>:where(h1, h2, h3, h4, h5, h6)) {
.bm-container.bm-center-vertically {
width: fit-content;
margin-left: auto;
margin-right: auto;
@ -289,6 +289,18 @@ input[type="file"] {
color: lightgray;
}
/* List items of unordered lists */
.bm-window ul li {
list-style: disc;
margin-left: 5ch;
}
/* Scrollable container */
.bm-window .bm-container.bm-scrollable {
max-height: calc(80vh - 150px);
overflow: auto;
}
/* Flex children space between */
.bm-flex-between {
display: flex;
@ -314,12 +326,6 @@ input[type="file"] {
fill: white;
}
/* Filter window scrollable container */
#bm-window-filter .bm-container.bm-scrollable {
max-height: calc(80vh - 150px);
overflow: auto;
}
/* Filter flex */
.bm-filter-flex {
display: flex;