diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c477d93
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+# Blue Marble Enhanced
+
+
+
+This fork is based on [SwingTheVine/Wplace-BlueMarble](https://github.com/SwingTheVine/Wplace-BlueMarble) and focuses on practical improvements for everyday template work on [wplace.live](https://wplace.live/).
+
+The goal is not to replace the upstream project. This fork keeps the original Blue Marble workflow, then adds usability, workflow, and Color Filter improvements on top.
+
+## What Is Different
+
+Version `0.94.0` introduces the first enhanced release:
+
+- Redesigned Blue Marble windows with a minimal liquid-glass visual style.
+- Redesigned window controls, buttons, typography, spacing, and transitions.
+- Added a resizable windowed mode for Color Filter.
+- Added Color Filter position and size persistence.
+- Added persistence for shown and hidden colors in Color Filter.
+- Added automatic Color Filter refresh every 10 seconds.
+- Updated Color Filter visibility icons to match the new interface style.
+
+## Installation
+
+Install the latest userscript from the release page:
+
+[Download the latest release](https://github.com/alexeygasenko/Wplace-BlueMarble/releases/latest)
+
+Use `BlueMarble.user.js` with a userscript manager such as Tampermonkey, then refresh [wplace.live](https://wplace.live/).
+
+## Color Filter
+
+Color Filter is one of the main areas improved here. It can be opened as a compact window, resized, moved around the canvas, and restored with the same size and position the next time you use it.
+
+Hidden and visible colors are remembered, so you can isolate the colors you are actively painting without rebuilding the filter state every session. The list also refreshes automatically every 10 seconds, keeping pixel counts current without a manual refresh button.
+
+## Upstream
+
+Original project:
+
+[SwingTheVine/Wplace-BlueMarble](https://github.com/SwingTheVine/Wplace-BlueMarble)
+
+This fork keeps the original license and credits. For upstream documentation, contribution rules, and project background, refer to the original repository.
+
+## License
+
+Blue Marble is licensed under the Mozilla Public License 2.0. See [LICENSE.txt](./LICENSE.txt).
diff --git a/build/build.js b/build/build.js
index 488e83b..d21237a 100644
--- a/build/build.js
+++ b/build/build.js
@@ -10,6 +10,7 @@
// ES Module imports
import esbuild from 'esbuild';
+import crypto from 'crypto';
import fs from 'fs';
import { execSync } from 'child_process';
import { consoleStyle } from './utils.js';
@@ -22,6 +23,24 @@ const terser = require('terser');
const isGitHub = !!process.env?.GITHUB_ACTIONS; // Is this running in a GitHub Action Workflow?'
+/** Appends a build hash comment to an output file.
+ * The hash is based on the file contents before the hash comment is added.
+ * @param {string} path - Path to the file
+ * @param {'js' | 'css'} type - Output type for comment syntax
+ * @returns {string} The short build hash
+ * @since 0.92.0
+ */
+function appendBuildHashComment(path, type = 'js') {
+ const content = fs.readFileSync(path, 'utf8').trimEnd();
+ const hash = crypto.createHash('sha256').update(content, 'utf8').digest('hex').slice(0, 12);
+ const comment = (type == 'css')
+ ? `/* Build Hash: ${hash} */`
+ : `// Build Hash: ${hash}`;
+
+ fs.writeFileSync(path, `${content}\n\n${comment}\n`, 'utf8');
+ return hash;
+}
+
console.log(`${consoleStyle.BLUE}Starting build...${consoleStyle.RESET}`);
// Tries to build the wiki if build.js is run in a GitHub Workflow
@@ -214,4 +233,16 @@ esbuild.build({
fs.writeFileSync(`dist/${greasyForkName}.user.js`, greasyForkBMjs, 'utf-8');
+const buildHashes = {
+ 'BlueMarble.user.css': appendBuildHashComment('dist/BlueMarble.user.css', 'css'),
+ 'BlueMarble.user.js': appendBuildHashComment('dist/BlueMarble.user.js', 'js'),
+ [`${standaloneName}.user.js`]: appendBuildHashComment(`dist/${standaloneName}.user.js`, 'js'),
+ [`${greasyForkName}.user.css`]: appendBuildHashComment(`dist/${greasyForkName}.user.css`, 'css'),
+ [`${greasyForkName}.user.js`]: appendBuildHashComment(`dist/${greasyForkName}.user.js`, 'js')
+};
+
console.log(`${consoleStyle.GREEN + consoleStyle.BOLD + consoleStyle.UNDERLINE}Building complete!${consoleStyle.RESET}`);
+console.log(`Build hashes:`);
+for (const [file, hash] of Object.entries(buildHashes)) {
+ console.log(`- ${file}: ${hash}`);
+}
diff --git a/dist/BlueMarble-For-GreasyFork.user.css b/dist/BlueMarble-For-GreasyFork.user.css
index bb285a5..17d3f26 100644
--- a/dist/BlueMarble-For-GreasyFork.user.css
+++ b/dist/BlueMarble-For-GreasyFork.user.css
@@ -1,35 +1,750 @@
+/* src/confettiManager.css */
+div:has(> confetti-piece) {
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ pointer-events: none;
+}
+confetti-piece {
+ position: absolute;
+ top: -10px;
+ width: var(--size);
+ height: var(--size);
+ background: currentColor;
+ transform: translate3d(var(--x), -10vh, 0) rotate(var(--rot));
+ animation: fall var(--duration) linear var(--delay);
+ will-change: transform;
+ pointer-events: none;
+}
+@keyframes fall {
+ to {
+ transform: translate3d(var(--x), 110vh, 0) rotate(calc(var(--rot) + 720deg));
+ }
+}
+
+/* src/overlay.css */
+.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-window {
+ --bm-surface-strong: rgba(9, 20, 42, 0.5);
+ --bm-surface-soft: rgba(24, 41, 74, 0.28);
+ --bm-surface-glass: rgba(255, 255, 255, 0.1);
+ --bm-surface-glass-strong: rgba(255, 255, 255, 0.18);
+ --bm-border-soft: rgba(255, 255, 255, 0.18);
+ --bm-border-strong: rgba(163, 228, 255, 0.34);
+ --bm-text-primary: rgba(17, 36, 66, 0.96);
+ --bm-text-secondary: rgba(36, 57, 90, 0.84);
+ --bm-accent-start: #baf6ff;
+ --bm-accent-end: #81b6ff;
+ --bm-accent-shadow: rgba(132, 182, 255, 0.22);
+ --bm-font-body:
+ "Rajdhani",
+ "Segoe UI Variable Text",
+ "Segoe UI",
+ sans-serif;
+ --bm-font-display:
+ "Michroma",
+ "Orbitron",
+ "Segoe UI",
+ sans-serif;
+ --bm-font-mono:
+ "Roboto Mono",
+ "Rajdhani",
+ "Courier New",
+ monospace;
+ position: fixed;
+ isolation: isolate;
+ overflow: hidden;
+ background:
+ radial-gradient(
+ circle at 14% 12%,
+ rgba(255, 255, 255, 0.24),
+ transparent 18%),
+ radial-gradient(
+ circle at 86% 8%,
+ rgba(186, 246, 255, 0.22),
+ transparent 24%),
+ radial-gradient(
+ circle at 82% 84%,
+ rgba(129, 182, 255, 0.18),
+ transparent 28%),
+ linear-gradient(
+ 145deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.06) 22%,
+ rgba(105, 145, 212, 0.08) 54%,
+ rgba(9, 20, 42, 0.18));
+ color: var(--bm-text-primary);
+ padding: 6px;
+ border-radius: 16px;
+ border: 1px solid var(--bm-border-soft);
+ box-shadow:
+ 0 18px 40px rgba(0, 0, 0, 0.2),
+ inset 0 1px 0 rgba(255, 255, 255, 0.22),
+ inset 0 -1px 0 rgba(255, 255, 255, 0.05);
+ z-index: 9000;
+ transition:
+ background 320ms ease,
+ border-color 220ms ease,
+ box-shadow 220ms ease,
+ opacity 220ms ease,
+ transform 0s,
+ width 220ms ease,
+ max-width 220ms ease,
+ max-height 220ms ease;
+ top: 75px;
+ left: 60px;
+ width: auto;
+ max-height: fit-content;
+ max-width: calc(100% - 135px);
+ backdrop-filter: blur(26px) saturate(1.25);
+ font-family: var(--bm-font-body);
+ letter-spacing: 0.04em;
+}
+.bm-window::before,
+.bm-window::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+.bm-window::before {
+ border-radius: inherit;
+ padding: 1px;
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.45),
+ rgba(255, 255, 255, 0.12) 24%,
+ rgba(186, 246, 255, 0.22) 58%,
+ rgba(129, 182, 255, 0.3));
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ opacity: 0.85;
+}
+.bm-window::after {
+ border-radius: inherit;
+ background:
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.2),
+ transparent 24%),
+ radial-gradient(
+ circle at 18% 0%,
+ rgba(255, 255, 255, 0.22),
+ transparent 22%),
+ radial-gradient(
+ circle at 88% 16%,
+ rgba(186, 246, 255, 0.18),
+ transparent 18%);
+ opacity: 1;
+}
+.bm-dragbar {
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ align-items: center;
+ gap: 0.28ch;
+ padding: 0.18rem 0.24rem;
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ background:
+ radial-gradient(
+ circle at 0 0,
+ rgba(255, 255, 255, 0.22) 0,
+ transparent 42%),
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.08));
+ cursor: grab;
+ width: 100%;
+ height: fit-content;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 6px 18px rgba(0, 0, 0, 0.1);
+}
+.bm-dragbar.bm-dragging {
+ cursor: grabbing;
+}
+.bm-window:has(.bm-dragbar.bm-dragging) {
+ pointer-events: none;
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+.bm-dragbar.bm-dragging {
+ pointer-events: auto;
+}
+.bm-favicon {
+ display: inline-block;
+ height: 2.2em;
+ margin-right: 0.45ch;
+ padding: 0.2rem;
+ border-radius: 12px;
+ vertical-align: middle;
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.22),
+ rgba(255, 255, 255, 0.08));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 8px 18px rgba(0, 0, 0, 0.12);
+}
+.bm-window h1 {
+ display: inline-block;
+ font-size: 1rem;
+ font-weight: 700;
+ vertical-align: middle;
+ font-family: var(--bm-font-display);
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ color: rgba(16, 33, 60, 0.96);
+}
+.bm-dragbar h1,
+.bm-dragbar-text {
+ font-size: 0.78rem;
+ user-select: none;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.14em;
+ color: rgba(18, 37, 66, 0.95);
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.28);
+}
+.bm-dragbar div:has(h1) {
+ display: contents;
+}
+.bm-window h2 {
+ display: inline-block;
+ font-size: 0.88rem;
+ font-weight: 700;
+ vertical-align: middle;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.1em;
+ color: rgba(18, 35, 64, 0.96);
+}
+.bm-window h3 {
+ display: inline-block;
+ font-size: large;
+ font-weight: 700;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.08em;
+ color: rgba(18, 35, 64, 0.96);
+}
+.bm-window p {
+ color: var(--bm-text-secondary);
+ line-height: 1.5;
+ letter-spacing: 0.035em;
+}
+.bm-window hr {
+ border: none;
+ height: 1px;
+ margin: 0.32rem 0;
+ background:
+ linear-gradient(
+ 90deg,
+ transparent,
+ rgba(255, 255, 255, 0.28),
+ transparent);
+}
+.bm-container.bm-center-vertically {
+ width: fit-content;
+ margin-left: auto;
+ margin-right: auto;
+}
+.bm-container {
+ margin: 0.24em 0;
+}
+.bm-window input,
+.bm-window select,
+.bm-window textarea,
+.bm-window button {
+ font: inherit;
+}
+.bm-window button {
+ appearance: none;
+ color: var(--bm-text-primary);
+ font-family: var(--bm-font-display);
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.22),
+ rgba(164, 208, 255, 0.14));
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ border-radius: 999px;
+ padding: 0.28em 0.62em;
+ min-height: 1.78em;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 8px 20px rgba(0, 0, 0, 0.1);
+ transition:
+ background 180ms ease,
+ border-color 180ms ease,
+ box-shadow 180ms ease,
+ filter 180ms ease,
+ opacity 180ms ease,
+ transform 180ms ease;
+}
+.bm-window button:hover,
+.bm-window button:focus-visible {
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.28),
+ rgba(186, 246, 255, 0.18));
+ border-color: rgba(230, 245, 255, 0.36);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.26), 0 10px 22px rgba(0, 0, 0, 0.12);
+ transform: translateY(-1px);
+}
+.bm-window button:focus-visible,
+.bm-window input:focus-visible,
+.bm-window select:focus-visible,
+.bm-window textarea:focus-visible {
+ outline: none;
+ border-color: rgba(116, 231, 255, 0.62);
+ box-shadow: 0 0 0 3px rgba(116, 231, 255, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.12);
+}
+.bm-window button:active {
+ transform: translateY(0) scale(0.98);
+ filter: brightness(0.98);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 8px 16px rgba(18, 36, 78, 0.24);
+}
+.bm-window button:disabled {
+ opacity: 0.56;
+ cursor: not-allowed;
+ text-decoration: none;
+ transform: none;
+ filter: saturate(0.72);
+ box-shadow: none;
+}
+.bm-window button.bm-button-primary {
+ color: #07203b;
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.42),
+ rgba(186, 246, 255, 0.34));
+ border-color: rgba(239, 248, 255, 0.42);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.44), 0 10px 22px rgba(129, 182, 255, 0.14);
+}
+.bm-window button.bm-button-primary:hover,
+.bm-window button.bm-button-primary:focus-visible {
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.52),
+ rgba(204, 250, 255, 0.36));
+}
+.bm-window button.bm-button-secondary {
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.16),
+ rgba(255, 255, 255, 0.08));
+ border-color: rgba(255, 255, 255, 0.18);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 8px 20px rgba(0, 0, 0, 0.08);
+}
+.bm-window button.bm-button-secondary:hover,
+.bm-window button.bm-button-secondary:focus-visible {
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.24),
+ rgba(186, 246, 255, 0.12));
+}
+.bm-button-circle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ inline-size: 1.62rem;
+ block-size: 1.62rem;
+ min-height: 1.62rem !important;
+ min-width: 1.62rem;
+ margin-top: 0;
+ text-align: center;
+ line-height: 1;
+ padding: 0 !important;
+ aspect-ratio: 1 / 1;
+ border-radius: 50% !important;
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.24),
+ rgba(255, 255, 255, 0.1)) !important;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 8px 18px rgba(0, 0, 0, 0.1) !important;
+ overflow: hidden;
+ flex: 0 0 auto;
+ font-size: 0.74rem;
+}
+.bm-button-circle svg {
+ display: block;
+ width: 70%;
+ height: 70%;
+ flex: 0 0 auto;
+ margin: auto;
+ color: currentColor;
+ pointer-events: none;
+ transform: translateZ(0);
+ overflow: visible;
+}
+.bm-button-circle .bm-button-icon-settings {
+ width: 68%;
+ height: 68%;
+ transform: translateY(0.5px) translateZ(0);
+}
+.bm-button-circle .bm-button-icon-minimize {
+ width: 62%;
+ height: 62%;
+ transform: translateY(0.75px) translateZ(0);
+}
+#bm-window-filter.bm-windowed .bm-button-circle .bm-button-icon-minimize {
+ transform: translateZ(0);
+}
+.bm-button-pin {
+ vertical-align: middle;
+}
+.bm-button-pin svg {
+ width: 50%;
+ margin: 0 auto;
+ fill: #111;
+}
+.bm-window button.bm-button-trans {
+ background: transparent !important;
+ box-shadow: none !important;
+ border-color: transparent !important;
+}
+.bm-button-trans.bm-button-hover-white:hover,
+.bm-button-trans.bm-button-hover-white:focus {
+ background-color: rgba(255, 255, 255, 0.18) !important;
+}
+.bm-button-trans.bm-button-hover-white:active {
+ background-color: rgba(255, 255, 255, 0.24) !important;
+}
+.bm-button-trans.bm-button-hover-black:hover,
+.bm-button-trans.bm-button-hover-black:focus {
+ background-color: rgba(0, 0, 0, 0.14) !important;
+}
+.bm-button-trans.bm-button-hover-black:active {
+ background-color: rgba(0, 0, 0, 0.2) !important;
+}
+.bm-window input[type=number],
+.bm-window select,
+.bm-window textarea {
+ color: var(--bm-text-primary);
+ font-family: var(--bm-font-body);
+ background:
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.16),
+ rgba(255, 255, 255, 0.06));
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 12px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
+}
+input[type=number].bm-input-coords {
+ appearance: auto;
+ -moz-appearance: textfield;
+ width: 5.9ch;
+ margin-left: 1ch;
+ padding: 0.2em 0.35ch;
+ font-size: small;
+}
+input[type=number].bm-input-coords::-webkit-outer-spin-button,
+input[type=number].bm-input-coords::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+div:has(> .bm-input-file) > button {
+ width: 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.bm-input-file,
+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-window select {
+ padding: 0.22em 0.45ch;
+}
+.bm-window label:has(input[type=checkbox]) {
+ display: flex;
+ width: fit-content;
+ gap: 1ch;
+ align-items: center;
+ color: var(--bm-text-secondary);
+}
+.bm-window input[type=checkbox] {
+ width: 1em;
+ accent-color: #74e7ff;
+}
+.bm-window-content {
+ overflow: hidden;
+ max-height: calc(100% - 5px);
+ transition: height 300ms cubic-bezier(.4, 0, .2, 1);
+}
+.bm-window textarea {
+ font-size: small;
+ padding: 0.38em 0.52em;
+ height: 4em;
+ width: 100%;
+ resize: vertical;
+ line-height: 1.45;
+}
+.bm-window textarea::placeholder,
+.bm-window input::placeholder {
+ color: rgba(47, 68, 102, 0.6);
+}
+.bm-window a:not(:has(*)) {
+ color: rgba(21, 88, 164, 0.94);
+ text-decoration: underline;
+ text-decoration-color: rgba(21, 88, 164, 0.35);
+}
+.bm-window small {
+ font-size: x-small;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.12em;
+ color: var(--bm-text-secondary);
+}
+.bm-window ul li {
+ list-style: disc;
+ margin-left: 5ch;
+}
+.bm-window .bm-container.bm-scrollable {
+ max-height: var(--bm-scrollable-max-height, calc(80vh - 150px));
+ overflow: auto;
+ scrollbar-width: thin;
+ scrollbar-color: rgba(146, 221, 255, 0.42) rgba(255, 255, 255, 0.05);
+}
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar-thumb {
+ background:
+ linear-gradient(
+ 180deg,
+ rgba(116, 231, 255, 0.48),
+ rgba(83, 141, 255, 0.4));
+ border-radius: 999px;
+ border: 2px solid transparent;
+ background-clip: padding-box;
+}
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.04);
+ border-radius: 999px;
+}
+.bm-flex-between {
+ display: flex;
+ align-content: center;
+ justify-content: space-between;
+ align-items: center;
+ gap: 0.35ch;
+}
+.bm-flex-center {
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ align-items: center;
+ gap: 0.35ch;
+}
+.bm-ascii {
+ white-space: pre;
+ letter-spacing: 0;
+ line-height: 1 !important;
+ font-size: 1.6em;
+ font-family: monospace;
+}
+.bm-windowed .bm-container:not(#bm-window-main .bm-container) {
+ margin-top: 0.18em;
+ margin-bottom: 0.18em;
+}
+.bm-windowed h1:not(#bm-window-main h1) {
+ font-size: 1em;
+}
+
/* src/WindowFilter.css */
+#bm-window-filter .bm-filter-eye-icon {
+ display: block;
+ width: 1.28em;
+ height: 1.28em;
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.9;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ overflow: visible;
+}
#bm-window-filter p svg {
- display: inline;
+ display: inline-block;
height: 1em;
- fill: white;
+ width: 1em;
+ color: currentColor;
+ vertical-align: -0.16em;
+}
+#bm-window-filter:not(.bm-windowed) {
+ width: min(50rem, calc(100vw - 0.55rem));
+ max-width: min(50rem, calc(100vw - 0.55rem)) !important;
+}
+#bm-window-filter .bm-filter-header {
+ padding-top: 0.08rem;
+}
+#bm-window-filter .bm-filter-toolbar {
+ gap: 0.22rem;
+ flex-wrap: wrap;
+ width: 100%;
+ padding: 0.16rem;
+}
+#bm-window-filter .bm-filter-toolbar > button {
+ flex: 1 1 10rem;
+}
+#bm-window-filter .bm-filter-scrollable {
+ padding-right: 0.08rem;
+}
+#bm-window-filter .bm-filter-insights {
+ display: grid;
+ grid-template-columns: minmax(16rem, 20rem) minmax(0, 1fr);
+ gap: 0.24rem 0.3rem;
+ align-items: stretch;
+}
+#bm-window-filter .bm-filter-insights > hr,
+#bm-window-filter .bm-filter-sort-panel {
+ grid-column: 1 / -1;
+}
+#bm-window-filter .bm-filter-stats-card,
+#bm-window-filter .bm-filter-note,
+#bm-window-filter .bm-filter-sort-panel {
+ padding: 0.38rem 0.48rem;
+ border-radius: 13px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background:
+ linear-gradient(
+ 155deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.07));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 8px 20px rgba(0, 0, 0, 0.08);
+}
+#bm-window-filter .bm-filter-stats-card {
+ display: grid;
+ gap: 0.18rem;
+}
+#bm-window-filter .bm-filter-stats-card br {
+ display: none;
+}
+#bm-window-filter .bm-filter-stats-card span {
+ display: block;
+ padding: 0.28rem 0.38rem;
+ border-radius: 10px;
+ background:
+ linear-gradient(
+ 180deg,
+ rgba(255, 255, 255, 0.2),
+ rgba(255, 255, 255, 0.08));
+}
+#bm-window-filter .bm-filter-note p {
+ margin: 0;
+}
+#bm-window-filter .bm-filter-sort-panel fieldset {
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+#bm-window-filter .bm-filter-sort-panel legend {
+ margin-bottom: 0.12rem;
+}
+#bm-window-filter .bm-filter-sort-panel .bm-container {
+ margin-top: 0.1rem;
+ margin-bottom: 0.1rem;
+}
+#bm-window-filter .bm-filter-sort-actions {
+ display: flex;
+ justify-content: flex-start;
+}
+#bm-window-filter .bm-filter-sort-actions button {
+ min-width: 7.8rem;
}
#bm-filter-flex {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
- gap: 1em 3ch;
+ align-items: stretch;
+ gap: 0.3rem;
}
#bm-window-filter .bm-filter-color {
+ position: relative;
+ overflow: hidden;
width: fit-content;
max-width: 35ch;
- background-color: rgba(21, 48, 99, 0.9);
- border-radius: 1em;
- padding: 0.5em;
- gap: 1ch;
- transition: background-color 0.3s ease;
+ padding: 0.28rem;
+ gap: 0.28rem;
+ border-radius: 13px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background:
+ linear-gradient(
+ 160deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.07));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), 0 8px 20px rgba(0, 0, 0, 0.08);
+ transition:
+ background 0.25s ease,
+ border-color 0.25s ease,
+ box-shadow 0.25s ease,
+ transform 0.25s ease;
+}
+#bm-window-filter .bm-filter-color::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(
+ circle at top right,
+ rgba(255, 255, 255, 0.18),
+ transparent 24%),
+ radial-gradient(
+ circle at 18% 0%,
+ rgba(186, 246, 255, 0.12),
+ transparent 22%);
}
#bm-window-filter .bm-filter-color:hover,
-#bm-window-filter.bm-filter-color:focus-within {
- background-color: rgba(17, 40, 85, 0.9);
+#bm-window-filter .bm-filter-color:focus-within {
+ transform: translateY(-2px);
+ border-color: rgba(146, 221, 255, 0.26);
+ background:
+ linear-gradient(
+ 160deg,
+ rgba(255, 255, 255, 0.22),
+ rgba(186, 246, 255, 0.1));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 10px 24px rgba(0, 0, 0, 0.1);
}
#bm-window-filter .bm-filter-container-rgb {
display: block;
- border: thick double darkslategray;
width: fit-content;
height: fit-content;
- padding: 1ch;
+ padding: 0.26rem;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 10px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 6px 16px rgba(0, 0, 0, 0.08);
}
#bm-window-filter .bm-filter-color[data-id="-2"] .bm-filter-container-rgb {
background:
@@ -47,21 +762,36 @@
background-color: transparent !important;
}
#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb svg {
- fill: white !important;
+ color: white !important;
}
#bm-window-filter .bm-filter-color[data-id="0"] .bm-filter-container-rgb {
background-color: transparent !important;
}
#bm-window-filter .bm-filter-container-rgb button {
- padding: 0.75em 0.5ch;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.24em;
+ min-width: 2.05rem;
+ min-height: 2.05rem;
+ border-radius: 999px;
+ line-height: 1;
}
-#bm-window-filter .bm-filter-container-rgb svg {
- width: 4ch;
+#bm-window-filter .bm-filter-container-rgb .bm-filter-eye-icon {
+ width: 1.55rem;
+ height: 1.55rem;
+ filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.18));
}
#bm-window-filter .bm-filter-color > .bm-flex-between {
flex-direction: column;
align-items: flex-start;
- gap: 0;
+ gap: 0.02rem;
+}
+#bm-window-filter .bm-filter-color h2 {
+ margin: 0.04rem 0 0;
+}
+#bm-window-filter .bm-filter-color .bm-filter-color-pxl-desc {
+ margin: 0.1rem 0 0;
}
#bm-window-filter .bm-filter-color small {
font-size: 0.75em;
@@ -69,36 +799,321 @@
#bm-window-filter .bm-filter-color.bm-color-hide {
display: none;
}
+#bm-window-filter.bm-windowed {
+ --bm-scrollable-max-height: 100%;
+ display: grid;
+ grid-template-rows: auto minmax(0, 1fr);
+ width: 360px;
+ height: min(60vh, 22rem);
+ min-width: 360px;
+ min-height: 180px;
+ max-width: min(1000px, calc(100vw - 16px)) !important;
+ max-height: min(1400px, calc(100vh - 16px)) !important;
+ overflow: hidden;
+ box-sizing: border-box;
+ position: fixed;
+ transition:
+ background 320ms ease,
+ border-color 220ms ease,
+ box-shadow 220ms ease,
+ transform 0s;
+}
+#bm-window-filter.bm-windowed .bm-window-content {
+ display: grid;
+ grid-template-rows: auto auto auto auto minmax(0, 1fr);
+ grid-row: 2;
+ min-height: 0;
+ min-width: 0;
+ overflow: hidden;
+}
+#bm-window-filter.bm-windowed .bm-filter-toolbar {
+ gap: 0.16rem;
+ flex-wrap: nowrap;
+ width: 100%;
+ padding: 0.12rem;
+}
+#bm-window-filter.bm-windowed .bm-filter-toolbar > button {
+ flex: 1 1 0;
+ min-width: 0;
+}
#bm-window-filter.bm-windowed #bm-filter-flex {
flex-direction: column;
- gap: 0.25em;
+ align-items: stretch;
+ gap: 0.16rem;
+ width: 100%;
+ align-self: stretch;
+ min-width: 0;
+ box-sizing: border-box;
}
#bm-window-filter.bm-windowed .bm-filter-color {
- width: auto;
+ width: 100%;
+ max-width: none;
+ align-self: stretch;
+ flex: 1 1 auto;
+ min-width: 0;
margin: 0;
- padding: 0;
+ padding: 0.12rem;
+ border-radius: 11px;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-filter-color > .bm-flex-between {
+ width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
+}
+#bm-window-filter.bm-windowed .bm-container.bm-scrollable {
+ display: block;
+ grid-row: 5;
+ min-height: 0;
+ min-width: 0;
+ height: 100%;
+ width: 100%;
+ max-height: 100% !important;
+ overflow: auto;
+ box-sizing: border-box;
+}
+#bm-window-filter.bm-windowed .bm-resize-corner {
+ position: absolute;
+ right: 4px;
+ bottom: 4px;
+ display: flex;
+ width: 20px;
+ height: 20px;
+ align-items: flex-end;
+ justify-content: flex-end;
+ padding-right: 4px;
+ padding-bottom: 4px;
+ box-sizing: border-box;
+ z-index: 5;
+ cursor: nwse-resize;
+ pointer-events: auto;
+ opacity: 0.78;
+ touch-action: none;
+ user-select: none;
+ color: rgba(255, 255, 255, 0.86);
+ background:
+ linear-gradient(
+ 135deg,
+ rgba(255, 255, 255, 0.14),
+ rgba(83, 141, 255, 0.14));
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ border-radius: 8px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
+}
+#bm-window-filter.bm-windowed .bm-resize-corner:hover,
+#bm-window-filter.bm-windowed .bm-resize-corner.bm-resizing {
+ opacity: 1;
+ border-color: rgba(146, 221, 255, 0.36);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 8px 16px rgba(0, 0, 0, 0.16);
}
#bm-window-filter.bm-windowed .bm-filter-container-rgb {
display: flex;
width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
gap: 0.5ch;
align-items: center;
- padding: 0.1em 0.5ch;
+ padding: 0.1rem 0.2rem;
border: none;
- border-radius: 1em;
+ border-radius: 8px;
+ box-sizing: border-box;
}
#bm-window-filter.bm-windowed .bm-filter-container-rgb button {
- padding: 0.5em 0.25ch;
+ padding: 0.2em;
+ flex: 0 0 auto;
}
-#bm-window-filter.bm-windowed .bm-filter-container-rgb svg {
- width: 3ch;
+#bm-window-filter.bm-windowed .bm-filter-container-rgb .bm-filter-eye-icon {
+ width: 1.36rem;
+ height: 1.36rem;
}
#bm-window-filter.bm-windowed .bm-filter-color h2 {
- font-size: 0.75em;
+ font-size: 0.78rem;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
#bm-window-filter #bm-filter-windowed-color-totals {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.08rem 0.24rem;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.12);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
font-size: 1em;
}
+@media (max-width: 980px) {
+ #bm-window-filter:not(.bm-windowed) {
+ width: min(100vw - 0.35rem, 50rem);
+ max-width: min(100vw - 0.35rem, 50rem) !important;
+ }
+ #bm-window-filter .bm-filter-insights {
+ grid-template-columns: 1fr;
+ }
+ #bm-window-filter .bm-filter-note,
+ #bm-window-filter .bm-filter-sort-panel,
+ #bm-window-filter .bm-filter-insights > hr {
+ grid-column: auto;
+ }
+}
+
+/* src/WindowMain.css */
+#bm-window-main {
+ width: min(25.5rem, calc(100vw - 0.65rem));
+ max-width: min(25.5rem, calc(100vw - 0.65rem)) !important;
+}
+#bm-window-main .bm-window-content {
+ display: flex;
+ flex-direction: column;
+}
+#bm-window-main .bm-main-hero,
+#bm-window-main .bm-main-shell {
+ position: relative;
+ overflow: hidden;
+ border-radius: 14px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background:
+ linear-gradient(
+ 150deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.08));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 10px 24px rgba(0, 0, 0, 0.1);
+}
+#bm-window-main .bm-main-hero::before,
+#bm-window-main .bm-main-shell::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(
+ circle at top right,
+ rgba(255, 255, 255, 0.2),
+ transparent 26%),
+ radial-gradient(
+ circle at 20% 0%,
+ rgba(186, 246, 255, 0.16),
+ transparent 24%);
+}
+#bm-window-main .bm-main-hero {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.48rem 0.58rem;
+}
+#bm-window-main .bm-main-hero h1 {
+ margin: 0;
+}
+#bm-window-main .bm-main-stats {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.24rem;
+}
+#bm-window-main .bm-main-stat-card {
+ min-width: 0;
+ min-height: 2.55rem;
+ display: flex;
+ align-items: flex-start;
+ padding: 0.34rem 0.45rem;
+ border-radius: 11px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background:
+ linear-gradient(
+ 160deg,
+ rgba(255, 255, 255, 0.18),
+ rgba(255, 255, 255, 0.07));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
+ color: rgba(18, 35, 63, 0.96);
+ line-height: 1.22;
+}
+#bm-window-main .bm-main-stat-card > span,
+#bm-window-main .bm-main-stat-card > time {
+ min-width: 0;
+}
+#bm-window-main .bm-main-stat-card-value,
+#bm-window-main .bm-main-stat-card-timer {
+ flex-direction: column;
+ justify-content: center;
+ gap: 0.08rem;
+}
+#bm-window-main .bm-main-stat-value {
+ display: inline-block;
+ font-size: 1.3em;
+ color: rgba(15, 31, 56, 0.96);
+ white-space: normal;
+ overflow-wrap: anywhere;
+}
+#bm-window-main .bm-main-stat-value b {
+ font-size: 1em;
+}
+#bm-window-main .bm-main-stat-label {
+ color: rgba(49, 71, 105, 0.82);
+ font-size: 0.62rem;
+ letter-spacing: 0.08em;
+ font-family: var(--bm-font-display);
+ text-transform: uppercase;
+}
+#bm-window-main .bm-main-stat-card time {
+ white-space: nowrap;
+ font-family: var(--bm-font-mono);
+ letter-spacing: 0.06em;
+}
+#bm-window-main .bm-main-shell {
+ padding: 0.48rem;
+}
+#bm-window-main .bm-main-coords {
+ display: grid;
+ grid-template-columns: auto repeat(4, minmax(0, 1fr));
+ gap: 0.22rem;
+ align-items: center;
+}
+#bm-window-main .bm-main-coords .bm-button-pin {
+ width: 1.8rem;
+ height: 1.8rem;
+}
+#bm-window-main .bm-main-coords .bm-input-coords {
+ width: 100%;
+ margin-left: 0;
+ text-align: center;
+}
+#bm-window-main .bm-main-upload,
+#bm-window-main .bm-main-status {
+ margin-top: 0.24rem;
+}
+#bm-window-main .bm-main-upload > div {
+ width: 100%;
+}
+#bm-window-main .bm-main-actions {
+ gap: 0.24rem;
+ margin-top: 0.3rem;
+}
+#bm-window-main .bm-main-actions > button {
+ flex: 1 1 0;
+}
+#bm-window-main .bm-main-status textarea {
+ min-height: 4.1rem;
+}
+@media (max-width: 720px) {
+ #bm-window-main {
+ width: min(100vw - 0.45rem, 25.5rem);
+ max-width: min(100vw - 0.45rem, 25.5rem) !important;
+ }
+ #bm-window-main .bm-main-stats {
+ grid-template-columns: 1fr;
+ }
+ #bm-window-main .bm-main-coords {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ #bm-window-main .bm-main-coords .bm-button-pin {
+ grid-column: 1 / -1;
+ width: 100%;
+ aspect-ratio: auto;
+ height: 1.8rem;
+ }
+ #bm-window-main .bm-main-actions {
+ flex-direction: column;
+ }
+}
/* src/WindowSettings.css */
#bm-window-settings div:has(> .bm-highlight-preset-container) {
@@ -187,293 +1202,6 @@
gap: 0;
}
-/* src/confettiManager.css */
-div:has(> confetti-piece) {
- position: absolute;
- inset: 0;
- overflow: hidden;
- pointer-events: none;
-}
-confetti-piece {
- position: absolute;
- top: -10px;
- width: var(--size);
- height: var(--size);
- background: currentColor;
- transform: translate3d(var(--x), -10vh, 0) rotate(var(--rot));
- animation: fall var(--duration) linear var(--delay);
- will-change: transform;
- pointer-events: none;
-}
-@keyframes fall {
- to {
- transform: translate3d(var(--x), 110vh, 0) rotate(calc(var(--rot) + 720deg));
- }
-}
-
-/* src/overlay.css */
-.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-window {
- position: fixed;
- background-color: rgba(21, 48, 99, 0.9);
- color: white;
- padding: 10px;
- border-radius: 8px;
- z-index: 9000;
- transition: all 0.3s ease, transform 0s;
- top: 75px;
- left: 60px;
- width: auto;
- max-height: fit-content;
- max-width: calc(100% - 135px);
- font-family:
- "Roboto Mono",
- "Courier New",
- "Monaco",
- "DejaVu Sans Mono",
- monospace,
- "Arial";
- letter-spacing: 0.05em;
-}
-.bm-window.bm-windowed {
- max-width: 300px;
-}
-.bm-dragbar {
- display: grid;
- grid-template-columns: auto 1fr auto;
- align-items: center;
- gap: 0.5ch;
- background: url('data:image/svg+xml;utf8, ') repeat;
- cursor: grab;
- width: 100%;
- height: fit-content;
-}
-.bm-dragbar.bm-dragging {
- cursor: grabbing;
-}
-.bm-window:has(.bm-dragbar.bm-dragging) {
- pointer-events: none;
- user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
-}
-.bm-dragbar.bm-dragging {
- pointer-events: auto;
-}
-.bm-favicon {
- display: inline-block;
- height: 2.5em;
- margin-right: 1ch;
- vertical-align: middle;
-}
-.bm-window h1 {
- display: inline-block;
- font-size: x-large;
- font-weight: bold;
- vertical-align: middle;
-}
-.bm-dragbar h1,
-.bm-dragbar-text {
- font-size: 1.2em;
- user-select: none;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- text-shadow:
- 3px 0px rgba(21, 48, 99, 0.5),
- -3px 0px rgba(21, 48, 99, 0.5),
- 0px 3px rgba(21, 48, 99, 0.5),
- 0px -3px rgba(21, 48, 99, 0.5),
- 3px 3px rgba(21, 48, 99, 0.5),
- -3px 3px rgba(21, 48, 99, 0.5),
- 3px -3px rgba(21, 48, 99, 0.5),
- -3px -3px rgba(21, 48, 99, 0.5);
-}
-.bm-dragbar div:has(h1) {
- display: contents;
-}
-.bm-window h2 {
- display: inline-block;
- font-size: larger;
- font-weight: 700;
- vertical-align: middle;
-}
-.bm-window h3 {
- display: inline-block;
- font-size: large;
- font-weight: 700;
-}
-.bm-container.bm-center-vertically {
- width: fit-content;
- margin-left: auto;
- margin-right: auto;
-}
-.bm-container {
- margin: 0.5em 0;
-}
-.bm-window button {
- background-color: #144eb9;
- border-radius: 1em;
- padding: 0 0.75ch;
-}
-.bm-window button:hover,
-.bm-window button:focus-visible {
- background-color: #1061e5;
-}
-.bm-window button:active,
-.bm-window button:disabled {
- background-color: #2e97ff;
-}
-.bm-window button:disabled,
-.bm-window button:disabled {
- text-decoration: line-through;
- cursor: not-allowed;
-}
-.bm-button-circle {
- border: white 1px solid;
- height: 1.5em;
- width: 1.5em;
- margin-top: 2px;
- text-align: center;
- line-height: 1em;
- padding: 0 !important;
-}
-.bm-button-pin {
- vertical-align: middle;
-}
-.bm-button-pin svg {
- width: 50%;
- margin: 0 auto;
- fill: #111;
-}
-.bm-window button.bm-button-trans {
- background-color: unset;
-}
-.bm-button-trans.bm-button-hover-white:hover,
-.bm-button-trans.bm-button-hover-white:focus {
- background-color: rgba(255, 255, 255, 0.17);
-}
-.bm-button-trans.bm-button-hover-white:active {
- background-color: rgba(255, 255, 255, 0.22);
-}
-.bm-button-trans.bm-button-hover-black:hover,
-.bm-button-trans.bm-button-hover-black:focus {
- background-color: rgba(0, 0, 0, 0.17);
-}
-.bm-button-trans.bm-button-hover-black:active {
- background-color: rgba(0, 0, 0, 0.22);
-}
-input[type=number].bm-input-coords {
- appearance: auto;
- -moz-appearance: textfield;
- width: 5.5ch;
- margin-left: 1ch;
- background-color: rgba(0, 0, 0, 0.2);
- padding: 0 0.5ch;
- font-size: small;
-}
-input[type=number].bm-input-coords::-webkit-outer-spin-button,
-input[type=number].bm-input-coords::-webkit-inner-spin-button {
- -webkit-appearance: none;
- margin: 0;
-}
-div:has(> .bm-input-file) > button {
- width: 100%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.bm-input-file,
-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-window select {
- color: white;
- background-color: #144eb9;
- border-radius: 1em;
- padding: 0 0.5ch;
-}
-.bm-window label:has(input[type=checkbox]) {
- display: flex;
- width: fit-content;
- gap: 1ch;
-}
-.bm-window input[type=checkbox] {
- width: 1em;
-}
-.bm-window-content {
- overflow: hidden;
- transition: height 300ms cubic-bezier(.4, 0, .2, 1);
-}
-.bm-window textarea {
- font-size: small;
- background-color: rgba(0, 0, 0, 0.2);
- padding: 0 0.5ch;
- height: 5.25em;
- width: 100%;
-}
-.bm-window a:not(:has(*)) {
- text-decoration: underline;
-}
-.bm-window small {
- 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;
- justify-content: space-between;
- align-items: center;
- gap: 0.5ch;
-}
-.bm-flex-center {
- display: flex;
- align-content: center;
- justify-content: center;
- align-items: center;
- gap: 0.5ch;
-}
-.bm-ascii {
- white-space: pre;
- letter-spacing: 0;
- line-height: 1 !important;
- font-size: 1.6em;
- font-family: monospace;
-}
-.bm-windowed .bm-container:not(#bm-window-main .bm-container) {
- margin-top: 0.25em;
- margin-bottom: 0.25em;
-}
-.bm-windowed h1:not(#bm-window-main h1) {
- font-size: 1em;
-}
-
/* src/main.css */
+
+/* Build Hash: af601d626afd */
diff --git a/dist/BlueMarble-For-GreasyFork.user.js b/dist/BlueMarble-For-GreasyFork.user.js
index 6eaa865..1c896d1 100644
--- a/dist/BlueMarble-For-GreasyFork.user.js
+++ b/dist/BlueMarble-For-GreasyFork.user.js
@@ -2,14 +2,14 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.94.0
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
// @license MPL-2.0
// @supportURL https://discord.gg/tpeBPy46hf
// @homepageURL https://bluemarble.lol/
-// @icon https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/78477321232b29c09e3794c360068d7d23a0172c/dist/assets/Favicon.png
+// @icon https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/2cd51bf91944ae2acb253ea5bbd76f79b7a2edd3/dist/assets/Favicon.png
// @updateURL https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/BlueMarble-For-GreasyFork.user.js
// @downloadURL https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/BlueMarble-For-GreasyFork.user.js
// @match https://wplace.live/*
@@ -21,7 +21,7 @@
// @grant GM_xmlhttpRequest
// @grant GM.download
// @connect telemetry.thebluecorner.net
-// @resource CSS-BM-File https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/78477321232b29c09e3794c360068d7d23a0172c/dist/BlueMarble-For-GreasyFork.user.css
+// @resource CSS-BM-File https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/2cd51bf91944ae2acb253ea5bbd76f79b7a2edd3/dist/BlueMarble-For-GreasyFork.user.css
// @antifeature tracking Anonymous opt-in telemetry data
// @noframes
// ==/UserScript==
@@ -318,6 +318,8 @@
];
// src/Overlay.js
+ var minimizeIconExpanded = ' ';
+ var minimizeIconCollapsed = ' ';
var _Overlay_instances, createElement_fn, applyAttribute_fn;
var Overlay = class {
/** Constructor for the Overlay class.
@@ -1321,23 +1323,64 @@
button.style.textDecoration = "none";
const window2 = button.closest(".bm-window");
const dragbar = button.closest(".bm-dragbar");
- const header = window2.querySelector("h1");
- const windowContent = window2.querySelector(".bm-window-content");
- window2.parentElement.append(window2);
- if (button.dataset["buttonStatus"] == "expanded") {
- windowContent.style.height = windowContent.scrollHeight + "px";
- window2.style.width = window2.scrollWidth + "px";
- windowContent.style.height = "0";
- windowContent.addEventListener("transitionend", function handler() {
- windowContent.style.display = "none";
+ const header = window2?.querySelector("h1");
+ const windowContent = window2?.querySelector(".bm-window-content");
+ if (!window2 || !dragbar || !windowContent) {
+ button.disabled = false;
+ button.style.textDecoration = "";
+ return;
+ }
+ const finishMinimizeTransition = (callback) => {
+ let isFinished = false;
+ let fallbackTimer;
+ const finish = () => {
+ if (isFinished) {
+ return;
+ }
+ isFinished = true;
+ clearTimeout(fallbackTimer);
+ windowContent.removeEventListener("transitionend", handler);
+ callback();
button.disabled = false;
button.style.textDecoration = "";
- windowContent.removeEventListener("transitionend", handler);
+ };
+ const handler = (event) => {
+ if (event.target != windowContent || event.propertyName != "height") {
+ return;
+ }
+ finish();
+ };
+ windowContent.addEventListener("transitionend", handler);
+ fallbackTimer = setTimeout(finish, 360);
+ };
+ const getCollapsedHeight = () => {
+ const windowStyle = getComputedStyle(window2);
+ const toPixels = (value) => parseFloat(value) || 0;
+ const extraHeight = windowStyle.boxSizing == "border-box" ? toPixels(windowStyle.paddingTop) + toPixels(windowStyle.paddingBottom) + toPixels(windowStyle.borderTopWidth) + toPixels(windowStyle.borderBottomWidth) : 0;
+ return Math.ceil(dragbar.getBoundingClientRect().height + extraHeight + 2);
+ };
+ window2.parentElement.append(window2);
+ if (button.dataset["buttonStatus"] == "expanded") {
+ window2.dataset["widthBeforeMinimize"] = window2.style.width;
+ window2.dataset["heightBeforeMinimize"] = window2.style.height;
+ window2.dataset["minHeightBeforeMinimize"] = window2.style.minHeight;
+ windowContent.style.height = windowContent.scrollHeight + "px";
+ void windowContent.offsetHeight;
+ if (!window2.style.width) {
+ window2.style.width = window2.scrollWidth + "px";
+ }
+ finishMinimizeTransition(() => {
+ windowContent.style.display = "none";
});
- const dragbarHeader1 = header.cloneNode(true);
+ windowContent.style.height = "0";
+ if (window2.style.height || window2.classList.contains("bm-windowed")) {
+ window2.style.minHeight = "0px";
+ window2.style.height = getCollapsedHeight() + "px";
+ }
+ const dragbarHeader1 = header?.cloneNode(true) ?? document.createElement("h1");
const dragbarHeader1Text = dragbarHeader1.textContent;
button.nextElementSibling.appendChild(dragbarHeader1);
- button.textContent = "\u25B6";
+ button.innerHTML = minimizeIconCollapsed;
button.dataset["buttonStatus"] = "collapsed";
button.ariaLabel = `Unminimize window "${dragbarHeader1Text}"`;
} else {
@@ -1346,15 +1389,18 @@
dragbarHeader1.remove();
windowContent.style.display = "";
windowContent.style.height = "0";
- window2.style.width = "";
- windowContent.style.height = windowContent.scrollHeight + "px";
- windowContent.addEventListener("transitionend", function handler() {
+ window2.style.width = window2.dataset["widthBeforeMinimize"] ?? "";
+ window2.style.minHeight = window2.dataset["minHeightBeforeMinimize"] ?? "";
+ window2.style.height = window2.dataset["heightBeforeMinimize"] ?? "";
+ void windowContent.offsetHeight;
+ finishMinimizeTransition(() => {
windowContent.style.height = "";
- button.disabled = false;
- button.style.textDecoration = "";
- windowContent.removeEventListener("transitionend", handler);
+ delete window2.dataset["widthBeforeMinimize"];
+ delete window2.dataset["heightBeforeMinimize"];
+ delete window2.dataset["minHeightBeforeMinimize"];
});
- button.textContent = "\u25BC";
+ windowContent.style.height = windowContent.scrollHeight + "px";
+ button.innerHTML = minimizeIconExpanded;
button.dataset["buttonStatus"] = "expanded";
button.ariaLabel = `Minimize window "${dragbarHeader1Text}"`;
}
@@ -1366,9 +1412,11 @@
* @param {string} iMoveThingsSelector - The drag handle element
* @since 0.8.2
*/
- handleDrag(moveMeSelector, iMoveThingsSelector) {
+ handleDrag(moveMeSelector, iMoveThingsSelector, options = {}) {
const moveMe = document.querySelector(moveMeSelector);
const iMoveThings = document.querySelector(iMoveThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {
+ });
if (!moveMe || !iMoveThings) {
this.handleDisplayError(`Can not drag! ${!moveMe ? "moveMe" : ""} ${!moveMe && !iMoveThings ? "and " : ""}${!iMoveThings ? "iMoveThings " : ""}was not found!`);
return;
@@ -1438,6 +1486,12 @@
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
document.removeEventListener("touchcancel", endDrag);
+ onEnd({
+ element: moveMe,
+ x: currentX,
+ y: currentY
+ });
+ initialRect = null;
};
const onMouseMove = (event) => {
if (isDragging && initialRect) {
@@ -1467,6 +1521,124 @@
event.preventDefault();
}, { passive: false });
}
+ /** Handles resizing of an overlay window from a resize handle.
+ * @param {string} resizeMeSelector - The element to resize
+ * @param {string} iResizeThingsSelector - The resize handle element
+ * @param {{onEnd?: function({element: HTMLElement, width: number, height: number}): void, minWidth?: number, minHeight?: number, maxWidth?: number, maxHeight?: number}} [options={}]
+ * @since 0.92.0
+ */
+ handleResize(resizeMeSelector, iResizeThingsSelector, options = {}) {
+ const resizeMe = document.querySelector(resizeMeSelector);
+ const iResizeThings = document.querySelector(iResizeThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {
+ });
+ if (!resizeMe || !iResizeThings) {
+ this.handleDisplayError(`Can not resize! ${!resizeMe ? "resizeMe" : ""} ${!resizeMe && !iResizeThings ? "and " : ""}${!iResizeThings ? "iResizeThings " : ""}was not found!`);
+ return;
+ }
+ let isResizing = false;
+ let startX = 0;
+ let startY = 0;
+ let startWidth = 0;
+ let startHeight = 0;
+ let currentWidth = 0;
+ let currentHeight = 0;
+ let targetWidth = 0;
+ let targetHeight = 0;
+ let animationFrame = null;
+ const getMaximumWidth = () => Number.isFinite(options?.maxWidth) ? options.maxWidth : window.innerWidth - 16;
+ const getMaximumHeight = () => Number.isFinite(options?.maxHeight) ? options.maxHeight : window.innerHeight - 16;
+ const minimumWidth = Number.isFinite(options?.minWidth) ? options.minWidth : 200;
+ const minimumHeight = Number.isFinite(options?.minHeight) ? options.minHeight : 160;
+ const clamp = (value, minimum, maximum) => Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
+ const updateSize = () => {
+ if (isResizing) {
+ const deltaWidth = Math.abs(currentWidth - targetWidth);
+ const deltaHeight = Math.abs(currentHeight - targetHeight);
+ if (deltaWidth > 0.5 || deltaHeight > 0.5) {
+ currentWidth = targetWidth;
+ currentHeight = targetHeight;
+ resizeMe.style.width = `${currentWidth}px`;
+ resizeMe.style.height = `${currentHeight}px`;
+ }
+ animationFrame = requestAnimationFrame(updateSize);
+ }
+ };
+ const startResize = (clientX, clientY) => {
+ isResizing = true;
+ startX = clientX;
+ startY = clientY;
+ startWidth = resizeMe.offsetWidth;
+ startHeight = resizeMe.offsetHeight;
+ currentWidth = startWidth;
+ currentHeight = startHeight;
+ targetWidth = startWidth;
+ targetHeight = startHeight;
+ document.body.style.userSelect = "none";
+ iResizeThings.classList.add("bm-resizing");
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("touchmove", onTouchMove, { passive: false });
+ document.addEventListener("mouseup", endResize);
+ document.addEventListener("touchend", endResize);
+ document.addEventListener("touchcancel", endResize);
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ }
+ updateSize();
+ };
+ const endResize = () => {
+ isResizing = false;
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ animationFrame = null;
+ }
+ document.body.style.userSelect = "";
+ iResizeThings.classList.remove("bm-resizing");
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("touchmove", onTouchMove);
+ document.removeEventListener("mouseup", endResize);
+ document.removeEventListener("touchend", endResize);
+ document.removeEventListener("touchcancel", endResize);
+ onEnd({
+ element: resizeMe,
+ width: currentWidth,
+ height: currentHeight
+ });
+ };
+ const onMouseMove = (event) => {
+ if (!isResizing) {
+ return;
+ }
+ targetWidth = clamp(startWidth + (event.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (event.clientY - startY), minimumHeight, getMaximumHeight());
+ };
+ const onTouchMove = (event) => {
+ if (!isResizing) {
+ return;
+ }
+ const touch = event?.touches?.[0];
+ if (!touch) {
+ return;
+ }
+ targetWidth = clamp(startWidth + (touch.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (touch.clientY - startY), minimumHeight, getMaximumHeight());
+ event.preventDefault();
+ };
+ iResizeThings.addEventListener("mousedown", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(event.clientX, event.clientY);
+ });
+ iResizeThings.addEventListener("touchstart", (event) => {
+ const touch = event?.touches?.[0];
+ if (!touch) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(touch.clientX, touch.clientY);
+ }, { passive: false });
+ }
/** Handles status display.
* This will output plain text into the output Status box.
* Additionally, this will output an info message to the console.
@@ -1545,6 +1717,7 @@
};
// src/WindowSettings.js
+ var closeIcon = ' ';
var _WindowSettings_instances, errorOverrideFailure_fn;
var WindowSettings = class extends Overlay {
/** Constructor for the Settings window
@@ -1570,12 +1743,12 @@
document.querySelector(`#${this.windowID}`).remove();
return;
}
- this.window = this.addDiv({ "id": this.windowID, "class": "bm-window" }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
+ this.window = this.addDiv({ "id": this.windowID, "class": "bm-window" }).addDragbar().addButton({ "class": "bm-button-circle", "innerHTML": minimizeIconExpanded, "aria-label": 'Minimize window "Settings"', "data-button-status": "expanded" }, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {
button.click();
};
- }).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
+ }).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "innerHTML": closeIcon, "aria-label": 'Close window "Settings"' }, (instance, button) => {
button.onclick = () => {
document.querySelector(`#${this.windowID}`)?.remove();
};
@@ -1637,15 +1810,28 @@
* @since 0.91.39
*/
async updateUserStorage() {
+ await this.saveUserStorage();
+ }
+ /** Saves the user settings in userscript storage.
+ * @param {boolean} [force=false] - Should the throttle be ignored?
+ * @since 0.92.0
+ */
+ async saveUserStorage(force = false) {
const userSettingsCurrent = JSON.stringify(this.userSettings);
const userSettingsOld = JSON.stringify(this.userSettingsOld);
- if (userSettingsCurrent != userSettingsOld && Date.now() - this.lastUpdateTime > this.updateFrequency) {
+ if (userSettingsCurrent != userSettingsOld && (force || Date.now() - this.lastUpdateTime > this.updateFrequency)) {
await GM.setValue(this.userSettingsSaveLocation, userSettingsCurrent);
this.userSettingsOld = structuredClone(this.userSettings);
this.lastUpdateTime = Date.now();
console.log(userSettingsCurrent);
}
}
+ /** Immediately saves the user settings in userscript storage.
+ * @since 0.92.0
+ */
+ async saveUserStorageNow() {
+ await this.saveUserStorage(true);
+ }
/** Toggles a boolean flag to the state that was passed in.
* If no state was passed in, the flag will flip to the opposite state.
* The existence of the flag determines its state. If it exists, it is `true`.
@@ -2161,65 +2347,11 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
customElements.define("confetti-piece", BlueMarbleConfettiPiece);
- // src/WindowCredits.js
- var WindowCredts = class extends Overlay {
- /** Constructor for the Credits window
- * @param {string} name - The name of the userscript
- * @param {string} version - The version of the userscript
- * @since 0.90.9
- * @see {@link Overlay#constructor} for examples
- */
- constructor(name2, version2) {
- super(name2, version2);
- this.window = null;
- this.windowID = "bm-window-credits";
- this.windowParent = document.body;
- }
- /** Spawns a Credits window.
- * If another credits window already exists, we DON'T spawn another!
- * Parent/child relationships in the DOM structure below are indicated by indentation.
- * @since 0.90.9
- */
- buildWindow() {
- const ascii = `
-\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
-\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
-\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557
-\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D
-\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
-\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
-
-\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
-\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
-\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557
-\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D
-\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
-\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D
-`;
- if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
- return;
- }
- this.window = this.addDiv({ "id": this.windowID, "class": "bm-window" }, (instance, div) => {
- }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Credits"', "data-button-status": "expanded" }, (instance, button) => {
- button.onclick = () => instance.handleMinimization(button);
- button.ontouchend = () => {
- button.click();
- };
- }).buildElement().addDiv().buildElement().addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Credits"' }, (instance, button) => {
- button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
- };
- button.ontouchend = () => {
- button.click();
- };
- }).buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically" }).addHeader(1, { "textContent": "Credits" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).addSpan({ "role": "img", "aria-label": this.name }).addSpan({ "innerHTML": ascii, "class": "bm-ascii", "aria-hidden": "true" }).buildElement().buildElement().addBr().buildElement().addHr().buildElement().addBr().buildElement().addSpan({ "textContent": '"Blue Marble" userscript is made by SwingTheVine.' }).buildElement().addBr().buildElement().addSpan({ "innerHTML": 'The Blue Marble Website is made by crqch .' }).buildElement().addBr().buildElement().addSpan({ "textContent": `The Blue Marble Website used until ${localizeDate(new Date(1756069320 * 1e3))} was made by Camille Daguin.` }).buildElement().addBr().buildElement().addSpan({ "textContent": 'The favicon "Blue Marble" is owned by NASA. (The image of the Earth is owned by NASA)' }).buildElement().addBr().buildElement().addSpan({ "textContent": "Special Thanks:" }).buildElement().addUl().addLi({ "textContent": "Espresso, Meqa, and Robot for moderating SwingTheVine's community." }).buildElement().addLi({ "innerHTML": 'nof, darkness for creating similar userscripts!' }).buildElement().addLi({ "innerHTML": 'Wonda for the Blue Marble banner image!' }).buildElement().addLi({ "innerHTML": 'BullStein , allanf181 for being early beta testers!' }).buildElement().addLi({ "innerHTML": 'guidu_ and Nick-machado for the original "Minimize" Button code!' }).buildElement().addLi({ "innerHTML": 'Nomad and Gustav for the tutorials!' }).buildElement().addLi({ "innerHTML": 'cfp for creating the template overlay that Blue Marble was based on!' }).buildElement().addLi({ "innerHTML": 'Force Network for hosting the telemetry server !' }).buildElement().addLi({ "innerHTML": 'TheBlueCorner for getting me interested in online pixel canvases!' }).buildElement().buildElement().addBr().buildElement().addSpan({ "innerHTML": 'Donators :' }).buildElement().addUl().addLi({ "textContent": "Soultree" }).buildElement().addLi({ "textContent": "Espresso" }).buildElement().addLi({ "textContent": "BEST FAN" }).buildElement().addLi({ "textContent": "FuchsDresden" }).buildElement().addLi({ "textContent": "Jack" }).buildElement().addLi({ "textContent": "raiken_au" }).buildElement().addLi({ "textContent": "Jacob" }).buildElement().addLi({ "textContent": "StupidOne" }).buildElement().addLi({ "textContent": "2 Anonymous Supporters" }).buildElement().buildElement().buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
- this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
- }
- };
-
// src/WindowFilter.js
- var _WindowFilter_instances, buildColorList_fn, sortColorList_fn, selectColorList_fn, calculatePixelStatistics_fn;
+ var closeIcon2 = ' ';
+ var fullscreenIcon = ' ';
+ var windowedIcon = ' ';
+ var _WindowFilter_instances, getWindowState_fn, prefersWindowedMode_fn, setWindowModePreference_fn, syncSortFormControls_fn, closeWindow_fn, startAutoRefresh_fn, stopAutoRefresh_fn, cleanupWindowPersistence_fn, clampWindowDimension_fn, clampWindowPosition_fn, restoreWindowState_fn, saveWindowState_fn, scheduleWindowStateSave_fn, initializeWindowedPersistence_fn, buildColorList_fn, sortColorList_fn, selectColorList_fn, calculatePixelStatistics_fn;
var WindowFilter = class extends Overlay {
/** Constructor for the color filter window
* @param {*} executor - The executing class
@@ -2233,9 +2365,21 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
this.windowID = "bm-window-filter";
this.colorListID = "bm-filter-flex";
this.windowParent = document.body;
+ this.settingsManager = executor.settingsManager ?? null;
+ this.windowModeFlag = "ftr-oWin";
+ this.windowStateKey = "windowFilter";
+ this.windowResizeObserver = null;
+ this.windowViewportResizeHandler = null;
+ this.windowSaveTimeout = null;
+ this.colorRefreshInterval = null;
+ this.colorRefreshIntervalMS = 1e4;
+ this.windowMinWidth = 360;
+ this.windowMinHeight = 220;
+ this.windowMaxWidth = 1e3;
+ this.windowMaxHeight = 1400;
this.templateManager = executor.apiManager?.templateManager;
- this.eyeOpen = ' ';
- this.eyeClosed = ' ';
+ this.eyeOpen = ' ';
+ this.eyeClosed = ' ';
const { palette, LUT: _ } = this.templateManager.paletteBM;
this.palette = palette;
this.tilesLoadedTotal = 0;
@@ -2246,10 +2390,20 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
this.allPixelsTotal = 0;
this.timeRemaining = 0;
this.timeRemainingLocalized = "";
- this.sortPrimary = "id";
- this.sortSecondary = "ascending";
+ this.sortPrimary = "total";
+ this.sortSecondary = "descending";
this.showUnused = false;
}
+ /** Builds the preferred filter window mode for the user.
+ * @since 0.92.0
+ */
+ buildPreferredWindow() {
+ if (__privateMethod(this, _WindowFilter_instances, prefersWindowedMode_fn).call(this)) {
+ this.buildWindowed();
+ return;
+ }
+ this.buildWindow();
+ }
/** Spawns a Color Filter window.
* If another color filter window already exists, we DON'T spawn another!
* Parent/child relationships in the DOM structure below are indicated by indentation.
@@ -2257,41 +2411,34 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
*/
buildWindow() {
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
return;
}
this.window = this.addDiv({ "id": this.windowID, "class": "bm-window" }, (instance, div) => {
- }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
+ }).addDragbar().addButton({ "class": "bm-button-circle", "innerHTML": minimizeIconExpanded, "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {
button.click();
};
- }).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "textContent": "\u{1F5D7}", "aria-label": 'Switch to windowed mode for "Color Filter"' }, (instance, button) => {
+ }).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "innerHTML": windowedIcon, "aria-label": 'Switch to windowed mode for "Color Filter"' }, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ __privateMethod(this, _WindowFilter_instances, setWindowModePreference_fn).call(this, true);
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
this.buildWindowed();
};
button.ontouchend = () => {
button.click();
};
- }).buildElement().addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
- button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
- };
+ }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": closeIcon2, "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
+ button.onclick = () => __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
button.ontouchend = () => {
button.click();
};
- }).buildElement().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": "Hide All Colors" }, (instance, button) => {
+ }).buildElement().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically bm-filter-header" }).addHeader(1, { "textContent": "Color Filter" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-flex-between bm-center-vertically bm-filter-toolbar", "style": "gap: 1.5ch;" }).addButton({ "class": "bm-button-secondary", "textContent": "Hide All Colors" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, false);
- }).buildElement().addButton({ "textContent": "Refresh Data" }, (instance, button) => {
- button.onclick = () => {
- button.disabled = true;
- this.updateColorList();
- button.disabled = false;
- };
- }).buildElement().addButton({ "textContent": "Show All Colors" }, (instance, button) => {
+ }).buildElement().addButton({ "class": "bm-button-secondary", "textContent": "Show All Colors" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, true);
- }).buildElement().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).addDiv({ "class": "bm-container", "style": "margin-left: 2.5ch; margin-right: 2.5ch;" }).addDiv({ "class": "bm-container" }).addSpan({ "id": "bm-filter-tile-load", "innerHTML": "Tiles Loaded: 0 / ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-correct", "innerHTML": "Correct Pixels: ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-total", "innerHTML": "Total Pixels: ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-remaining", "innerHTML": "Complete: ??? (???)" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-completed", "innerHTML": "??? ???" }).buildElement().buildElement().addDiv({ "class": "bm-container" }).addP({ "innerHTML": `Press the \u{1F5D7} button to make this window smaller. Colors with the icon ${this.eyeOpen.replace(" {
+ }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-scrollable bm-filter-scrollable" }).addDiv({ "class": "bm-container bm-filter-insights", "style": "margin-left: 2.5ch; margin-right: 2.5ch;" }).addDiv({ "class": "bm-container bm-filter-stats-card" }).addSpan({ "id": "bm-filter-tile-load", "innerHTML": "Tiles Loaded: 0 / ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-correct", "innerHTML": "Correct Pixels: ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-total", "innerHTML": "Total Pixels: ???" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-remaining", "innerHTML": "Complete: ??? (???)" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-filter-tot-completed", "innerHTML": "??? ???" }).buildElement().buildElement().addDiv({ "class": "bm-container bm-filter-note" }).addP({ "innerHTML": `Press the ${windowedIcon.replace(" {
button.onclick = (event) => {
event.preventDefault();
const formData = new FormData(document.querySelector(`#${this.windowID} form`));
@@ -2306,12 +2453,14 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
const scrollableContainer = document.querySelector(`#${this.windowID} .bm-container.bm-scrollable`);
__privateMethod(this, _WindowFilter_instances, buildColorList_fn).call(this, scrollableContainer);
+ __privateMethod(this, _WindowFilter_instances, syncSortFormControls_fn).call(this);
__privateMethod(this, _WindowFilter_instances, sortColorList_fn).call(this, this.sortPrimary, this.sortSecondary, this.showUnused);
this.updateInnerHTML("#bm-filter-tile-load", `Tiles Loaded: ${localizeNumber(this.tilesLoadedTotal)} / ${localizeNumber(this.tilesTotal)}`);
this.updateInnerHTML("#bm-filter-tot-correct", `Correct Pixels: ${localizeNumber(this.allPixelsCorrectTotal)}`);
this.updateInnerHTML("#bm-filter-tot-total", `Total Pixels: ${localizeNumber(this.allPixelsTotal)}`);
this.updateInnerHTML("#bm-filter-tot-remaining", `Remaining: ${localizeNumber((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0))} (${localizePercent(((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0)) / (this.allPixelsTotal || 1))})`);
this.updateInnerHTML("#bm-filter-tot-completed", `Completed at: ${this.timeRemainingLocalized} `);
+ __privateMethod(this, _WindowFilter_instances, startAutoRefresh_fn).call(this);
}
/** Spawns a windowed Color Filter window.
* If another color filter window already exists, we DON'T spawn another!
@@ -2320,10 +2469,14 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
*/
buildWindowed() {
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
return;
}
- this.window = this.addDiv({ "id": this.windowID, "class": "bm-window bm-windowed" }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
+ this.window = this.addDiv({
+ "id": this.windowID,
+ "class": "bm-window bm-windowed",
+ "style": `width: 360px; height: min(70vh, 32rem); min-width: ${this.windowMinWidth}px; min-height: ${this.windowMinHeight}px; max-width: min(${this.windowMaxWidth}px, calc(100vw - 16px)); max-height: min(${this.windowMaxHeight}px, calc(100vh - 16px));`
+ }).addDragbar().addButton({ "class": "bm-button-circle", "innerHTML": minimizeIconExpanded, "aria-label": 'Minimize window "Color Filter"', "data-button-status": "expanded" }, (instance, button) => {
button.onclick = () => {
const windowedColorTotals = document.querySelector("#bm-filter-windowed-color-totals");
if (windowedColorTotals) {
@@ -2334,36 +2487,38 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
button.ontouchend = () => {
button.click();
};
- }).buildElement().addDiv().addSpan({ "id": "bm-filter-windowed-color-totals", "class": "bm-dragbar-text", "style": "font-weight: 700;" }).buildElement().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "textContent": "\u{1F5D6}", "aria-label": 'Switch to fullscreen mode for "Color Filter"' }, (instance, button) => {
+ }).buildElement().addDiv().addSpan({ "id": "bm-filter-windowed-color-totals", "class": "bm-dragbar-text", "style": "font-weight: 700;" }).buildElement().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "innerHTML": fullscreenIcon, "aria-label": 'Switch to fullscreen mode for "Color Filter"' }, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ __privateMethod(this, _WindowFilter_instances, setWindowModePreference_fn).call(this, false);
+ __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
this.buildWindow();
};
button.ontouchend = () => {
button.click();
};
- }).buildElement().addButton({ "class": "bm-button-circle", "textContent": "\u2716", "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
- button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
- };
+ }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": closeIcon2, "aria-label": 'Close window "Color Filter"' }, (instance, button) => {
+ button.onclick = () => __privateMethod(this, _WindowFilter_instances, closeWindow_fn).call(this);
button.ontouchend = () => {
button.click();
};
- }).buildElement().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": "None" }, (instance, button) => {
+ }).buildElement().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container bm-center-vertically bm-filter-header" }).addHeader(1, { "textContent": "Color Filter" }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-flex-between bm-center-vertically bm-filter-toolbar", "style": "gap: 1.5ch;" }).addButton({ "class": "bm-button-secondary", "textContent": "None" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, false);
- }).buildElement().addButton({ "textContent": "Refresh" }, (instance, button) => {
- button.onclick = () => {
- button.disabled = true;
- this.updateColorList();
- button.disabled = false;
- };
- }).buildElement().addButton({ "textContent": "All" }, (instance, button) => {
+ }).buildElement().addButton({ "class": "bm-button-secondary", "textContent": "All" }, (instance, button) => {
button.onclick = () => __privateMethod(this, _WindowFilter_instances, selectColorList_fn).call(this, true);
- }).buildElement().buildElement().addDiv({ "class": "bm-container bm-scrollable" }).buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
- this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
+ }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-scrollable bm-filter-scrollable" }).buildElement().buildElement().addDiv({
+ "class": "bm-resize-corner",
+ "title": "Resize Color Filter window",
+ "aria-label": "Resize Color Filter window",
+ "role": "presentation",
+ "textContent": "\u25E2",
+ "style": "position: absolute; right: 0; bottom: 0; width: 28px; height: 28px; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 4px; padding-bottom: 4px; box-sizing: border-box; z-index: 5; cursor: nwse-resize; pointer-events: auto; touch-action: none; user-select: none; font-size: 8px; line-height: 1; color: rgba(255,255,255,0.95); background: transparent; border: none; box-shadow: none;"
+ }).buildElement().buildElement().buildOverlay(this.windowParent);
+ __privateMethod(this, _WindowFilter_instances, initializeWindowedPersistence_fn).call(this);
const scrollableContainer = document.querySelector(`#${this.windowID} .bm-container.bm-scrollable`);
__privateMethod(this, _WindowFilter_instances, buildColorList_fn).call(this, scrollableContainer);
+ __privateMethod(this, _WindowFilter_instances, syncSortFormControls_fn).call(this);
__privateMethod(this, _WindowFilter_instances, sortColorList_fn).call(this, this.sortPrimary, this.sortSecondary, this.showUnused);
+ __privateMethod(this, _WindowFilter_instances, startAutoRefresh_fn).call(this);
}
/** The information about a specific color on the palette.
* @typedef {Object} ColorData
@@ -2414,6 +2569,11 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
const allTotal = this.allPixelsTotal.toString().length > 7 ? this.allPixelsTotal.toString().slice(0, 2) + "\u2026" + this.allPixelsTotal.toString().slice(-3) : this.allPixelsTotal.toString();
this.updateInnerHTML("#bm-filter-windowed-color-totals", `${allCorrect}/${allTotal}`, true);
}
+ this.updateInnerHTML("#bm-filter-tile-load", `Tiles Loaded: ${localizeNumber(this.tilesLoadedTotal)} / ${localizeNumber(this.tilesTotal)}`);
+ this.updateInnerHTML("#bm-filter-tot-correct", `Correct Pixels: ${localizeNumber(this.allPixelsCorrectTotal)}`);
+ this.updateInnerHTML("#bm-filter-tot-total", `Total Pixels: ${localizeNumber(this.allPixelsTotal)}`);
+ this.updateInnerHTML("#bm-filter-tot-remaining", `Remaining: ${localizeNumber((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0))} (${localizePercent(((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0)) / (this.allPixelsTotal || 1))})`);
+ this.updateInnerHTML("#bm-filter-tot-completed", `Completed at: ${this.timeRemainingLocalized} `);
if (!colorList) {
return colorStatistics;
}
@@ -2445,6 +2605,259 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
}
};
_WindowFilter_instances = new WeakSet();
+ /** Retrieves the persisted window state object.
+ * @returns {Object | null}
+ * @since 0.92.0
+ */
+ getWindowState_fn = function() {
+ var _a, _b;
+ if (!this.settingsManager) {
+ return null;
+ }
+ (_a = this.settingsManager.userSettings)[_b = this.windowStateKey] ?? (_a[_b] = {});
+ return this.settingsManager.userSettings[this.windowStateKey];
+ };
+ /** Returns whether the filter should open in windowed mode.
+ * Defaults to windowed mode when no explicit preference was stored.
+ * @returns {boolean}
+ * @since 0.92.1
+ */
+ prefersWindowedMode_fn = function() {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (windowState?.mode == "windowed") {
+ return true;
+ }
+ if (windowState?.mode == "fullscreen") {
+ return false;
+ }
+ return true;
+ };
+ /** Updates the preferred window mode setting.
+ * @param {boolean} shouldBeWindowed
+ * @since 0.92.0
+ */
+ setWindowModePreference_fn = function(shouldBeWindowed) {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (windowState) {
+ windowState.mode = shouldBeWindowed ? "windowed" : "fullscreen";
+ }
+ if (!this.settingsManager) {
+ return;
+ }
+ this.settingsManager.toggleFlag(this.windowModeFlag, shouldBeWindowed);
+ void this.settingsManager.saveUserStorageNow();
+ };
+ /** Updates the visible sort controls to reflect the active sort state.
+ * @since 0.92.1
+ */
+ syncSortFormControls_fn = function() {
+ const sortPrimaryInput = document.querySelector(`#${this.windowID} #bm-filter-sort-primary`);
+ const sortSecondaryInput = document.querySelector(`#${this.windowID} #bm-filter-sort-secondary`);
+ const showUnusedInput = document.querySelector(`#${this.windowID} #bm-filter-show-unused`);
+ if (sortPrimaryInput instanceof HTMLSelectElement) {
+ sortPrimaryInput.value = this.sortPrimary;
+ }
+ if (sortSecondaryInput instanceof HTMLSelectElement) {
+ sortSecondaryInput.value = this.sortSecondary;
+ }
+ if (showUnusedInput instanceof HTMLInputElement) {
+ showUnusedInput.checked = this.showUnused;
+ }
+ };
+ /** Immediately closes the filter window and cleans up persistence observers.
+ * @since 0.92.0
+ */
+ closeWindow_fn = function() {
+ const windowElement = document.querySelector(`#${this.windowID}`);
+ if (windowElement?.classList.contains("bm-windowed")) {
+ __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, windowElement);
+ }
+ __privateMethod(this, _WindowFilter_instances, stopAutoRefresh_fn).call(this);
+ __privateMethod(this, _WindowFilter_instances, cleanupWindowPersistence_fn).call(this);
+ windowElement?.remove();
+ };
+ /** Starts the automatic Color Filter statistics refresh loop.
+ * @since 0.92.1
+ */
+ startAutoRefresh_fn = function() {
+ __privateMethod(this, _WindowFilter_instances, stopAutoRefresh_fn).call(this);
+ this.colorRefreshInterval = setInterval(() => {
+ if (!document.querySelector(`#${this.windowID}`)) {
+ __privateMethod(this, _WindowFilter_instances, stopAutoRefresh_fn).call(this);
+ return;
+ }
+ this.updateColorList();
+ }, this.colorRefreshIntervalMS);
+ };
+ /** Stops the automatic Color Filter statistics refresh loop.
+ * @since 0.92.1
+ */
+ stopAutoRefresh_fn = function() {
+ if (!this.colorRefreshInterval) {
+ return;
+ }
+ clearInterval(this.colorRefreshInterval);
+ this.colorRefreshInterval = null;
+ };
+ /** Disconnects live observers used for window persistence.
+ * @since 0.92.0
+ */
+ cleanupWindowPersistence_fn = function() {
+ if (this.windowResizeObserver) {
+ this.windowResizeObserver.disconnect();
+ this.windowResizeObserver = null;
+ }
+ if (this.windowViewportResizeHandler) {
+ window.removeEventListener("resize", this.windowViewportResizeHandler);
+ this.windowViewportResizeHandler = null;
+ }
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ this.windowSaveTimeout = null;
+ }
+ };
+ /** Returns a clamped dimension value for the window.
+ * @param {number} size - The size in pixels
+ * @param {number} minimum - Minimum allowed size
+ * @param {number} maximum - Maximum allowed size
+ * @returns {number}
+ * @since 0.92.0
+ */
+ clampWindowDimension_fn = function(size, minimum, maximum) {
+ const resolvedMaximum = Math.max(minimum, maximum);
+ return Math.min(Math.max(Math.round(Number(size) || minimum), minimum), resolvedMaximum);
+ };
+ /** Returns a viewport-safe position for the window.
+ * @param {HTMLElement} windowElement
+ * @param {number} x
+ * @param {number} y
+ * @returns {{x: number, y: number}}
+ * @since 0.92.0
+ */
+ clampWindowPosition_fn = function(windowElement, x, y) {
+ const margin = 8;
+ const maxX = Math.max(margin, window.innerWidth - windowElement.offsetWidth - margin);
+ const maxY = Math.max(margin, window.innerHeight - windowElement.offsetHeight - margin);
+ return {
+ x: Math.min(Math.max(Math.round(Number(x) || margin), margin), maxX),
+ y: Math.min(Math.max(Math.round(Number(y) || margin), margin), maxY)
+ };
+ };
+ /** Applies the persisted size and position to the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ restoreWindowState_fn = function(windowElement) {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (!windowState || !windowElement) {
+ return;
+ }
+ const width = Number(windowState.width);
+ const height = Number(windowState.height);
+ const hasWidth = Number.isFinite(width);
+ const hasHeight = Number.isFinite(height);
+ if (hasWidth) {
+ windowState.width = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ windowElement.style.width = `${windowState.width}px`;
+ }
+ if (hasHeight) {
+ windowState.height = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+ windowElement.style.height = `${windowState.height}px`;
+ }
+ requestAnimationFrame(() => {
+ if (!windowElement.isConnected) {
+ return;
+ }
+ const x = Number(windowState.x);
+ const y = Number(windowState.y);
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
+ return;
+ }
+ const clampedPosition = __privateMethod(this, _WindowFilter_instances, clampWindowPosition_fn).call(this, windowElement, x, y);
+ windowElement.style.left = "0px";
+ windowElement.style.top = "0px";
+ windowElement.style.right = "";
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+ if (clampedPosition.x != x || clampedPosition.y != y) {
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ void this.settingsManager?.saveUserStorageNow();
+ }
+ });
+ };
+ /** Saves the current size and position of the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ saveWindowState_fn = function(windowElement) {
+ const windowState = __privateMethod(this, _WindowFilter_instances, getWindowState_fn).call(this);
+ if (!windowState || !windowElement?.isConnected || !windowElement.classList.contains("bm-windowed")) {
+ return;
+ }
+ if (windowElement.querySelector('.bm-dragbar button[data-button-status="collapsed"]')) {
+ return;
+ }
+ const rect = windowElement.getBoundingClientRect();
+ const width = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, rect.width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ const height = __privateMethod(this, _WindowFilter_instances, clampWindowDimension_fn).call(this, rect.height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+ if (Math.round(rect.width) != width) {
+ windowElement.style.width = `${width}px`;
+ }
+ if (Math.round(rect.height) != height) {
+ windowElement.style.height = `${height}px`;
+ }
+ const clampedPosition = __privateMethod(this, _WindowFilter_instances, clampWindowPosition_fn).call(this, windowElement, rect.left, rect.top);
+ windowElement.style.left = "0px";
+ windowElement.style.top = "0px";
+ windowElement.style.right = "";
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ windowState.width = width;
+ windowState.height = height;
+ void this.settingsManager?.saveUserStorageNow();
+ };
+ /** Debounces persisting the current window size and position.
+ * @param {HTMLElement} windowElement
+ * @param {number} [delay=150]
+ * @since 0.92.0
+ */
+ scheduleWindowStateSave_fn = function(windowElement, delay = 150) {
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ }
+ this.windowSaveTimeout = setTimeout(() => {
+ this.windowSaveTimeout = null;
+ __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, windowElement);
+ }, delay);
+ };
+ /** Enables persistence and resize handling for the windowed filter.
+ * @since 0.92.0
+ */
+ initializeWindowedPersistence_fn = function() {
+ const windowElement = document.querySelector(`#${this.windowID}.bm-window`);
+ if (!windowElement) {
+ return;
+ }
+ __privateMethod(this, _WindowFilter_instances, cleanupWindowPersistence_fn).call(this);
+ __privateMethod(this, _WindowFilter_instances, restoreWindowState_fn).call(this, windowElement);
+ this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`, {
+ onEnd: ({ element }) => __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, element)
+ });
+ this.handleResize(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-resize-corner`, {
+ minWidth: this.windowMinWidth,
+ minHeight: this.windowMinHeight,
+ maxWidth: Math.min(this.windowMaxWidth, window.innerWidth - 16),
+ maxHeight: Math.min(this.windowMaxHeight, window.innerHeight - 16),
+ onEnd: ({ element }) => __privateMethod(this, _WindowFilter_instances, saveWindowState_fn).call(this, element)
+ });
+ if (typeof ResizeObserver == "function") {
+ this.windowResizeObserver = new ResizeObserver(() => __privateMethod(this, _WindowFilter_instances, scheduleWindowStateSave_fn).call(this, windowElement));
+ this.windowResizeObserver.observe(windowElement);
+ }
+ this.windowViewportResizeHandler = () => __privateMethod(this, _WindowFilter_instances, scheduleWindowStateSave_fn).call(this, windowElement, 0);
+ window.addEventListener("resize", this.windowViewportResizeHandler);
+ };
/** Creates the color list container.
* @param {HTMLElement} parentElement - Parent element to add the color list to as a child
* @since 0.88.222
@@ -2489,22 +2902,23 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
"class": "bm-button-trans " + bgEffectForButtons,
"data-state": isColorHidden ? "hidden" : "shown",
"aria-label": isColorHidden ? `Show the color ${color.name || ""} on templates.` : `Hide the color ${color.name || ""} on templates.`,
- "innerHTML": isColorHidden ? this.eyeClosed.replace(" {
button.onclick = () => {
button.style.textDecoration = "none";
button.disabled = true;
if (button.dataset["state"] == "shown") {
- button.innerHTML = this.eyeClosed.replace(" {
button.onclick = () => {
button.style.textDecoration = "none";
button.disabled = true;
if (button.dataset["state"] == "shown") {
- button.innerHTML = this.eyeClosed.replace(" {
+ }).addDragbar().addButton({ "class": "bm-button-circle", "innerHTML": minimizeIconExpanded, "aria-label": 'Minimize window "Blue Marble"', "data-button-status": "expanded" }, (instance, button) => {
+ button.onclick = () => instance.handleMinimization(button);
+ button.ontouchend = () => {
+ button.click();
+ };
+ }).buildElement().addDiv().buildElement().addDiv({ "class": "bm-flex-center" }).addButton({ "class": "bm-button-circle", "innerHTML": settingsIcon, "title": "Settings", "aria-label": "Open settings" }, (instance, button) => {
+ button.onclick = () => {
+ instance.settingsManager.buildWindow();
+ };
+ }).buildElement().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addHr().buildElement().addDiv({ "class": "bm-container bm-main-hero" }).addImg({ "class": "bm-favicon", "src": "https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png" }, (instance, img) => {
+ const date = /* @__PURE__ */ new Date();
+ const dayOfTheYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 1)) / (1e3 * 60 * 60 * 24)) + 1;
+ if (dayOfTheYear == 204) {
+ img.parentNode.style.position = "relative";
+ img.parentNode.innerHTML = img.parentNode.innerHTML + ` `;
+ img.onload = () => {
+ const confettiManager = new ConfettiManager();
+ confettiManager.createConfetti(document.querySelector(`#${this.windowID}`));
+ };
+ }
+ }).buildElement().addHeader(1, { "textContent": this.name }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-main-stats" }).addDiv({ "class": "bm-main-stat-card bm-main-stat-card-value" }).addSpan({ "class": "bm-main-stat-label", "textContent": "Droplets" }).buildElement().addSpan({ "id": "bm-user-droplets", "class": "bm-main-stat-value", "textContent": "0" }).buildElement().buildElement().addDiv({ "class": "bm-main-stat-card bm-main-stat-card-value" }).addSpan({ "class": "bm-main-stat-label", "textContent": "Next Level" }).buildElement().addSpan({ "id": "bm-user-nextlevel", "class": "bm-main-stat-value", "textContent": "0 px" }).buildElement().buildElement().addDiv({ "class": "bm-main-stat-card bm-main-stat-card-timer" }).addSpan({ "class": "bm-main-stat-label", "textContent": "Charges" }).buildElement().addTimer(Date.now(), 1e3, { "class": "bm-main-stat-value", "style": "font-weight: 700;" }, (instance, timer) => {
+ instance.apiManager.chargeRefillTimerID = timer.id;
+ }).buildElement().buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container bm-main-shell" }).addDiv({ "class": "bm-container bm-main-coords" }).addButton(
+ { "class": "bm-button-circle bm-button-pin", "style": "margin-top: 0;", "innerHTML": ' ' },
+ (instance, button) => {
+ button.onclick = () => {
+ const coords2 = instance.apiManager?.coordsTilePixel;
+ if (!coords2?.[0]) {
+ instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
+ return;
+ }
+ instance.updateInnerHTML("bm-input-tx", coords2?.[0] || "");
+ instance.updateInnerHTML("bm-input-ty", coords2?.[1] || "");
+ instance.updateInnerHTML("bm-input-px", coords2?.[2] || "");
+ instance.updateInnerHTML("bm-input-py", coords2?.[3] || "");
+ };
+ }
+ ).buildElement().addInput({ "type": "number", "id": "bm-input-tx", "class": "bm-input-coords", "placeholder": "Tl X", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
+ input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
+ }).buildElement().addInput({ "type": "number", "id": "bm-input-ty", "class": "bm-input-coords", "placeholder": "Tl Y", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
+ input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
+ }).buildElement().addInput({ "type": "number", "id": "bm-input-px", "class": "bm-input-coords", "placeholder": "Px X", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
+ input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
+ }).buildElement().addInput({ "type": "number", "id": "bm-input-py", "class": "bm-input-coords", "placeholder": "Px Y", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
+ input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
+ }).buildElement().buildElement().addDiv({ "class": "bm-container bm-main-upload" }).addInputFile({ "class": "bm-input-file", "textContent": "Upload Template", "accept": "image/png, image/jpeg, image/webp, image/bmp, image/gif" }).buildElement().buildElement().addDiv({ "class": "bm-container bm-flex-between bm-main-actions" }).addButton({ "class": "bm-button-secondary", "textContent": "Disable", "data-button-status": "shown" }, (instance, button) => {
+ button.onclick = () => {
+ button.disabled = true;
+ if (button.dataset["buttonStatus"] == "shown") {
+ instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(false);
+ button.dataset["buttonStatus"] = "hidden";
+ button.textContent = "Enable";
+ instance.handleDisplayStatus(`Disabled templates!`);
+ } else {
+ instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(true);
+ button.dataset["buttonStatus"] = "shown";
+ button.textContent = "Disable";
+ instance.handleDisplayStatus(`Enabled templates!`);
+ }
+ button.disabled = false;
+ };
+ }).buildElement().addButton({ "class": "bm-button-primary", "textContent": "Create" }, (instance, button) => {
+ button.onclick = () => {
+ const input = document.querySelector(`#${this.windowID} .bm-input-file`);
+ const coordTlX = document.querySelector("#bm-input-tx");
+ if (!coordTlX.checkValidity()) {
+ coordTlX.reportValidity();
+ instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
+ return;
+ }
+ const coordTlY = document.querySelector("#bm-input-ty");
+ if (!coordTlY.checkValidity()) {
+ coordTlY.reportValidity();
+ instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
+ return;
+ }
+ const coordPxX = document.querySelector("#bm-input-px");
+ if (!coordPxX.checkValidity()) {
+ coordPxX.reportValidity();
+ instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
+ return;
+ }
+ const coordPxY = document.querySelector("#bm-input-py");
+ if (!coordPxY.checkValidity()) {
+ coordPxY.reportValidity();
+ instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
+ return;
+ }
+ if (!input?.files[0]) {
+ instance.handleDisplayError(`No file selected!`);
+ return;
+ }
+ instance?.apiManager?.templateManager.createTemplate(input.files[0], input.files[0]?.name.replace(/\.[^/.]+$/, ""), [Number(coordTlX.value), Number(coordTlY.value), Number(coordPxX.value), Number(coordPxY.value)]);
+ instance.handleDisplayStatus(`Drew to canvas!`);
+ };
+ }).buildElement().addButton({ "class": "bm-button-secondary", "textContent": "Filter" }, (instance, button) => {
+ button.onclick = () => this.buildWindowFilter();
+ }).buildElement().buildElement().addDiv({ "class": "bm-container bm-main-status" }).addTextarea({ "id": this.outputStatusId, "placeholder": `Status: Sleeping...
+Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
+ this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
+ }
+ /** 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() {
+ const windowFilter = new WindowFilter(this);
+ windowFilter.buildPreferredWindow();
+ }
+ };
+ _WindowMain_instances = new WeakSet();
+ coordinateInputPaste_fn = async function(instance, input, event) {
+ event.preventDefault();
+ const data = await getClipboardData(event);
+ const coords2 = data.split(/[^a-zA-Z0-9]+/).filter((index) => index).map(Number).filter(
+ (number) => !isNaN(number)
+ // Removes NaN `[4]`
+ );
+ if (coords2.length == 2 && input.id == "bm-input-px") {
+ instance.updateInnerHTML("bm-input-px", coords2?.[0] || "");
+ instance.updateInnerHTML("bm-input-py", coords2?.[1] || "");
+ } else if (coords2.length == 1) {
+ instance.updateInnerHTML(input.id, coords2?.[0] || "");
+ } else {
+ instance.updateInnerHTML("bm-input-tx", coords2?.[0] || "");
+ instance.updateInnerHTML("bm-input-ty", coords2?.[1] || "");
+ instance.updateInnerHTML("bm-input-px", coords2?.[2] || "");
+ instance.updateInnerHTML("bm-input-py", coords2?.[3] || "");
+ }
+ };
+
// src/WindowWizard.js
var _WindowWizard_instances, displaySchemaHealth_fn, displayTemplateList_fn, convertSchema_1_x_x_To_2_x_x_fn;
var _WindowWizard = class _WindowWizard extends Overlay {
@@ -2692,7 +3268,7 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
style = style.concat("z-index: 9001;").trim();
}
this.window = this.addDiv({ "id": this.windowID, "class": "bm-window", "style": style }, (instance, div) => {
- }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Template Wizard"', "data-button-status": "expanded" }, (instance, button) => {
+ }).addDragbar().addButton({ "class": "bm-button-circle", "innerHTML": minimizeIconExpanded, "aria-label": 'Minimize window "Template Wizard"', "data-button-status": "expanded" }, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {
button.click();
@@ -2818,189 +3394,8 @@ Getting Y ${pixelY}-${pixelY + drawSizeY}`);
};
var WindowWizard = _WindowWizard;
- // src/WindowMain.js
- var _WindowMain_instances, buildWindowFilter_fn, coordinateInputPaste_fn;
- var WindowMain = class extends Overlay {
- /** Constructor for the main Blue Marble window
- * @param {string} name - The name of the userscript
- * @param {string} version - The version of the userscript
- * @since 0.88.326
- * @see {@link Overlay#constructor}
- */
- constructor(name2, version2) {
- super(name2, version2);
- __privateAdd(this, _WindowMain_instances);
- this.window = null;
- this.windowID = "bm-window-main";
- this.windowParent = document.body;
- }
- /** Creates the main Blue Marble window.
- * Parent/child relationships in the DOM structure below are indicated by indentation.
- * @since 0.58.3
- */
- buildWindow() {
- if (document.querySelector(`#${this.windowID}`)) {
- this.handleDisplayError("Main window already exists!");
- return;
- }
- this.window = this.addDiv({ "id": this.windowID, "class": "bm-window bm-windowed", "style": "top: 10px; left: unset; right: 75px;" }, (instance, div) => {
- }).addDragbar().addButton({ "class": "bm-button-circle", "textContent": "\u25BC", "aria-label": 'Minimize window "Blue Marble"', "data-button-status": "expanded" }, (instance, button) => {
- button.onclick = () => instance.handleMinimization(button);
- button.ontouchend = () => {
- button.click();
- };
- }).buildElement().addDiv().buildElement().buildElement().addDiv({ "class": "bm-window-content" }).addDiv({ "class": "bm-container" }).addImg({ "class": "bm-favicon", "src": "https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png" }, (instance, img) => {
- const date = /* @__PURE__ */ new Date();
- const dayOfTheYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 1)) / (1e3 * 60 * 60 * 24)) + 1;
- if (dayOfTheYear == 204) {
- img.parentNode.style.position = "relative";
- img.parentNode.innerHTML = img.parentNode.innerHTML + ` `;
- img.onload = () => {
- const confettiManager = new ConfettiManager();
- confettiManager.createConfetti(document.querySelector(`#${this.windowID}`));
- };
- }
- }).buildElement().addHeader(1, { "textContent": this.name }).buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container" }).addSpan({ "id": "bm-user-droplets", "textContent": "Droplets:" }).buildElement().addBr().buildElement().addSpan({ "id": "bm-user-nextlevel", "textContent": "Next level in..." }).buildElement().addBr().buildElement().addSpan({ "textContent": "Charges: " }).addTimer(Date.now(), 1e3, { "style": "font-weight: 700;" }, (instance, timer) => {
- instance.apiManager.chargeRefillTimerID = timer.id;
- }).buildElement().buildElement().buildElement().addHr().buildElement().addDiv({ "class": "bm-container" }).addDiv({ "class": "bm-container" }).addButton(
- { "class": "bm-button-circle bm-button-pin", "style": "margin-top: 0;", "innerHTML": ' ' },
- (instance, button) => {
- button.onclick = () => {
- const coords2 = instance.apiManager?.coordsTilePixel;
- if (!coords2?.[0]) {
- instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
- return;
- }
- instance.updateInnerHTML("bm-input-tx", coords2?.[0] || "");
- instance.updateInnerHTML("bm-input-ty", coords2?.[1] || "");
- instance.updateInnerHTML("bm-input-px", coords2?.[2] || "");
- instance.updateInnerHTML("bm-input-py", coords2?.[3] || "");
- };
- }
- ).buildElement().addInput({ "type": "number", "id": "bm-input-tx", "class": "bm-input-coords", "placeholder": "Tl X", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
- input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
- }).buildElement().addInput({ "type": "number", "id": "bm-input-ty", "class": "bm-input-coords", "placeholder": "Tl Y", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
- input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
- }).buildElement().addInput({ "type": "number", "id": "bm-input-px", "class": "bm-input-coords", "placeholder": "Px X", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
- input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
- }).buildElement().addInput({ "type": "number", "id": "bm-input-py", "class": "bm-input-coords", "placeholder": "Px Y", "min": 0, "max": 2047, "step": 1, "required": true }, (instance, input) => {
- input.addEventListener("paste", (event) => __privateMethod(this, _WindowMain_instances, coordinateInputPaste_fn).call(this, instance, input, event));
- }).buildElement().buildElement().addDiv({ "class": "bm-container" }).addInputFile({ "class": "bm-input-file", "textContent": "Upload Template", "accept": "image/png, image/jpeg, image/webp, image/bmp, image/gif" }).buildElement().buildElement().addDiv({ "class": "bm-container bm-flex-between" }).addButton({ "textContent": "Disable", "data-button-status": "shown" }, (instance, button) => {
- button.onclick = () => {
- button.disabled = true;
- if (button.dataset["buttonStatus"] == "shown") {
- instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(false);
- button.dataset["buttonStatus"] = "hidden";
- button.textContent = "Enable";
- instance.handleDisplayStatus(`Disabled templates!`);
- } else {
- instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(true);
- button.dataset["buttonStatus"] = "shown";
- button.textContent = "Disable";
- instance.handleDisplayStatus(`Enabled templates!`);
- }
- button.disabled = false;
- };
- }).buildElement().addButton({ "textContent": "Create" }, (instance, button) => {
- button.onclick = () => {
- const input = document.querySelector(`#${this.windowID} .bm-input-file`);
- const coordTlX = document.querySelector("#bm-input-tx");
- if (!coordTlX.checkValidity()) {
- coordTlX.reportValidity();
- instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
- return;
- }
- const coordTlY = document.querySelector("#bm-input-ty");
- if (!coordTlY.checkValidity()) {
- coordTlY.reportValidity();
- instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
- return;
- }
- const coordPxX = document.querySelector("#bm-input-px");
- if (!coordPxX.checkValidity()) {
- coordPxX.reportValidity();
- instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
- return;
- }
- const coordPxY = document.querySelector("#bm-input-py");
- if (!coordPxY.checkValidity()) {
- coordPxY.reportValidity();
- instance.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");
- return;
- }
- if (!input?.files[0]) {
- instance.handleDisplayError(`No file selected!`);
- return;
- }
- instance?.apiManager?.templateManager.createTemplate(input.files[0], input.files[0]?.name.replace(/\.[^/.]+$/, ""), [Number(coordTlX.value), Number(coordTlY.value), Number(coordPxX.value), Number(coordPxY.value)]);
- instance.handleDisplayStatus(`Drew to canvas!`);
- };
- }).buildElement().addButton({ "textContent": "Filter" }, (instance, button) => {
- button.onclick = () => __privateMethod(this, _WindowMain_instances, buildWindowFilter_fn).call(this);
- }).buildElement().buildElement().addDiv({ "class": "bm-container" }).addTextarea({ "id": this.outputStatusId, "placeholder": `Status: Sleeping...
-Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().addDiv({ "class": "bm-container bm-flex-between", "style": "margin-bottom: 0; flex-direction: column;" }).addDiv({ "class": "bm-flex-between" }).addButton({ "class": "bm-button-circle", "innerHTML": "\u2699\uFE0F", "title": "Settings" }, (instance, button) => {
- button.onclick = () => {
- instance.settingsManager.buildWindow();
- };
- }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": "\u{1F9D9}", "title": "Template Wizard" }, (instance, button) => {
- button.onclick = () => {
- const templateManager2 = instance.apiManager?.templateManager;
- const wizard = new WindowWizard(this.name, this.version, templateManager2?.schemaVersion, templateManager2);
- wizard.buildWindow();
- };
- }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": "\u{1F3A8}", "title": "Template Color Converter" }, (instance, button) => {
- button.onclick = () => {
- window.open("https://pepoafonso.github.io/color_converter_wplace/", "_blank", "noopener noreferrer");
- };
- }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": "\u{1F310}", "title": "Official Blue Marble Website" }, (instance, button) => {
- button.onclick = () => {
- window.open("https://bluemarble.lol/", "_blank", "noopener noreferrer");
- };
- }).buildElement().addButton({ "class": "bm-button-circle", "title": "Donate to SwingTheVine", "innerHTML": ' ' }, (instance, button) => {
- button.onclick = () => {
- window.open("https://ko-fi.com/swingthevine", "_blank", "noopener noreferrer");
- };
- }).buildElement().addButton({ "class": "bm-button-circle", "innerHTML": "\u{1F91D}", "title": "Credits" }, (instance, button) => {
- button.onclick = () => {
- const credits = new WindowCredts(this.name, this.version);
- credits.buildWindow();
- };
- }).buildElement().buildElement().addSmall({ "textContent": "Made by SwingTheVine", "style": "margin-top: auto;" }).buildElement().buildElement().buildElement().buildElement().buildElement().buildOverlay(this.windowParent);
- this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
- }
- };
- _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() {
- const windowFilter = new WindowFilter(this);
- windowFilter.buildWindow();
- };
- coordinateInputPaste_fn = async function(instance, input, event) {
- event.preventDefault();
- const data = await getClipboardData(event);
- const coords2 = data.split(/[^a-zA-Z0-9]+/).filter((index) => index).map(Number).filter(
- (number) => !isNaN(number)
- // Removes NaN `[4]`
- );
- if (coords2.length == 2 && input.id == "bm-input-px") {
- instance.updateInnerHTML("bm-input-px", coords2?.[0] || "");
- instance.updateInnerHTML("bm-input-py", coords2?.[1] || "");
- } else if (coords2.length == 1) {
- instance.updateInnerHTML(input.id, coords2?.[0] || "");
- } else {
- instance.updateInnerHTML("bm-input-tx", coords2?.[0] || "");
- instance.updateInnerHTML("bm-input-ty", coords2?.[1] || "");
- instance.updateInnerHTML("bm-input-px", coords2?.[2] || "");
- instance.updateInnerHTML("bm-input-py", coords2?.[3] || "");
- }
- };
-
// src/templateManager.js
- var _TemplateManager_instances, loadTemplate_fn, storeTemplates_fn, parseBlueMarble_fn, parseOSU_fn, calculateCorrectPixelsOnTile_And_FilterTile_fn;
+ var _TemplateManager_instances, restoreFilteredColorsFromSettings_fn, persistFilteredColors_fn, loadTemplate_fn, storeTemplates_fn, parseBlueMarble_fn, parseOSU_fn, calculateCorrectPixelsOnTile_And_FilterTile_fn;
var TemplateManager = class {
/** The constructor for the {@link TemplateManager} class.
* @param {string} name - The name of the userscript
@@ -3041,6 +3436,24 @@ Version: ${this.version}`, "readOnly": true }).buildElement().buildElement().add
*/
setSettingsManager(settingsManager2) {
this.settingsManager = settingsManager2;
+ __privateMethod(this, _TemplateManager_instances, restoreFilteredColorsFromSettings_fn).call(this);
+ }
+ /** Updates whether a palette color should be hidden on the canvas.
+ * @param {number} colorID
+ * @param {boolean} shouldHide
+ * @since 0.92.1
+ */
+ setColorFiltered(colorID, shouldHide) {
+ const parsedColorID = Number(colorID);
+ if (!Number.isFinite(parsedColorID)) {
+ return;
+ }
+ if (shouldHide) {
+ this.shouldFilterColor.set(parsedColorID, true);
+ } else {
+ this.shouldFilterColor.delete(parsedColorID);
+ }
+ __privateMethod(this, _TemplateManager_instances, persistFilteredColors_fn).call(this);
}
/** Creates the JSON object to store templates in
* @returns {{ whoami: string, scriptVersion: string, schemaVersion: string, templates: Object }} The JSON object
@@ -3366,11 +3779,11 @@ There are ${pixelsCorrectTotal} correct pixels.`);
/** Imports the JSON object, and appends it to any JSON object already loaded
* @param {string} json - The JSON string to parse
*/
- importJSON(json) {
+ async importJSON(json) {
console.log(`Importing JSON...`);
console.log(json);
if (json?.whoami == "BlueMarble") {
- __privateMethod(this, _TemplateManager_instances, parseBlueMarble_fn).call(this, json);
+ await __privateMethod(this, _TemplateManager_instances, parseBlueMarble_fn).call(this, json);
}
}
/** Sets the `templatesShouldBeDrawn` boolean to a value.
@@ -3382,6 +3795,31 @@ There are ${pixelsCorrectTotal} correct pixels.`);
}
};
_TemplateManager_instances = new WeakSet();
+ /** Restores hidden colors from persisted user settings.
+ * @since 0.92.1
+ */
+ restoreFilteredColorsFromSettings_fn = function() {
+ const storedFilter = this.settingsManager?.userSettings?.filter;
+ const filteredColors = Array.isArray(storedFilter) ? storedFilter : [];
+ this.shouldFilterColor.clear();
+ for (const colorID of filteredColors) {
+ const parsedColorID = Number(colorID);
+ if (!Number.isFinite(parsedColorID)) {
+ continue;
+ }
+ this.shouldFilterColor.set(parsedColorID, true);
+ }
+ };
+ /** Persists hidden colors to user settings storage.
+ * @since 0.92.1
+ */
+ persistFilteredColors_fn = function() {
+ if (!this.settingsManager) {
+ return;
+ }
+ this.settingsManager.userSettings.filter = Array.from(this.shouldFilterColor.keys()).map((colorID) => Number(colorID)).filter((colorID) => Number.isFinite(colorID)).sort((a, b) => a - b);
+ void this.settingsManager.saveUserStorageNow();
+ };
/** Generates a {@link Template} class instance from the JSON object template.
* {@link createTemplate()} will create a class instance and save to template storage.
* `#loadTemplate()` will create a class instance without saving to the template storage.
@@ -3622,29 +4060,7 @@ Use Blue Marble version ${scriptVersion} or load a new template.`);
console.log(`%cBlue Marble%c: Recieved message about "%s"`, "color: cornflowerblue;", "", endpointText);
switch (endpointText) {
case "me":
- if (dataJSON["status"] && dataJSON["status"]?.toString()[0] != "2") {
- overlay.handleDisplayError(`You are not logged in or Wplace is offline!
-Could not fetch userdata.`);
- return;
- }
- const nextLevelPixels = Math.ceil(Math.pow(Math.floor(dataJSON["level"]) * Math.pow(30, 0.65), 1 / 0.65) - dataJSON["pixelsPainted"]);
- console.log(dataJSON["id"]);
- if (!!dataJSON["id"] || dataJSON["id"] === 0) {
- console.log(numberToEncoded(
- dataJSON["id"],
- "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
- ));
- }
- this.templateManager.userID = dataJSON["id"];
- if (this.chargeRefillTimerID.length != 0) {
- const chargeRefillTimer = document.querySelector("#" + this.chargeRefillTimerID);
- if (chargeRefillTimer) {
- const chargeData = dataJSON["charges"];
- chargeRefillTimer.dataset["endDate"] = Date.now() + (chargeData["max"] - chargeData["count"]) * chargeData["cooldownMs"];
- }
- }
- overlay.updateInnerHTML("bm-user-droplets", `Droplets: ${localizeNumber(dataJSON["droplets"])} `);
- overlay.updateInnerHTML("bm-user-nextlevel", `Next level in ${localizeNumber(nextLevelPixels)} pixel${nextLevelPixels == 1 ? "" : "s"}`);
+ this.applyUserDataToOverlay(overlay, dataJSON);
break;
case "pixel":
const coordsTile = data["endpoint"].split("?")[0].split("/").filter((s) => s && !isNaN(Number(s)));
@@ -3708,6 +4124,76 @@ Did you try clicking the canvas first?`);
}
});
}
+ /** Applies user data from the /me endpoint to the current overlay.
+ * @param {Overlay} overlay
+ * @param {Object.} dataJSON
+ * @since 0.92.1
+ */
+ applyUserDataToOverlay(overlay, dataJSON) {
+ if (dataJSON["status"] && dataJSON["status"]?.toString()[0] != "2") {
+ overlay.handleDisplayError(`You are not logged in or Wplace is offline!
+Could not fetch userdata.`);
+ return;
+ }
+ const nextLevelPixels = Math.ceil(Math.pow(Math.floor(dataJSON["level"]) * Math.pow(30, 0.65), 1 / 0.65) - dataJSON["pixelsPainted"]);
+ console.log(dataJSON["id"]);
+ if (!!dataJSON["id"] || dataJSON["id"] === 0) {
+ console.log(numberToEncoded(
+ dataJSON["id"],
+ "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ ));
+ }
+ this.templateManager.userID = dataJSON["id"];
+ if (this.chargeRefillTimerID.length != 0) {
+ const chargeRefillTimer = document.querySelector("#" + this.chargeRefillTimerID);
+ if (chargeRefillTimer) {
+ const chargeData = dataJSON["charges"];
+ chargeRefillTimer.dataset["endDate"] = Date.now() + (chargeData["max"] - chargeData["count"]) * chargeData["cooldownMs"];
+ }
+ }
+ overlay.updateInnerHTML("bm-user-droplets", `${localizeNumber(dataJSON["droplets"])} `);
+ overlay.updateInnerHTML("bm-user-nextlevel", `${localizeNumber(nextLevelPixels)} px`);
+ }
+ /** Requests the current /me payload directly so the overlay has initial user data
+ * even if the first network response was missed during startup.
+ * @param {Overlay} overlay
+ * @since 0.92.1
+ */
+ async requestCurrentUserData(overlay) {
+ try {
+ const response = await fetch(`${window.location.origin}/api/me`, {
+ credentials: "include"
+ });
+ if (!response.ok) {
+ overlay.handleDisplayError(`Could not fetch userdata.
+HTTP ${response.status}`);
+ return;
+ }
+ const dataJSON = await response.json();
+ this.applyUserDataToOverlay(overlay, dataJSON);
+ } catch (error) {
+ consoleError("Failed to fetch current user data:", error);
+ }
+ }
+ /** Applies cached /me data from sessionStorage if it was captured during early startup.
+ * @param {Overlay} overlay
+ * @returns {boolean}
+ * @since 0.92.1
+ */
+ applyCachedUserData(overlay) {
+ try {
+ const cached = sessionStorage.getItem("bm-last-me");
+ if (!cached) {
+ return false;
+ }
+ const dataJSON = JSON.parse(cached);
+ this.applyUserDataToOverlay(overlay, dataJSON);
+ return true;
+ } catch (error) {
+ consoleError("Failed to apply cached user data:", error);
+ return false;
+ }
+ }
// Sends a heartbeat to the telemetry server
async sendHeartbeat(version2) {
console.log("Sending heartbeat to telemetry server...");
@@ -3888,6 +4374,14 @@ Did you try clicking the canvas first?`);
if (contentType.includes("application/json")) {
console.log(`%c${name2}%c: Sending JSON message about endpoint "${endpointName}"`, consoleStyle2, "");
cloned.json().then((jsonData) => {
+ const endpointText = endpointName?.split("?")[0].split("/").filter((s) => s && isNaN(Number(s))).filter((s) => s && !s.includes(".")).pop();
+ if (endpointText == "me") {
+ try {
+ sessionStorage.setItem("bm-last-me", JSON.stringify(jsonData));
+ } catch (error) {
+ console.warn(`%c${name2}%c: Failed to cache "/me" payload`, consoleStyle2, "", error);
+ }
+ }
window.postMessage({
source: "blue-marble",
endpoint: endpointName,
@@ -3934,13 +4428,9 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
});
var cssOverlay = GM_getResourceText("CSS-BM-File");
GM_addStyle(cssOverlay);
- var robotoMonoInjectionPoint = "robotoMonoInjectionPoint";
- if (!!(robotoMonoInjectionPoint.indexOf("@font-face") + 1)) {
- console.log(`Loading Roboto Mono as a file...`);
- GM_addStyle(robotoMonoInjectionPoint);
- } else {
- stylesheetLink = document.createElement("link");
- stylesheetLink.href = "https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap";
+ function appendFontStylesheet(href) {
+ const stylesheetLink = document.createElement("link");
+ stylesheetLink.href = href;
stylesheetLink.rel = "preload";
stylesheetLink.as = "style";
stylesheetLink.onload = function() {
@@ -3949,7 +4439,14 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
};
document.head?.appendChild(stylesheetLink);
}
- var stylesheetLink;
+ var robotoMonoInjectionPoint = "robotoMonoInjectionPoint";
+ appendFontStylesheet("https://fonts.googleapis.com/css2?family=Michroma&family=Rajdhani:wght@400;500;600;700&display=swap");
+ if (!!(robotoMonoInjectionPoint.indexOf("@font-face") + 1)) {
+ console.log(`Loading Roboto Mono as a file...`);
+ GM_addStyle(robotoMonoInjectionPoint);
+ } else {
+ appendFontStylesheet("https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap");
+ }
var userSettings = JSON.parse(GM_getValue("bmUserSettings", "{}"));
var observers = new Observers();
var windowMain = new WindowMain(name, version);
@@ -3962,7 +4459,6 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
templateManager.setSettingsManager(settingsManager);
var storageTemplates = JSON.parse(GM_getValue("bmTemplates", "{}"));
console.log(storageTemplates);
- templateManager.importJSON(storageTemplates);
console.log(userSettings);
console.log(Object.keys(userSettings).length);
if (Object.keys(userSettings).length == 0) {
@@ -3981,10 +4477,17 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
windowTelemetry.setApiManager(apiManager);
windowTelemetry.buildWindow();
}
- windowMain.buildWindow();
- apiManager.spontaneousResponseListener(windowMain);
- observeBlack();
- consoleLog(`%c${name}%c (${version}) userscript has loaded!`, "color: cornflowerblue;", "");
+ void initializeBlueMarble();
+ async function initializeBlueMarble() {
+ await templateManager.importJSON(storageTemplates);
+ apiManager.spontaneousResponseListener(windowMain);
+ windowMain.buildWindow();
+ windowMain.buildWindowFilter();
+ apiManager.applyCachedUserData(windowMain);
+ void apiManager.requestCurrentUserData(windowMain);
+ observeBlack();
+ consoleLog(`%c${name}%c (${version}) userscript has loaded!`, "color: cornflowerblue;", "");
+ }
function observeBlack() {
const observer = new MutationObserver((mutations, observer2) => {
const black = document.querySelector("#color-1");
@@ -4014,3 +4517,5 @@ Time Since Blink: ${String(Math.floor(elapsed / 6e4)).padStart(2, "0")}:${String
observer.observe(document.body, { childList: true, subtree: true });
}
})();
+
+// Build Hash: f31122a513fd
diff --git a/dist/BlueMarble-Standalone.user.js b/dist/BlueMarble-Standalone.user.js
index 8401e77..5ba7c72 100644
--- a/dist/BlueMarble-Standalone.user.js
+++ b/dist/BlueMarble-Standalone.user.js
@@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.94.0
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
@@ -38,4 +38,6 @@
The "Blue Marble" image is owned by NASA.
*/
-(()=>{var t=t=>{throw TypeError(t)},e=(e,i,n)=>i.has(e)?t("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(e):i.set(e,n),i=(e,i,n)=>(((e,i)=>{i.has(e)||t("Cannot access private method")})(e,i),n);function n(t){return new Promise(e=>setTimeout(e,t))}function s(t){return(new Intl.NumberFormat).format(t)}function o(t){return new Intl.NumberFormat(void 0,{style:"percent",t:2,i:2}).format(t)}function a(t){return t.toLocaleString(void 0,{o:"long",l:"numeric",h:"2-digit",m:"2-digit",u:"2-digit"})}function r(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function l(...t){(0,console.log)(...t)}function h(...t){(0,console.error)(...t)}function c(...t){(0,console.warn)(...t)}function m(t,e){if(0===t)return e[0];let i="";const n=e.length;for(;t>0;)i=e[t%n]+i,t=Math.floor(t/n);return i}function d(t,e){let i=0;const n=e.length;for(const s of t){const t=e.indexOf(s);-1==t&&h(`Invalid character '${s}' encountered whilst decoding! Is the decode alphabet/base incorrect?`),i=i*n+t}return i}function u(t){let e="";for(let i=0;i(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*e[0]+.7152*e[1]+.0722*e[2]}function f(t,e,i){return Array.isArray(t)&&([t,e,i]=t),(1<<24|t<<16|e<<8|i).toString(16).slice(1)}var g,w,x,y,v,C=[{id:0,premium:!1,name:"Transparent",rgb:[0,0,0]},{id:1,premium:!1,name:"Black",rgb:[0,0,0]},{id:2,premium:!1,name:"Dark Gray",rgb:[60,60,60]},{id:3,premium:!1,name:"Gray",rgb:[120,120,120]},{id:4,premium:!1,name:"Light Gray",rgb:[210,210,210]},{id:5,premium:!1,name:"White",rgb:[255,255,255]},{id:6,premium:!1,name:"Deep Red",rgb:[96,0,24]},{id:7,premium:!1,name:"Red",rgb:[237,28,36]},{id:8,premium:!1,name:"Orange",rgb:[255,127,39]},{id:9,premium:!1,name:"Gold",rgb:[246,170,9]},{id:10,premium:!1,name:"Yellow",rgb:[249,221,59]},{id:11,premium:!1,name:"Light Yellow",rgb:[255,250,188]},{id:12,premium:!1,name:"Dark Green",rgb:[14,185,104]},{id:13,premium:!1,name:"Green",rgb:[19,230,123]},{id:14,premium:!1,name:"Light Green",rgb:[135,255,94]},{id:15,premium:!1,name:"Dark Teal",rgb:[12,129,110]},{id:16,premium:!1,name:"Teal",rgb:[16,174,166]},{id:17,premium:!1,name:"Light Teal",rgb:[19,225,190]},{id:18,premium:!1,name:"Dark Blue",rgb:[40,80,158]},{id:19,premium:!1,name:"Blue",rgb:[64,147,228]},{id:20,premium:!1,name:"Cyan",rgb:[96,247,242]},{id:21,premium:!1,name:"Indigo",rgb:[107,80,246]},{id:22,premium:!1,name:"Light Indigo",rgb:[153,177,251]},{id:23,premium:!1,name:"Dark Purple",rgb:[120,12,153]},{id:24,premium:!1,name:"Purple",rgb:[170,56,185]},{id:25,premium:!1,name:"Light Purple",rgb:[224,159,249]},{id:26,premium:!1,name:"Dark Pink",rgb:[203,0,122]},{id:27,premium:!1,name:"Pink",rgb:[236,31,128]},{id:28,premium:!1,name:"Light Pink",rgb:[243,141,169]},{id:29,premium:!1,name:"Dark Brown",rgb:[104,70,52]},{id:30,premium:!1,name:"Brown",rgb:[149,104,42]},{id:31,premium:!1,name:"Beige",rgb:[248,178,119]},{id:32,premium:!0,name:"Medium Gray",rgb:[170,170,170]},{id:33,premium:!0,name:"Dark Red",rgb:[165,14,30]},{id:34,premium:!0,name:"Light Red",rgb:[250,128,114]},{id:35,premium:!0,name:"Dark Orange",rgb:[228,92,26]},{id:36,premium:!0,name:"Light Tan",rgb:[214,181,148]},{id:37,premium:!0,name:"Dark Goldenrod",rgb:[156,132,49]},{id:38,premium:!0,name:"Goldenrod",rgb:[197,173,49]},{id:39,premium:!0,name:"Light Goldenrod",rgb:[232,212,95]},{id:40,premium:!0,name:"Dark Olive",rgb:[74,107,58]},{id:41,premium:!0,name:"Olive",rgb:[90,148,74]},{id:42,premium:!0,name:"Light Olive",rgb:[132,197,115]},{id:43,premium:!0,name:"Dark Cyan",rgb:[15,121,159]},{id:44,premium:!0,name:"Light Cyan",rgb:[187,250,242]},{id:45,premium:!0,name:"Light Blue",rgb:[125,199,255]},{id:46,premium:!0,name:"Dark Indigo",rgb:[77,49,184]},{id:47,premium:!0,name:"Dark Slate Blue",rgb:[74,66,132]},{id:48,premium:!0,name:"Slate Blue",rgb:[122,113,196]},{id:49,premium:!0,name:"Light Slate Blue",rgb:[181,174,241]},{id:50,premium:!0,name:"Light Brown",rgb:[219,164,99]},{id:51,premium:!0,name:"Dark Beige",rgb:[209,128,81]},{id:52,premium:!0,name:"Light Beige",rgb:[255,197,165]},{id:53,premium:!0,name:"Dark Peach",rgb:[155,82,73]},{id:54,premium:!0,name:"Peach",rgb:[209,128,120]},{id:55,premium:!0,name:"Light Peach",rgb:[250,182,164]},{id:56,premium:!0,name:"Dark Tan",rgb:[123,99,82]},{id:57,premium:!0,name:"Tan",rgb:[156,132,107]},{id:58,premium:!0,name:"Dark Slate",rgb:[51,57,65]},{id:59,premium:!0,name:"Slate",rgb:[109,117,141]},{id:60,premium:!0,name:"Light Slate",rgb:[179,185,209]},{id:61,premium:!0,name:"Dark Stone",rgb:[109,100,63]},{id:62,premium:!0,name:"Stone",rgb:[148,140,107]},{id:63,premium:!0,name:"Light Stone",rgb:[205,197,158]}],M=class{constructor(t,i){e(this,g),this.name=t,this.version=i,this.p=null,this.v=null,this.C="bm-r",this.M=null,this.T=null,this.$=[]}S(t){this.p=t}k(t){this.v=t}D(){return this.$.length>0&&(this.T=this.$.pop()),this}L(t){t?.appendChild(this.M),this.M=null,this.T=null,this.$=[]}H(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{},t)),this}N(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"p",{},t)),this}O(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"small",{},t)),this}B(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"span",{},t)),this}I(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"details",{},t)),this}P(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"summary",{},t)),this}A(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"img",{},t)),this}W(t,e={},n=()=>{}){return n(this,i(this,g,w).call(this,"h"+t,{},e)),this}V(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"hr",{},t)),this}_(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"br",{},t)),this}F(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"form",{},t)),this}U(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"fieldset",{},t)),this}G(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"legend",{},t)),this}R(t={},e=()=>{}){const n={};t.textContent?(n.textContent=t.textContent,delete t.textContent):t.innerHTML&&(n.innerHTML=t.innerHTML,delete t.textContent);const s=i(this,g,w).call(this,"label",n),o=i(this,g,w).call(this,"input",{type:"checkbox"},t);return s.insertBefore(o,s.firstChild),this.D(),e(this,s,o),this}j(t={},e=()=>{}){const n=i(this,g,w).call(this,"label",{textContent:t.textContent??"",for:t.id??""});return delete t.textContent,this.D(),e(this,n,i(this,g,w).call(this,"select",{},t)),this}Y(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"option",{},t)),this}X(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ol",{},t)),this}J(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ul",{},t)),this}q(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"menu",{},t)),this}Z(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"li",{},t)),this}K(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"table",{},t)),this}tt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"caption",{},t)),this}et(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"thead",{},t)),this}it(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tbody",{},t)),this}nt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tfoot",{},t)),this}st(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tr",{},t)),this}ot(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"th",{},t)),this}rt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"td",{},t)),this}lt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"button",{},t)),this}ht(t={},e=()=>{}){const n=t.title??t.textContent??"Help: No info";delete t.textContent,t.title=`Help: ${n}`;const s={textContent:"?",className:"bm-10",onclick:()=>{this.ct(this.C,n)}};return e(this,i(this,g,w).call(this,"button",s,t)),this}dt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"input",{},t)),this}ut(t={},e=()=>{}){const n=t.textContent??"";delete t.textContent;const s=i(this,g,w).call(this,"div"),o=i(this,g,w).call(this,"input",{type:"file",tabindex:"-1","aria-hidden":"true"},t);this.D();const a=i(this,g,w).call(this,"button",{textContent:n});return this.D(),this.D(),a.addEventListener("click",()=>{o.click()}),o.addEventListener("change",()=>{a.style.maxWidth=`${a.offsetWidth}px`,o.files.length>0?a.textContent=o.files[0].name:a.textContent=n}),e(this,s,o,a),this}bt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"textarea",{},t)),this}ft(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{class:"bm-S"},t)),this}gt(t=Date.now(),e=500,n={},s=()=>{}){const o="bm--",a=n?.id||o+"-"+crypto.randomUUID().slice(0,8),r={class:o},l=i(this,g,w).call(this,"time",r,n);return l.id=a,l.dataset.endDate=t,setInterval(()=>{if(!l.isConnected)return;const t=Math.max(l.dataset.endDate-Date.now(),0),e=Math.floor(t/1e3),i=Math.floor(e/3600),n=Math.floor(e%60),s=Math.floor(e%3600/60);l.setAttribute("datetime",`PT${i}H${s}M${n}S`),l.textContent=String(i).padStart(2,"0")+":"+String(s).padStart(2,"0")+":"+String(n).padStart(2,"0")},e),s(this,l),this}ct(t,e,i=!1){const n=document.getElementById(t.replace(/^#/,""));n&&(n instanceof HTMLInputElement?n.value=e:i?n.textContent=e:n.innerHTML=e)}wt(t){if(t.disabled)return;t.disabled=!0,t.style.textDecoration="none";const e=t.closest(".bm-W"),i=t.closest(".bm-S"),n=e.querySelector("h1"),s=e.querySelector(".bm-m");if(e.parentElement.append(e),"expanded"==t.dataset.buttonStatus){s.style.height=s.scrollHeight+"px",e.style.width=e.scrollWidth+"px",s.style.height="0",s.addEventListener("transitionend",function e(){s.style.display="none",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)});const i=n.cloneNode(!0),o=i.textContent;t.nextElementSibling.appendChild(i),t.textContent="▶",t.dataset.buttonStatus="collapsed",t.ariaLabel=`Unminimize window "${o}"`}else{const n=i.querySelector("h1"),o=n.textContent;n.remove(),s.style.display="",s.style.height="0",e.style.width="",s.style.height=s.scrollHeight+"px",s.addEventListener("transitionend",function e(){s.style.height="",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)}),t.textContent="▼",t.dataset.buttonStatus="expanded",t.ariaLabel=`Minimize window "${o}"`}}xt(t,e){const i=document.querySelector(t),n=document.querySelector(e);if(!i||!n)return void this.yt(`Can not drag! ${i?"":"moveMe"} ${i||n?"":"and "}${n?"":"iMoveThings "}was not found!`);let s,o=!1,a=0,r=null,l=0,h=0,c=0,m=0,d=null;const u=()=>{if(o){const t=Math.abs(l-c),e=Math.abs(h-m);(t>.5||e>.5)&&(l=c,h=m,i.style.transform=`translate(${l}px, ${h}px)`,i.style.left="0px",i.style.top="0px",i.style.right=""),r=requestAnimationFrame(u)}},b=(t,e)=>{o=!0,d=i.getBoundingClientRect(),s=t-d.left,a=e-d.top;const b=window.getComputedStyle(i).transform;if(b&&"none"!==b){const t=new DOMMatrix(b);l=t.m41,h=t.m42}else l=d.left,h=d.top;c=l,m=h,document.body.style.userSelect="none",n.classList.add("bm-M"),document.addEventListener("mousemove",f),document.addEventListener("touchmove",g,{passive:!1}),document.addEventListener("mouseup",p),document.addEventListener("touchend",p),document.addEventListener("touchcancel",p),r&&cancelAnimationFrame(r),u()},p=()=>{o=!1,r&&(cancelAnimationFrame(r),r=null),document.body.style.userSelect="",n.classList.remove("bm-M"),document.removeEventListener("mousemove",f),document.removeEventListener("touchmove",g),document.removeEventListener("mouseup",p),document.removeEventListener("touchend",p),document.removeEventListener("touchcancel",p)},f=t=>{o&&d&&(c=t.clientX-s,m=t.clientY-a)},g=t=>{if(o&&d){const e=t.touches[0];if(!e)return;c=e.clientX-s,m=e.clientY-a,t.preventDefault()}};n.addEventListener("mousedown",function(t){t.preventDefault(),b(t.clientX,t.clientY)}),n.addEventListener("touchstart",function(t){const e=t?.touches?.[0];e&&(b(e.clientX,e.clientY),t.preventDefault())},{passive:!1})}vt(t){(0,console.info)(`${this.name}: ${t}`),this.ct(this.C,"Status: "+t,!0)}yt(t){(0,console.error)(`${this.name}: ${t}`),this.ct(this.C,"Error: "+t,!0)}};g=new WeakSet,w=function(t,e={},n={}){const s=document.createElement(t);this.M?(this.T?.appendChild(s),this.$.push(this.T),this.T=s):(this.M=s,this.T=s);for(const[t,n]of Object.entries(e))i(this,g,x).call(this,s,t,n);for(const[t,e]of Object.entries(n))i(this,g,x).call(this,s,t,e);return s},x=function(t,e,i){"class"==e?t.classList.add(...i.split(/\s+/)):"for"==e?t.htmlFor=i:"tabindex"==e?t.tabIndex=Number(i):"readonly"==e?t.readOnly="true"==i||"1"==i:"maxlength"==e?t.maxLength=Number(i):e.startsWith("data")?t.dataset[e.slice(5).split("-").map((t,e)=>0==e?t:t[0].toUpperCase()+t.slice(1)).join("")]=i:e.startsWith("aria")?t.setAttribute(e,i):t[e]=i};var T,$,S,k,D,L=class extends M{constructor(t,i){super(t,i),e(this,y),this.window=null,this.Ct="bm-l",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Settings"}).D().D().V().D().N({textContent:"Settings take 5 seconds to save."}).D().H({class:"bm-L bm-H"},(t,e)=>{this.$t(),this.St()}).D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}$t(){i(this,y,v).call(this,"Pixel Highlight")}St(){i(this,y,v).call(this,"Template")}};y=new WeakSet,v=function(t){this.window=this.H({class:"bm-L"}).W(2,{textContent:t}).D().V().D().N({innerHTML:`An error occured loading the ${t} category. SettingsManager failed to override the ${t} function inside WindowSettings.`}).D().D()},T=new WeakSet,$=function(t,e){t.disabled=!0;const i=t.dataset.status,n=this.kt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];let s=[2,0,0];const o=n;switch(i){case"Disabled":t.dataset.status="Incorrect",t.ariaLabel="Sub-pixel incorrect",s=[1,...e];break;case"Incorrect":t.dataset.status="Template",t.ariaLabel="Sub-pixel template",s=[2,...e];break;case"Template":t.dataset.status="Disabled",t.ariaLabel="Sub-pixel disabled",s=[0,...e]}const a=n.findIndex(([,t,e])=>t==s[1]&&e==s[2]);0!=s[0]?-1!=a?o[a]=s:o.push(s):-1!=a&&o.splice(a,1),this.kt.highlight=o,t.disabled=!1},S=async function(t){const e=document.querySelectorAll(".bm-3 button");for(const t of e)t.disabled=!0;let i=[0,0,0,0,2,0,0,0,0];switch(t){case"Cross":i=[0,1,0,1,2,1,0,1,0];break;case"X":i=[1,0,1,0,2,0,1,0,1];break;case"Full":i=[2,2,2,2,2,2,2,2,2]}const s=document.querySelector(".bm-n")?.childNodes??[];for(let t=0;t{const[n,s,o,a]=e.split(",").map(Number);(s>>24==0?0:s.get(e)??-2;const a=o.get(n);o.set(n,a?a+1:1)}return o};var N=class{constructor(){this.Et=Math.ceil(80/1300*window.innerWidth),this.Yt=C.slice(1)}Xt(t){const e=document.createElement("div");for(let t=0;t{t.parentNode.childElementCount<=1?t.parentNode.remove():t.remove()},e.appendChild(t)}t.appendChild(e)}},O=class extends HTMLElement{};customElements.define("confetti-piece",O);var B,I,P,A,W,V,_,z,F,U=class extends M{constructor(t,e){super(t,e),this.window=null,this.Ct="bm-o",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Credits"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Credits"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Credits"}).D().D().V().D().H({class:"bm-L bm-H"}).B({role:"img","aria-label":this.name}).B({innerHTML:"\n██████╗ ██╗ ██╗ ██╗███████╗\n██╔══██╗██║ ██║ ██║██╔════╝\n██████╔╝██║ ██║ ██║█████╗ \n██╔══██╗██║ ██║ ██║██╔══╝ \n██████╔╝███████╗╚██████╔╝███████╗\n╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝\n\n███╗ ███╗ █████╗ ██████╗ ██████╗ ██╗ ███████╗\n████╗ ████║██╔══██╗██╔══██╗██╔══██╗██║ ██╔════╝\n██╔████╔██║███████║██████╔╝██████╔╝██║ █████╗ \n██║╚██╔╝██║██╔══██║██╔══██╗██╔══██╗██║ ██╔══╝ \n██║ ╚═╝ ██║██║ ██║██║ ██║██████╔╝███████╗███████╗\n╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝\n",class:"bm-_","aria-hidden":"true"}).D().D()._().D().V().D()._().D().B({textContent:'"Blue Marble" userscript is made by SwingTheVine.'}).D()._().D().B({innerHTML:'The Blue Marble Website is made by crqch .'}).D()._().D().B({textContent:`The Blue Marble Website used until ${a(new Date(175606932e4))} was made by Camille Daguin.`}).D()._().D().B({textContent:'The favicon "Blue Marble" is owned by NASA. (The image of the Earth is owned by NASA)'}).D()._().D().B({textContent:"Special Thanks:"}).D().J().Z({textContent:"Espresso, Meqa, and Robot for moderating SwingTheVine's community."}).D().Z({innerHTML:'nof, darkness for creating similar userscripts!'}).D().Z({innerHTML:'Wonda for the Blue Marble banner image!'}).D().Z({innerHTML:'BullStein , allanf181 for being early beta testers!'}).D().Z({innerHTML:'guidu_ and Nick-machado for the original "Minimize" Button code!'}).D().Z({innerHTML:'Nomad and Gustav for the tutorials!'}).D().Z({innerHTML:'cfp for creating the template overlay that Blue Marble was based on!'}).D().Z({innerHTML:'Force Network for hosting the telemetry server !'}).D().Z({innerHTML:'TheBlueCorner for getting me interested in online pixel canvases!'}).D().D()._().D().B({innerHTML:'Donators :'}).D().J().Z({textContent:"Soultree"}).D().Z({textContent:"Espresso"}).D().Z({textContent:"BEST FAN"}).D().Z({textContent:"FuchsDresden"}).D().Z({textContent:"Jack"}).D().Z({textContent:"raiken_au"}).D().Z({textContent:"Jacob"}).D().Z({textContent:"StupidOne"}).D().Z({textContent:"2 Anonymous Supporters"}).D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}},G=class extends M{constructor(t){super(t.name,t.version),e(this,B),this.window=null,this.Ct="bm-t",this.Jt="bm-E",this.Mt=document.body,this.qt=t.p?.qt,this.Zt=' ',this.Qt=' ';const{palette:i,jt:n}=this.qt.Kt;this.palette=i,this.te=0,this.ee=0,this.ie=new Map,this.ne=new Map,this.se=0,this.oe=0,this.timeRemaining=0,this.ae="",this.sortPrimary="id",this.sortSecondary="ascending",this.showUnused=!1}Tt(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"🗗","aria-label":'Switch to windowed mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove(),this.re()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Color Filter"}).D().D().V().D().H({class:"bm-L bm-x bm-h",style:"gap: 1.5ch;"}).lt({textContent:"Hide All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!1)}).D().lt({textContent:"Refresh Data"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.le(),e.disabled=!1}}).D().lt({textContent:"Show All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!0)}).D().D().H({class:"bm-L bm-H"}).H({class:"bm-L",style:"margin-left: 2.5ch; margin-right: 2.5ch;"}).H({class:"bm-L"}).B({id:"bm-i",innerHTML:"Tiles Loaded: 0 / ???"}).D()._().D().B({id:"bm-d",innerHTML:"Correct Pixels: ???"}).D()._().D().B({id:"bm-j",innerHTML:"Total Pixels: ???"}).D()._().D().B({id:"bm-7",innerHTML:"Complete: ??? (???)"}).D()._().D().B({id:"bm-8",innerHTML:"??? ???"}).D().D().H({class:"bm-L"}).N({innerHTML:`Press the 🗗 button to make this window smaller. Colors with the icon ${this.Zt.replace("{e.onclick=t=>{t.preventDefault();const e=new FormData(document.querySelector(`#${this.Ct} form`)),n={};for(const[t,i]of e)n[t]=i;i(this,B,P).call(this,n.sortPrimary,n.sortSecondary,"on"==n.showUnused)}}).D().D().D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,I).call(this,t),i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),this.ct("#bm-i",`Tiles Loaded: ${s(this.te)} / ${s(this.ee)}`),this.ct("#bm-d",`Correct Pixels: ${s(this.se)}`),this.ct("#bm-j",`Total Pixels: ${s(this.oe)}`),this.ct("#bm-7",`Remaining: ${s((this.oe||0)-(this.se||0))} (${o(((this.oe||0)-(this.se||0))/(this.oe||1))})`),this.ct("#bm-8",`Completed at: ${this.ae} `)}re(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();this.window=this.H({id:this.Ct,class:"bm-W bm-N"}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>{const i=document.querySelector("#bm-2");i&&(i.style.display="expanded"==e.dataset.buttonStatus?"none":""),t.wt(e)},e.ontouchend=()=>{e.click()}}).D().H().B({id:"bm-2",class:"bm-y",style:"font-weight: 700;"}).D().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"🗖","aria-label":'Switch to fullscreen mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove(),this.Tt()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Color Filter"}).D().D().V().D().H({class:"bm-L bm-x bm-h",style:"gap: 1.5ch;"}).lt({textContent:"None"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!1)}).D().lt({textContent:"Refresh"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.le(),e.disabled=!1}}).D().lt({textContent:"All"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!0)}).D().D().H({class:"bm-L bm-H"}).D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,I).call(this,t),i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}le(){i(this,B,W).call(this);const t=document.querySelector(`#${this.Jt}`),e={};for(const t of this.palette){const i=this.ie.get(t.id)??0,n=s(i);let a=0,r="0",l=o(1);0!=i&&(a=this.ne.get(t.id)??"???","number"!=typeof a&&this.te==this.ee&&t.id&&(a=0),r="string"==typeof a?a:s(a),l=isNaN(a/i)?"???":o(a/i));const h=parseInt(i)-parseInt(a);e[t.id]={he:i,ce:n,me:a,de:r,ue:l,be:h}}if(document.querySelector("#bm-2")){const t=this.se.toString().length>7?this.se.toString().slice(0,2)+"…"+this.se.toString().slice(-3):this.se.toString(),e=this.oe.toString().length>7?this.oe.toString().slice(0,2)+"…"+this.oe.toString().slice(-3):this.oe.toString();this.ct("#bm-2",`${t}/${e}`,!0)}if(!t)return e;const n=Array.from(t.children);for(const t of n){const i=parseInt(t.dataset.id),{me:n,de:s,ue:o,he:a,ce:r,be:l}=e[i];t.dataset.correct=Number.isNaN(parseInt(n))?"0":n,t.dataset.total=a,t.dataset.percent="%"==o.slice(-1)?o.slice(0,-1):"0",t.dataset.incorrect=l||0;const h=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-9`);h&&(h.textContent=`${s} / ${r}`);const c=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-6`);c&&(c.textContent=`${"number"!=typeof l||isNaN(l)?"???":l} incorrect pixel${1==l?"":"s"}. Completed: ${o}`)}i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}};B=new WeakSet,I=function(t){const e=t.closest(`#${this.Ct}`)?.classList.contains("bm-N"),i=new M(this.name,this.version);i.H({id:this.Jt});const n=this.le();for(const t of this.palette){const s="#"+f(t.rgb).toUpperCase(),o=p(t.rgb);let a=1.05/(o+.05)>(o+.05)/.05?"white":"black";t.id||(a="transparent");const r="white"==a?"bm-f":"bm-g",{me:l,de:h,ue:c,he:m,ce:d,be:u}=n[t.id],b=!!this.qt.pe.get(t.id);if(e){const e=`background-size: auto 100%; background-repeat: repeat-x; background-image: url("data:image/svg+xml;utf8, ");`;i.H({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==c.slice(-1)?c.slice(0,-1):"0","data-incorrect":u||0}).H({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});${t.premium?e:""}`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.Qt.replace("{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.Qt.replace("Number(t)||0).join(",")});`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.Qt.replace("{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.Qt.replace("{const o=n.getAttribute("data-"+t),a=s.getAttribute("data-"+t),r=parseFloat(o),l=parseFloat(a),h=!isNaN(r),c=!isNaN(l);if(i?n.classList.remove("bm-I"):Number(n.getAttribute("data-total"))||n.classList.add("bm-I"),h&&c)return"ascending"===e?r-l:l-r;{const t=o.toLowerCase(),i=a.toLowerCase();return ti?"ascending"===e?1:-1:0}}),s.forEach(t=>n.appendChild(t))},A=function(t){const e=document.querySelector(`#${this.Jt}`),i=Array.from(e.children);for(const e of i){if(e.classList?.contains("bm-I"))continue;const i=e.querySelector(".bm-a button");("hidden"!=i.dataset.state||t)&&("shown"==i.dataset.state&&t||i.click())}},W=function(){this.oe=0,this.se=0,this.ne=new Map,this.ie=new Map;for(const t of this.qt.fe){const e=t.Bt?.total??0;this.oe+=e??0;const i=t.Bt?.colors??new Map;for(const[t,e]of i){const i=Number(e)||0,n=this.ie.get(t)??0;this.ie.set(t,n+i)}const n=t.Bt?.correct??{};this.te+=Object.keys(n).length,this.ee+=Object.keys(t.Ht).length;for(const t of Object.values(n))for(const[e,i]of t){const t=Number(i)||0;this.se+=t;const n=this.ne.get(e)??0;this.ne.set(e,n+t)}}this.se>=this.oe&&this.oe&&this.te==this.ee&&(new N).Xt(document.querySelector(`#${this.Ct}`)),this.timeRemaining=new Date(30*(this.oe-this.se)*1e3+Date.now()),this.ae=a(this.timeRemaining)};var R=class extends M{constructor(t,i,n,s=void 0){super(t,i),e(this,V),this.window=null,this.Ct="bm-u",this.Mt=document.body,this.ge=JSON.parse(GM_getValue("bmTemplates","{}")),this.scriptVersion=this.ge?.scriptVersion,this.schemaVersion=this.ge?.schemaVersion,this.we=void 0,this.xe=n,this.qt=s}Tt(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();let t="";document.querySelector("#bm-F")||(t=t.concat("z-index: 9001;").trim()),this.window=this.H({id:this.Ct,class:"bm-W",style:t},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Template Wizard"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Template Wizard"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Status"}).D().N({id:"bm-v",textContent:"Loading template storage status..."}).D().D().H({class:"bm-L bm-H"}).W(2,{textContent:"Detected templates:"}).D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`),i(this,V,_).call(this),i(this,V,z).call(this)}};V=new WeakSet,_=function(){const t=this.schemaVersion.split(/[-\.\+]/),e=this.xe.split(/[-\.\+]/);let n="";t[0]==e[0]?t[1]==e[1]?(n='Template storage health: Healthy! No futher action required. (Reason: Semantic version matches)',this.we="Good"):(n='Template storage health: Poor! You can still use your template, but some features may not work. It is recommended that you update Blue Marble\'s template storage. (Reason: MINOR version mismatch)',this.we="Poor"):t[0]Bad! It is guaranteed that some features are broken. You might still be able to use the template. It is HIGHLY recommended that you download all templates and update Blue Marble\'s template storage before continuing. (Reason: MAJOR version mismatch)',this.we="Bad"):(n='Template storage health: Dead! Blue Marble can not load the template storage. (Reason: MAJOR version unknown)',this.we="Dead");const s=` If you want to continue using your current templates, then make sure the template storage (schema) is up-to-date. If you don't want to update the template storage, then downgrade Blue Marble to version ${r(this.scriptVersion)} to continue using your templates. Alternatively, if you don't care about corrupting the templates listed below, you can fix any issues with the template storage by uploading a new template.`,o=function(){const t=[...document.querySelectorAll("body > div > .hidden")].filter(t=>/version:/i.test(t.textContent));if(t[0]){const e=t[0].textContent?.match(/\d+/);return e?new Date(Number(e[0])):void 0}}();let l=o?a(o):"???";this.ct("#bm-v",`${n} Your templates were created during Blue Marble version ${r(this.scriptVersion)} with schema version ${r(this.schemaVersion)} . The current Blue Marble version is ${r(this.version)} and requires schema version ${r(this.xe)} . Wplace was last updated on ${l} .${"Good"!=this.we?s:""}`);const h=new M(this.name,this.version);"Dead"!=this.we&&(h.H({class:"bm-L bm-D bm-h",style:"gap: 1.5ch;"}),h.lt({textContent:"Download all templates"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.qt.ye().then(()=>{e.disabled=!1})}}).D()),"Poor"!=this.we&&"Bad"!=this.we||h.lt({textContent:`Update template storage to ${this.xe}`},(t,e)=>{e.onclick=()=>{e.disabled=!0,i(this,V,F).call(this,!0)}}).D(),h.D().L(document.querySelector("#bm-v").parentNode)},z=function(){const t=this.ge?.templates;if(Object.keys(t).length>0){const e=document.querySelector(`#${this.Ct} .bm-H`),i=new M(this.name,this.version);i.H({id:"bm-B",class:"bm-L"});for(const e in t){const n=e,o=t[e];if(t.hasOwnProperty(e)){const t=n.split(" "),e=Number(t?.[0]),a=d(t?.[1]||"0",this.qt.ve),r=o.name||`Template ${e||""}`,l=o?.coords?.split(",").map(Number),h=o.pixels?.total??void 0,c=void 0,m="number"==typeof e?s(e):"???",u="number"==typeof a?s(a):"???",b="number"==typeof h?s(h):"???";i.H({class:"bm-L bm-D"}).H({class:"bm-D",style:"flex-direction: column; gap: 0;"}).H({class:"bm-1",textContent:c||"🖼️"}).D().O({textContent:`#${m}`}).D().D().H({class:"bm-D bm-0"}).W(3,{textContent:r}).D().B({textContent:`Uploaded by user #${u}`}).D().B({textContent:`Coordinates: ${l.join(", ")}`}).D().B({textContent:`Total Pixels: ${b}`}).D().D().D()}}i.D().L(e)}},F=async function(t){if(t){const t=document.querySelector(`#${this.Ct} .bm-m`);t.innerHTML="",new M(this.name,this.version).H({class:"bm-L"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Status"}).D().N({textContent:"Updating template storage. Please wait..."}).D().D().D().L(t)}GM_deleteValue("bmCoords");const e=this.ge?.templates;if(Object.keys(e).length>0)for(const[t,i]of Object.entries(e))if(e.hasOwnProperty(t)){const t=new H({displayName:i.name,Ht:i.tiles});t.Rt();const e=await this.qt.Ce(t);await this.qt.Me(e,t.displayName,t.coords)}t&&(document.querySelector(`#${this.Ct}`).remove(),new R(this.name,this.version,this.xe,this.qt).Tt())};var j,E,Y,X,J,q,Z,Q,K,tt=R;j=new WeakSet,E=function(){new G(this).Tt()},Y=async function(t,e,i){i.preventDefault();const n=await async function(t){let e="";return t&&(e=t.clipboardData.getData("text/plain")),0!=e.length||(await navigator.clipboard.readText().then(t=>{e=t}).catch(t=>{l("Failed to retrieve clipboard data using navigator! Using fallback methods...")}),0!=e.length||(e=window.clipboardData?.getData("Text"))),e}(i),s=n.split(/[^a-zA-Z0-9]+/).filter(t=>t).map(Number).filter(t=>!isNaN(t));2==s.length&&"bm-O"==e.id?(t.ct("bm-O",s?.[0]||""),t.ct("bm-P",s?.[1]||"")):1==s.length?t.ct(e.id,s?.[0]||""):(t.ct("bm-Q",s?.[0]||""),t.ct("bm-R",s?.[1]||""),t.ct("bm-O",s?.[2]||""),t.ct("bm-P",s?.[3]||""))},X=new WeakSet,J=async function(){GM.setValue("bmTemplates",JSON.stringify(this.Te))},q=async function(t){const e=t.templates,i=t?.schemaVersion,n=i.split(/[-\.\+]/),s=this.schemaVersion.split(/[-\.\+]/),o=t?.scriptVersion;n[0]==s[0]?(n[1]!=s[1]&&new tt(this.name,this.version,this.schemaVersion,this).Tt(),this.fe=await async function({Ot:t,$e:i,fe:n}){if(Object.keys(e).length>0)for(const s in e){const o=s,a=e[s];if(e.hasOwnProperty(s)){const e=o.split(" "),s=Number(e?.[0]),r=e?.[1]||"0",l=a.name||`Template ${s||""}`,h={total:a.pixels?.total,colors:new Map(Object.entries(a.pixels?.colors||{}).map(([t,e])=>[Number(t),e]))},c=a.tiles,m={},d={},u=t*i;for(const t in c)if(c.hasOwnProperty(t)){const e=b(c[t]),i=new Blob([e],{type:"image/png"}),n=await createImageBitmap(i);m[t]=n;const s=new OffscreenCanvas(u,u).getContext("2d");s.drawImage(n,0,0);const o=s.getImageData(0,0,n.width,n.height);d[t]=new Uint32Array(o.data.buffer)}const p=new H({displayName:l,Dt:s||this.fe?.length||0,Lt:r||""});p.Bt=h,p.Ht=m,p.Nt=d,n.push(p)}}return n}({Ot:this.Ot,$e:this.$e,fe:this.fe})):n[0]>>24&255,y=g>>>24&255,v=b.get(w)??-2,C=b.get(g)??-2;if(this.pe.get(v)&&(e[i*h+c]=g),-1==v){const t=536870912;this.pe.get(v)?e[i*h+c]=0:(u/o&1)==(f/o&1)?(e[i*h+c]=t,e[(i-1)*h+(c-1)]=t,e[(i-1)*h+(c+1)]=t,e[(i+1)*h+(c-1)]=t,e[(i+1)*h+(c+1)]=t):(e[i*h+c]=0,e[(i-1)*h+c]=t,e[(i+1)*h+c]=t,e[i*h+(c-1)]=t,e[i*h+(c+1)]=t)}if(!s&&x>m&&C!=v&&(d||y>m)){const t=e[i*h+c];for(const s of n){const[n,o,a]=s,r=0!=n?1!=n?t:4278190335:0;e[(i+a)*h+(c+o)]=r}}if(-1==v&&g<=m){const t=p.get(v);p.set(v,t?t+1:1);continue}if(x<=m||y<=m)continue;if(C!=v)continue;const M=p.get(v);p.set(v,M?M+1:1)}return{Be:p,Ie:e}},Q=new WeakSet,K=function(t){const e=JSON.parse(GM_getValue("bmUserSettings","{}"));e.telemetry=t,GM.setValue("bmUserSettings",JSON.stringify(e))};var et=GM_info.script.name.toString(),it=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-11",et),e.setAttribute("bm-X","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement?.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=t?.getAttribute("bm-11")||"Blue Marble",i=t?.getAttribute("bm-X")||"",n=new Map;window.addEventListener("message",t=>{const{source:s,endpoint:o,blobID:a,blobData:r,blink:l}=t.data;if(Date.now(),"blue-marble"==s&&a&&r&&!o){const t=n.get(a);"function"==typeof t?t(r):c(`%c${e}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`,i,"",a),n.delete(a)}});const s=window.fetch;window.fetch=async function(...t){const e=await s.apply(this,t),i=e.clone(),o=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",a=i.headers.get("content-type")||"";if(a.includes("application/json"))i.json().then(t=>{window.postMessage({source:"blue-marble",endpoint:o,jsonData:t},"*")}).catch(t=>{});else if(a.includes("image/")&&!o.includes("openfreemap")&&!o.includes("maps")){const t=Date.now(),e=await i.blob();return new Promise(s=>{const a=crypto.randomUUID();n.set(a,t=>{s(new Response(t,{headers:i.headers,status:i.status,statusText:i.statusText}))}),window.postMessage({source:"blue-marble",endpoint:o,blobID:a,blobData:e,blink:t})}).catch(t=>{Date.now()})}return e}});var nt=`#bm-t p svg{display:inline;height:1em;fill:#fff}#bm-E{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;gap:1em 3ch}#bm-t .bm-z{width:fit-content;max-width:35ch;background-color:#153063e6;border-radius:1em;padding:.5em;gap:1ch;transition:background-color .3s ease}#bm-t .bm-z:hover,#bm-t.bm-z:focus-within{background-color:#112855e6}#bm-t .bm-a{display:block;border:thick double darkslategray;width:fit-content;height:fit-content;padding:1ch}#bm-t .bm-z[data-id="-2"] .bm-a{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}#bm-t .bm-z[data-id="-1"] .bm-a{background:url('data:image/svg+xml;utf8, ') repeat;background-color:transparent!important}#bm-t .bm-z[data-id="-1"] .bm-a svg{fill:#fff!important}#bm-t .bm-z[data-id="0"] .bm-a{background-color:transparent!important}#bm-t .bm-a button{padding:.75em .5ch}#bm-t .bm-a svg{width:4ch}#bm-t .bm-z>.bm-x{flex-direction:column;align-items:flex-start;gap:0}#bm-t .bm-z small{font-size:.75em}#bm-t .bm-z.bm-I{display:none}#bm-t.bm-N #bm-E{flex-direction:column;gap:.25em}#bm-t.bm-N .bm-z{width:auto;margin:0;padding:0}#bm-t.bm-N .bm-a{display:flex;width:100%;gap:.5ch;align-items:center;padding:.1em .5ch;border:none;border-radius:1em}#bm-t.bm-N .bm-a button{padding:.5em .25ch}#bm-t.bm-N .bm-a svg{width:3ch}#bm-t.bm-N .bm-z h2{font-size:.75em}#bm-t #bm-2{font-size:1em}#bm-l div:has(>.bm-3){width:fit-content;justify-content:flex-start}#bm-l .bm-3{display:flex;flex-direction:column;width:13%}#bm-l .bm-3 span{width:fit-content;margin:auto;font-size:.7em}#bm-l .bm-3 button{width:fit-content;padding:0;border-radius:0}#bm-l .bm-3 svg{stroke:#333;stroke-width:.02px;width:100%;min-width:1.5ch;max-width:14.5ch}#bm-l .bm-3 button:hover svg,#bm-l .bm-3 button:focus svg{opacity:.9}#bm-l .bm-n{display:grid;grid-template-columns:1fr 1fr 1fr;width:25%;min-width:3ch;max-width:15ch}#bm-l .bm-n>button{width:100%;padding:0;aspect-ratio:1 / 1;background-color:#fff;border:#333 1px solid;border-radius:0;box-sizing:border-box}#bm-l .bm-n>button[data-status=Incorrect]{background-color:brown}#bm-l .bm-n>button[data-status=Template]{background-color:#2f4f4f}#bm-l .bm-n>button:hover,#bm-l .bm-n>button:focus{opacity:.8}#bm-B{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-start}#bm-B>.bm-L{width:100%;justify-content:flex-start;background-color:#153063e6;border-radius:1em;padding:.5em;transition:background-color .3s ease}#bm-B>.bm-L:hover,#bm-B>.bm-L:focus-within{background-color:#112855e6}#bm-B .bm-1{height:100%;font-size:xxx-large}#bm-B .bm-0{flex-direction:column;align-items:flex-start;gap:0}div:has(>confetti-piece){position:absolute;inset:0;overflow:hidden;pointer-events:none}confetti-piece{position:absolute;top:-10px;width:var(--size);height:var(--size);background:currentColor;transform:translate3d(var(--x),-10vh,0) rotate(var(--rot));animation:fall var(--duration) linear var(--delay);will-change:transform;pointer-events:none}@keyframes fall{to{transform:translate3d(var(--x),110vh,0) rotate(calc(var(--rot) + 720deg))}}.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-W{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:fit-content;max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}.bm-W.bm-N{max-width:300px}.bm-S{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8, ') repeat;cursor:grab;width:100%;height:fit-content}.bm-S.bm-M{cursor:grabbing}.bm-W:has(.bm-S.bm-M){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-S.bm-M{pointer-events:auto}.bm-T{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-W h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-S h1,.bm-y{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-S div:has(h1){display:contents}.bm-W h2{display:inline-block;font-size:larger;font-weight:700;vertical-align:middle}.bm-W h3{display:inline-block;font-size:large;font-weight:700}.bm-L.bm-h{width:fit-content;margin-left:auto;margin-right:auto}.bm-L{margin:.5em 0}.bm-W button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-W button:hover,.bm-W button:focus-visible{background-color:#1061e5}.bm-W button:active,.bm-W button:disabled{background-color:#2e97ff}.bm-W button:disabled{text-decoration:line-through;cursor:not-allowed}.bm-s{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-J{vertical-align:middle}.bm-J svg{width:50%;margin:0 auto;fill:#111}.bm-W button.bm-A{background-color:unset}.bm-A.bm-f:hover,.bm-A.bm-f:focus{background-color:#ffffff2b}.bm-A.bm-f:active{background-color:#ffffff38}.bm-A.bm-g:hover,.bm-A.bm-g:focus{background-color:#0000002b}.bm-A.bm-g:active{background-color:#00000038}input[type=number].bm-C{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-C::-webkit-outer-spin-button,input[type=number].bm-C::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-K)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-K,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-W select{color:#fff;background-color:#144eb9;border-radius:1em;padding:0 .5ch}.bm-W label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch}.bm-W input[type=checkbox]{width:1em}.bm-m{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-W textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-W a:not(:has(*)){text-decoration:underline}.bm-W small{font-size:x-small;color:#d3d3d3}.bm-W ul li{list-style:disc;margin-left:5ch}.bm-W .bm-L.bm-H{max-height:calc(80vh - 150px);overflow:auto}.bm-x{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-D{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}.bm-_{white-space:pre;letter-spacing:0;line-height:1!important;font-size:1.6em;font-family:monospace}.bm-N .bm-L:not(#bm-F .bm-L){margin-top:.25em;margin-bottom:.25em}.bm-N h1:not(#bm-F h1){font-size:1em}`;GM_addStyle(nt);var st,ot="@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:400;src:url(data:font/woff2;base64,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)format('woff2');}";ot.indexOf("@font-face")+1?GM_addStyle(ot):((st=document.createElement("link")).href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap",st.rel="preload",st.as="style",st.onload=function(){this.onload=null,this.rel="stylesheet"},document.head?.appendChild(st));var at=JSON.parse(GM_getValue("bmUserSettings","{}")),rt=(new class{constructor(){this.Pe=null,this.Ae=null,this.We="#bm-p"}Ve(t){return this.Ae=t,this.Pe=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.We)}),this}_e(){return this.Pe}observe(t,e=!1,i=!1){t.observe(this.Ae,{childList:e,subtree:i})}},new class extends M{constructor(t,i){super(t,i),e(this,j),this.window=null,this.Ct="bm-F",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?this.yt("Main window already exists!"):(this.window=this.H({id:this.Ct,class:"bm-W bm-N",style:"top: 10px; left: unset; right: 75px;"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Blue Marble"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().D().H({class:"bm-m"}).H({class:"bm-L"}).A({class:"bm-T",src:"data:image/png;base64,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"},(t,e)=>{const i=new Date;204==Math.floor((i.getTime()-new Date(i.getFullYear(),0,1))/864e5)+1&&(e.parentNode.style.position="relative",e.parentNode.innerHTML=e.parentNode.innerHTML+' ',e.onload=()=>{(new N).Xt(document.querySelector(`#${this.Ct}`))})}).D().W(1,{textContent:this.name}).D().D().V().D().H({class:"bm-L"}).B({id:"bm-w",textContent:"Droplets:"}).D()._().D().B({id:"bm-q",textContent:"Next level in..."}).D()._().D().B({textContent:"Charges: "}).gt(Date.now(),1e3,{style:"font-weight: 700;"},(t,e)=>{t.p.ze=e.id}).D().D().D().V().D().H({class:"bm-L"}).H({class:"bm-L"}).lt({class:"bm-s bm-J",style:"margin-top: 0;",innerHTML:' '},(t,e)=>{e.onclick=()=>{const e=t.p?.Fe;e?.[0]?(t.ct("bm-Q",e?.[0]||""),t.ct("bm-R",e?.[1]||""),t.ct("bm-O",e?.[2]||""),t.ct("bm-P",e?.[3]||"")):t.yt("Coordinates are malformed! Did you try clicking on the canvas first?")}}).D().dt({type:"number",id:"bm-Q",class:"bm-C",placeholder:"Tl X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-R",class:"bm-C",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-O",class:"bm-C",placeholder:"Px X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-P",class:"bm-C",placeholder:"Px Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().D().H({class:"bm-L"}).ut({class:"bm-K",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).D().D().H({class:"bm-L bm-x"}).lt({textContent:"Disable","data-button-status":"shown"},(t,e)=>{e.onclick=()=>{e.disabled=!0,"shown"==e.dataset.buttonStatus?(t.p?.qt?.Ue(!1),e.dataset.buttonStatus="hidden",e.textContent="Enable",t.vt("Disabled templates!")):(t.p?.qt?.Ue(!0),e.dataset.buttonStatus="shown",e.textContent="Disable",t.vt("Enabled templates!")),e.disabled=!1}}).D().lt({textContent:"Create"},(t,e)=>{e.onclick=()=>{const e=document.querySelector(`#${this.Ct} .bm-K`),i=document.querySelector("#bm-Q");if(!i.checkValidity())return i.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const n=document.querySelector("#bm-R");if(!n.checkValidity())return n.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-O");if(!s.checkValidity())return s.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-P");if(!o.checkValidity())return o.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(t?.p?.qt.Me(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(i.value),Number(n.value),Number(s.value),Number(o.value)]),t.vt("Drew to canvas!")):t.yt("No file selected!")}}).D().lt({textContent:"Filter"},(t,e)=>{e.onclick=()=>i(this,j,E).call(this)}).D().D().H({class:"bm-L"}).bt({id:this.C,placeholder:`Status: Sleeping...\nVersion: ${this.version}`,readOnly:!0}).D().D().H({class:"bm-L bm-x",style:"margin-bottom: 0; flex-direction: column;"}).H({class:"bm-x"}).lt({class:"bm-s",innerHTML:"⚙️",title:"Settings"},(t,e)=>{e.onclick=()=>{t.v.Tt()}}).D().lt({class:"bm-s",innerHTML:"🧙",title:"Template Wizard"},(t,e)=>{e.onclick=()=>{const e=t.p?.qt;new tt(this.name,this.version,e?.schemaVersion,e).Tt()}}).D().lt({class:"bm-s",innerHTML:"🎨",title:"Template Color Converter"},(t,e)=>{e.onclick=()=>{window.open("https://pepoafonso.github.io/color_converter_wplace/","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",innerHTML:"🌐",title:"Official Blue Marble Website"},(t,e)=>{e.onclick=()=>{window.open("https://bluemarble.lol/","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",title:"Donate to SwingTheVine",innerHTML:' '},(t,e)=>{e.onclick=()=>{window.open("https://ko-fi.com/swingthevine","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",innerHTML:"🤝",title:"Credits"},(t,e)=>{e.onclick=()=>{new U(this.name,this.version).Tt()}}).D().D().O({textContent:"Made by SwingTheVine",style:"margin-top: auto;"}).D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}}(et,it)),lt=new class{constructor(t,i){e(this,X),this.name=t,this.version=i,this.Se=null,this.v=null,this.schemaVersion="2.0.0",this.Ge=null,this.ve="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.Ot=1e3,this.$e=3,this.Oe=3,this.Kt=function(t){const e=C;e.unshift({id:-1,premium:!1,name:"Erased",rgb:[222,250,206]}),e.unshift({id:-2,premium:!1,name:"Other",rgb:[0,0,0]});const i=new Map;for(const n of e){if(0==n.id||-2==n.id)continue;const e=n.rgb[0],s=n.rgb[1],o=n.rgb[2];for(let a=-t;a<=t;a++)for(let r=-t;r<=t;r++)for(let l=-t;l<=t;l++){const t=e+a,h=s+r,c=o+l;if(t<0||t>255||h<0||h>255||c<0||c>255)continue;const m=(255<<24|c<<16|h<<8|t)>>>0;i.has(m)||i.set(m,n.id)}}return{palette:e,jt:i}}(this.Oe),this.De=null,this.Re="",this.fe=[],this.Te=null,this.je=!0,this.Ee=null,this.pe=new Map}Ye(t){this.Se=t}k(t){this.v=t}async Xe(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.schemaVersion,templates:{}}}async Me(t,e,n){this.Te||(this.Te=await this.Xe()),this.Se.vt(`Creating template at ${n.join(", ")}...`);const s=new H({displayName:e,Dt:0,Lt:m(this.Ge||0,this.ve),file:t,coords:n}),o=!this.v?.kt?.flags?.includes("hl-noSkip"),a=this.v?.kt?.flags?.includes("hl-agSkip"),{Ut:r,Gt:l}=await s.At(this.Ot,this.Kt,o,a);s.Ht=r;const h={total:s.Bt.total,colors:Object.fromEntries(s.Bt.colors)};this.Te.templates[`${s.Dt} ${s.Lt}`]={name:s.displayName,coords:n.join(", "),enabled:!0,pixels:h,tiles:l},this.fe=[],this.fe.push(s),this.Se.vt(`Template created at ${n.join(", ")}!`),await i(this,X,J).call(this)}Je(){}async qe(){this.Te||(this.Te=await this.Xe())}async Ze(){l("Downloading all templates...");for(const t of this.fe)await this.Qe(t),await n(500)}async ye(){const t=JSON.parse(GM_getValue("bmTemplates","{}"))?.templates;if(Object.keys(t).length>0)for(const[e,i]of Object.entries(t))t.hasOwnProperty(e)&&(await this.Qe(new H({displayName:i.name,Dt:e.split(" ")?.[0],Lt:e.split(" ")?.[1],Ht:i.tiles})),await n(500))}async Qe(t){t.Rt();const e=`${t.coords.join("-")}_${t.displayName.replaceAll(" ","-")}`,i=await this.Ce(t);await GM.download({url:URL.createObjectURL(i),name:e+".png",Ke:"uniquify",onload:()=>{l(`Download of template '${e}' complete!`)},onerror:(t,i)=>{h(`Download of template '${e}' failed because ${t}! Details: ${i}`)},ontimeout:()=>{c(`Download of template '${e}' has timed out!`)}})}async Ce(t){const e=t.Ht,i=Object.keys(e).sort(),n=await Promise.all(i.map(t=>{return i=e[t],new Promise((t,e)=>{const n=new Image;n.onload=()=>t(n),n.onerror=e,n.src="data:image/png;base64,"+i});var i}));let s=1/0,o=1/0,a=0,r=0;i.forEach((t,e)=>{const[i,l,h,c]=t.split(",").map(Number),m=n[e],d=i*this.Ot+h,u=l*this.Ot+c;s=Math.min(s,d),o=Math.min(o,u),a=Math.max(a,d+m.width/this.$e),r=Math.max(r,u+m.height/this.$e)});const l=a-s,h=r-o,c=l*this.$e,m=h*this.$e,d=new OffscreenCanvas(c,m),u=d.getContext("2d");i.forEach((t,e)=>{const[i,a,r,l]=t.split(",").map(Number),h=n[e],c=i*this.Ot+r,m=a*this.Ot+l;u.drawImage(h,(c-s)*this.$e,(m-o)*this.$e,h.width,h.height)}),u.globalCompositeOperation="destination-over",u.drawImage(d,0,-1),u.drawImage(d,0,1),u.drawImage(d,-1,0),u.drawImage(d,1,0);const b=new OffscreenCanvas(l,h),p=b.getContext("2d");return p.imageSmoothingEnabled=!1,p.drawImage(d,0,0,l*this.$e,h*this.$e,0,0,l,h),b.convertToBlob({type:"image/png"})}async ti(t,e){if(!this.je)return t;const n=this.Ot*this.$e;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0");const o=this.fe;o.sort((t,e)=>t.Dt-e.Dt);const a=o.map(t=>{const i=Object.keys(t.Ht).filter(t=>t.startsWith(e));if(0===i.length)return null;const n=i.map(e=>{const i=e.split(",");return{ei:t,Vt:t.Ht[e],Nt:t.Nt?.[e],ii:[i[0],i[1]],ni:[i[2],i[3]]}});return n?.[0]}).filter(Boolean),r=a?.length||0;if(!(r>0))return this.Se.vt(`Sleeping\nVersion: ${this.version}`),t;{const t=s(o.filter(t=>Object.keys(t.Ht).filter(t=>t.startsWith(e)).length>0).reduce((t,e)=>t+(e.Bt.total||0),0));this.Se.vt(`Displaying ${r} template${1==r?"":"s"}.\nTotal pixels: ${t}`)}const l=await createImageBitmap(t),h=new OffscreenCanvas(n,n),c=h.getContext("2d");c.imageSmoothingEnabled=!1,c.beginPath(),c.rect(0,0,n,n),c.clip(),c.clearRect(0,0,n,n),c.drawImage(l,0,0,n,n);const m=c.getImageData(0,0,n,n),d=new Uint32Array(m.data.buffer),u=this.v?.kt?.highlight||[[2,0,0]],b=u?.[0],p=1==u?.length&&2==b?.[0]&&0==b?.[1]&&0==b?.[2];for(const t of a){const n=!!t.ei.Bt?.colors?.get(-1);let s=t.Nt.slice();const o=Number(t.ni[0])*this.$e,a=Number(t.ni[1])*this.$e;if(0!=this.pe.size||n||c.drawImage(t.Vt,o,a),!s){const e=c.getImageData(o,a,t.Vt.width,t.Vt.height);s=new Uint32Array(e.data.buffer)}Date.now();const{Be:r,Ie:l}=i(this,X,Z).call(this,{ke:d,De:s,Le:[o,a,t.Vt.width,t.Vt.height],He:u,Ne:p});let h=0;const m=0;for(const[t,e]of r)t!=m&&(h+=e);0==this.pe.size&&!n&&p||c.drawImage(await createImageBitmap(new ImageData(new Uint8ClampedArray(l.buffer),t.Vt.width,t.Vt.height)),o,a),void 0===t.ei.Bt.correct&&(t.ei.Bt.correct={}),t.ei.Bt.correct[e]=r}return await h.convertToBlob({type:"image/png"})}si(t){"BlueMarble"==t?.whoami&&i(this,X,q).call(this,t)}Ue(t){this.je=t}}(et,it),ht=new class{constructor(t){this.qt=t,this.oi=!1,this.ze="",this.Fe=[],this.ai=[]}ri(t){window.addEventListener("message",async e=>{const i=e.data,n=i.jsonData;if(!i||"blue-marble"!==i.source)return;if(!i.endpoint)return;const o=i.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(o){case"me":if(n.status&&"2"!=n.status?.toString()[0])return void t.yt("You are not logged in or Wplace is offline!\nCould not fetch userdata.");const e=Math.ceil(Math.pow(Math.floor(n.level)*Math.pow(30,.65),1/.65)-n.pixelsPainted);if(n.id||n.id,this.qt.Ge=n.id,0!=this.ze.length){const t=document.querySelector("#"+this.ze);if(t){const e=n.charges;t.dataset.endDate=Date.now()+(e.max-e.count)*e.cooldownMs}}t.ct("bm-w",`Droplets: ${s(n.droplets)} `),t.ct("bm-q",`Next level in ${s(e)} pixel${1==e?"":"s"}`);break;case"pixel":const o=i.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),l=new URLSearchParams(i.endpoint.split("?")[1]),h=[l.get("x"),l.get("y")];if(this.Fe.length&&(!o.length||!h.length))return void t.yt("Coordinates are malformed!\nDid you try clicking the canvas first?");this.Fe=[...o,...h];const c=(a=o,r=h,[parseInt(a[0])%4*1e3+parseInt(r[0]),parseInt(a[1])%4*1e3+parseInt(r[1])]),m=document.querySelectorAll("span");for(const t of m){const e=t.textContent.trim();if(e.includes(c[0])&&e.includes(c[1])){let e=document.querySelector("#bm-p");o[0],o[1],h[0],h[1];const i=["Tl X:","Tl Y:","Px X:","Px Y:"],n=["bm-Y","bm-Z","bm-U","bm-V"],s=[...o,...h];if(e)for(const[t,e]of n.entries())document.getElementById(e).textContent=`${i[t]??"??:"} ${s[t]}`;else{e=document.createElement("span"),e.id="bm-p",e.style="display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;";for(const[t,o]of s.entries()){const a=document.createElement("span");a.id=n[s.indexOf(o)??""],a.textContent=`${i[t]??"??:"} ${o}`,e.appendChild(a)}t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",e)}}}break;case"tile":case"tiles":let d=i.endpoint.split("/");d=[parseInt(d[d.length-2]),parseInt(d[d.length-1].replace(".png",""))];const u=i.blobID,b=i.blobData,p=(Date.now(),await this.qt.ti(b,d));window.postMessage({source:"blue-marble",blobID:u,blobData:p,blink:i.blink});break;case"robots":this.oi="false"==n.userscript?.toString().toLowerCase()}var a,r})}async li(t){let e=GM_getValue("bmUserSettings","{}");if(e=JSON.parse(e),!e||!e.telemetry||!e.uuid)return;const i=navigator.userAgent;let n=await this.hi(i),s=this.ci(i);GM_xmlhttpRequest({method:"POST",url:"https://telemetry.thebluecorner.net/heartbeat",headers:{"Content-Type":"application/json"},data:JSON.stringify({uuid:e.uuid,version:t,browser:n,os:s}),onload:t=>{200!==t.status&&h("Failed to send heartbeat:",t.statusText)},onerror:t=>{h("Error sending heartbeat:",t)}})}async hi(t=navigator.userAgent){return(t=t||"").includes("OPR/")||t.includes("Opera")?"Opera":t.includes("Edg/")?"Edge":t.includes("Vivaldi")?"Vivaldi":t.includes("YaBrowser")?"Yandex":t.includes("Kiwi")?"Kiwi":t.includes("Brave")?"Brave":t.includes("Firefox/")?"Firefox":t.includes("Chrome/")?"Chrome":t.includes("Safari/")?"Safari":navigator.brave&&"function"==typeof navigator.brave.isBrave&&await navigator.brave.isBrave()?"Brave":"Unknown"}ci(t=navigator.userAgent){return/Windows NT 11/i.test(t=t||"")?"Windows 11":/Windows NT 10/i.test(t)?"Windows 10":/Windows NT 6\.3/i.test(t)?"Windows 8.1":/Windows NT 6\.2/i.test(t)?"Windows 8":/Windows NT 6\.1/i.test(t)?"Windows 7":/Windows NT 6\.0/i.test(t)?"Windows Vista":/Windows NT 5\.1|Windows XP/i.test(t)?"Windows XP":/Mac OS X 10[_\.]15/i.test(t)?"macOS Catalina":/Mac OS X 10[_\.]14/i.test(t)?"macOS Mojave":/Mac OS X 10[_\.]13/i.test(t)?"macOS High Sierra":/Mac OS X 10[_\.]12/i.test(t)?"macOS Sierra":/Mac OS X 10[_\.]11/i.test(t)?"OS X El Capitan":/Mac OS X 10[_\.]10/i.test(t)?"OS X Yosemite":/Mac OS X 10[_\.]/i.test(t)?"macOS":/Android/i.test(t)?"Android":/iPhone|iPad|iPod/i.test(t)?"iOS":/Linux/i.test(t)?"Linux":"Unknown"}}(lt),ct=new class extends L{constructor(t,i,n){var s;super(t,i),e(this,T),this.kt=n,(s=this.kt).flags??(s.flags=[]),this.mi=structuredClone(this.kt),this.di="bmUserSettings",this.ui=5e3,this.bi=0,setInterval(this.pi.bind(this),this.ui)}async pi(){const t=JSON.stringify(this.kt);t!=JSON.stringify(this.mi)&&Date.now()-this.bi>this.ui&&(await GM.setValue(this.di,t),this.mi=structuredClone(this.kt),this.bi=Date.now())}fi(t,e=void 0){const i=this.kt?.flags?.indexOf(t)??-1;-1!=i&&!0!==e?this.kt?.flags?.splice(i,1):-1==i&&!1!==e&&this.kt?.flags?.push(t)}$t(){const t=' ',e=' ',n=this.kt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];this.window=this.H({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().V().D().H({class:"bm-L",style:"margin-left: 1.5ch;"}).R({textContent:"Highlight transparent pixels"},(t,e,i)=>{i.checked=!this.kt?.flags?.includes("hl-noTrans"),i.onchange=t=>this.fi("hl-noTrans",!t.target.checked)}).D().N({id:"bm-4",textContent:"Choose a preset:",style:"font-weight: 700;"}).D().H({class:"bm-D",role:"group","aria-labelledby":"bm-4"}).H({class:"bm-3"}).B({textContent:"None"}).D().lt({innerHTML:t,"aria-label":'Preset "None"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"None")}).D().D().H({class:"bm-3"}).B({textContent:"Cross"}).D().lt({innerHTML:e,"aria-label":'Preset "Cross Shape"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"Cross")}).D().D().H({class:"bm-3"}).B({textContent:"X"}).D().lt({innerHTML:e.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"','d="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'),"aria-label":'Preset "X Shape"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"X")}).D().D().H({class:"bm-3"}).B({textContent:"Full"}).D().lt({innerHTML:t.replace("#fff","#2f4f4f"),"aria-label":'Preset "Full Template"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"Full")}).D().D().D().N({id:"bm-b",textContent:"Create a custom pattern:",style:"font-weight: 700;"}).D().H({class:"bm-n",role:"group","aria-labelledby":"bm-b"});for(let t=-1;t<=1;t++)for(let e=-1;e<=1;e++){const s=n[n.findIndex(([,i,n])=>i==e&&n==t)]?.[0]??0;let o="Disabled";1==s?o="Incorrect":2==s&&(o="Template"),this.window=this.lt({"data-status":o,"aria-label":`Sub-pixel ${o.toLowerCase()}`},(n,s)=>{s.onclick=()=>i(this,T,$).call(this,s,[e,t])}).D()}this.window=this.D().D().D()}St(){this.window=this.H({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().V().D().H({class:"bm-L",style:"margin-left: 1.5ch;"}).R({textContent:"Template creation should skip transparent tiles"},(t,e,i)=>{i.checked=!this.kt?.flags?.includes("hl-noSkip"),i.onchange=t=>this.fi("hl-noSkip",!t.target.checked)}).D().R({innerHTML:"Experimental: Template creation should aggressively skip transparent tiles"},(t,e,i)=>{i.checked=this.kt?.flags?.includes("hl-agSkip"),i.onchange=t=>this.fi("hl-agSkip",t.target.checked)}).D().D().D()}}(et,it,at);rt.k(ct),rt.S(ht),lt.Ye(rt),lt.k(ct);var mt=JSON.parse(GM_getValue("bmTemplates","{}"));if(lt.si(mt),0==Object.keys(at).length){const t=crypto.randomUUID();GM.setValue("bmUserSettings",JSON.stringify({uuid:t}))}setInterval(()=>ht.li(it),18e5);var dt=at?.telemetry;if(null==dt||dt>1){const t=new class extends M{constructor(t,i,n,s){super(t,i),e(this,Q),this.window=null,this.Ct="bm-k",this.Mt=document.body,this.gi=n,this.uuid=s}async Tt(){if(document.querySelector(`#${this.Ct}`))return void this.yt("Telemetry window already exists!");const t=await this.p.hi(navigator.userAgent),e=this.p.ci(navigator.userAgent);this.window=this.H({id:this.Ct,class:"bm-W",style:"height: 80vh; z-index: 9998;"}).H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:`${this.name} Telemetry`}).D().D().V().D().H({class:"bm-L bm-D",style:"gap: 1.5ch; flex-wrap: wrap;"}).lt({textContent:"Enable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,Q,K).call(this,this.gi);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"Disable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,Q,K).call(this,0);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"More Information"},(t,e)=>{e.onclick=()=>{window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data","_blank","noopener noreferrer")}}).D().D().H({class:"bm-L bm-H"}).H({class:"bm-L"}).W(2,{textContent:"Legal"}).D().N({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}!`}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Non-Legal Summary"}).D().N({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. This is the data stored on our servers:'}).D().J().Z({innerHTML:`A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID. Your UUID is: ${r(this.uuid)} `}).D().Z({innerHTML:`The version of Blue Marble you are using. Your version is: ${r(this.version)} `}).D().Z({innerHTML:`Your browser type, which is used to determine Blue Marble outages and browser popularity. Your browser type is: ${r(t)} `}).D().Z({innerHTML:`Your OS type, which is used to determine Blue Marble outages and OS popularity. Your OS type is: ${r(e)} `}).D().Z({innerHTML:"The date and time that Blue Marble sent the telemetry information."}).D().D().N({innerHTML:'All of the data mentioned above is aggregated every hour . 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.'}).D().D().D().D().D().L(this.Mt)}}(et,it,1,at?.uuid);t.S(ht),t.Tt()}rt.Tt(),ht.ri(rt),new MutationObserver((t,e)=>{const i=document.querySelector("#color-1");if(!i)return;let n=document.querySelector("#bm-G");if(!n){n=document.createElement("button"),n.id="bm-G",n.textContent="Move ↑",n.className="btn btn-soft",n.onclick=function(){const t=this.parentNode.parentNode.parentNode.parentNode,e="Move ↑"==this.textContent;t.parentNode.className=t.parentNode.className.replace(e?"bottom":"top",e?"top":"bottom"),t.style.borderTopLeftRadius=e?"0px":"var(--radius-box)",t.style.borderTopRightRadius=e?"0px":"var(--radius-box)",t.style.borderBottomLeftRadius=e?"var(--radius-box)":"0px",t.style.borderBottomRightRadius=e?"var(--radius-box)":"0px",this.textContent=e?"Move ↓":"Move ↑"};const t=i.parentNode.parentNode.parentNode.parentNode.querySelector("h2");t.parentNode?.appendChild(n)}}).observe(document.body,{childList:!0,subtree:!0}),l(`%c${et}%c (${it}) userscript has loaded!`,"color: cornflowerblue;","")})();
\ No newline at end of file
+(()=>{var t=t=>{throw TypeError(t)},e=(e,i,s)=>i.has(e)?t("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(e):i.set(e,s),i=(e,i,s)=>(((e,i)=>{i.has(e)||t("Cannot access private method")})(e,i),s);function s(t){return new Promise(e=>setTimeout(e,t))}function n(t){return(new Intl.NumberFormat).format(t)}function o(t){return new Intl.NumberFormat(void 0,{style:"percent",t:2,i:2}).format(t)}function a(t){return t.toLocaleString(void 0,{o:"long",l:"numeric",h:"2-digit",m:"2-digit",u:"2-digit"})}function r(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function l(...t){(0,console.log)(...t)}function c(...t){(0,console.error)(...t)}function h(...t){(0,console.warn)(...t)}function m(t,e){if(0===t)return e[0];let i="";const s=e.length;for(;t>0;)i=e[t%s]+i,t=Math.floor(t/s);return i}function d(t,e){let i=0;const s=e.length;for(const n of t){const t=e.indexOf(n);-1==t&&c(`Invalid character '${n}' encountered whilst decoding! Is the decode alphabet/base incorrect?`),i=i*s+t}return i}function u(t){let e="";for(let i=0;i(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*e[0]+.7152*e[1]+.0722*e[2]}function f(t,e,i){return Array.isArray(t)&&([t,e,i]=t),(1<<24|t<<16|e<<8|i).toString(16).slice(1)}var g,w,x,y=[{id:0,premium:!1,name:"Transparent",rgb:[0,0,0]},{id:1,premium:!1,name:"Black",rgb:[0,0,0]},{id:2,premium:!1,name:"Dark Gray",rgb:[60,60,60]},{id:3,premium:!1,name:"Gray",rgb:[120,120,120]},{id:4,premium:!1,name:"Light Gray",rgb:[210,210,210]},{id:5,premium:!1,name:"White",rgb:[255,255,255]},{id:6,premium:!1,name:"Deep Red",rgb:[96,0,24]},{id:7,premium:!1,name:"Red",rgb:[237,28,36]},{id:8,premium:!1,name:"Orange",rgb:[255,127,39]},{id:9,premium:!1,name:"Gold",rgb:[246,170,9]},{id:10,premium:!1,name:"Yellow",rgb:[249,221,59]},{id:11,premium:!1,name:"Light Yellow",rgb:[255,250,188]},{id:12,premium:!1,name:"Dark Green",rgb:[14,185,104]},{id:13,premium:!1,name:"Green",rgb:[19,230,123]},{id:14,premium:!1,name:"Light Green",rgb:[135,255,94]},{id:15,premium:!1,name:"Dark Teal",rgb:[12,129,110]},{id:16,premium:!1,name:"Teal",rgb:[16,174,166]},{id:17,premium:!1,name:"Light Teal",rgb:[19,225,190]},{id:18,premium:!1,name:"Dark Blue",rgb:[40,80,158]},{id:19,premium:!1,name:"Blue",rgb:[64,147,228]},{id:20,premium:!1,name:"Cyan",rgb:[96,247,242]},{id:21,premium:!1,name:"Indigo",rgb:[107,80,246]},{id:22,premium:!1,name:"Light Indigo",rgb:[153,177,251]},{id:23,premium:!1,name:"Dark Purple",rgb:[120,12,153]},{id:24,premium:!1,name:"Purple",rgb:[170,56,185]},{id:25,premium:!1,name:"Light Purple",rgb:[224,159,249]},{id:26,premium:!1,name:"Dark Pink",rgb:[203,0,122]},{id:27,premium:!1,name:"Pink",rgb:[236,31,128]},{id:28,premium:!1,name:"Light Pink",rgb:[243,141,169]},{id:29,premium:!1,name:"Dark Brown",rgb:[104,70,52]},{id:30,premium:!1,name:"Brown",rgb:[149,104,42]},{id:31,premium:!1,name:"Beige",rgb:[248,178,119]},{id:32,premium:!0,name:"Medium Gray",rgb:[170,170,170]},{id:33,premium:!0,name:"Dark Red",rgb:[165,14,30]},{id:34,premium:!0,name:"Light Red",rgb:[250,128,114]},{id:35,premium:!0,name:"Dark Orange",rgb:[228,92,26]},{id:36,premium:!0,name:"Light Tan",rgb:[214,181,148]},{id:37,premium:!0,name:"Dark Goldenrod",rgb:[156,132,49]},{id:38,premium:!0,name:"Goldenrod",rgb:[197,173,49]},{id:39,premium:!0,name:"Light Goldenrod",rgb:[232,212,95]},{id:40,premium:!0,name:"Dark Olive",rgb:[74,107,58]},{id:41,premium:!0,name:"Olive",rgb:[90,148,74]},{id:42,premium:!0,name:"Light Olive",rgb:[132,197,115]},{id:43,premium:!0,name:"Dark Cyan",rgb:[15,121,159]},{id:44,premium:!0,name:"Light Cyan",rgb:[187,250,242]},{id:45,premium:!0,name:"Light Blue",rgb:[125,199,255]},{id:46,premium:!0,name:"Dark Indigo",rgb:[77,49,184]},{id:47,premium:!0,name:"Dark Slate Blue",rgb:[74,66,132]},{id:48,premium:!0,name:"Slate Blue",rgb:[122,113,196]},{id:49,premium:!0,name:"Light Slate Blue",rgb:[181,174,241]},{id:50,premium:!0,name:"Light Brown",rgb:[219,164,99]},{id:51,premium:!0,name:"Dark Beige",rgb:[209,128,81]},{id:52,premium:!0,name:"Light Beige",rgb:[255,197,165]},{id:53,premium:!0,name:"Dark Peach",rgb:[155,82,73]},{id:54,premium:!0,name:"Peach",rgb:[209,128,120]},{id:55,premium:!0,name:"Light Peach",rgb:[250,182,164]},{id:56,premium:!0,name:"Dark Tan",rgb:[123,99,82]},{id:57,premium:!0,name:"Tan",rgb:[156,132,107]},{id:58,premium:!0,name:"Dark Slate",rgb:[51,57,65]},{id:59,premium:!0,name:"Slate",rgb:[109,117,141]},{id:60,premium:!0,name:"Light Slate",rgb:[179,185,209]},{id:61,premium:!0,name:"Dark Stone",rgb:[109,100,63]},{id:62,premium:!0,name:"Stone",rgb:[148,140,107]},{id:63,premium:!0,name:"Light Stone",rgb:[205,197,158]}],$=' ',v=class{constructor(t,i){e(this,g),this.name=t,this.version=i,this.p=null,this.$=null,this.v="bm-r",this.M=null,this.C=null,this.T=[]}S(t){this.p=t}k(t){this.$=t}D(){return this.T.length>0&&(this.C=this.T.pop()),this}N(t){t?.appendChild(this.M),this.M=null,this.C=null,this.T=[]}L(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{},t)),this}H(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"p",{},t)),this}O(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"small",{},t)),this}I(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"span",{},t)),this}B(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"details",{},t)),this}P(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"summary",{},t)),this}A(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"img",{},t)),this}W(t,e={},s=()=>{}){return s(this,i(this,g,w).call(this,"h"+t,{},e)),this}F(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"hr",{},t)),this}U(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"br",{},t)),this}V(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"form",{},t)),this}G(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"fieldset",{},t)),this}R(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"legend",{},t)),this}j(t={},e=()=>{}){const s={};t.textContent?(s.textContent=t.textContent,delete t.textContent):t.innerHTML&&(s.innerHTML=t.innerHTML,delete t.textContent);const n=i(this,g,w).call(this,"label",s),o=i(this,g,w).call(this,"input",{type:"checkbox"},t);return n.insertBefore(o,n.firstChild),this.D(),e(this,n,o),this}Y(t={},e=()=>{}){const s=i(this,g,w).call(this,"label",{textContent:t.textContent??"",for:t.id??""});return delete t.textContent,this.D(),e(this,s,i(this,g,w).call(this,"select",{},t)),this}J(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"option",{},t)),this}X(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ol",{},t)),this}_(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ul",{},t)),this}q(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"menu",{},t)),this}Z(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"li",{},t)),this}K(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"table",{},t)),this}tt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"caption",{},t)),this}et(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"thead",{},t)),this}it(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tbody",{},t)),this}st(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tfoot",{},t)),this}nt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tr",{},t)),this}ot(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"th",{},t)),this}rt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"td",{},t)),this}lt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"button",{},t)),this}ct(t={},e=()=>{}){const s=t.title??t.textContent??"Help: No info";delete t.textContent,t.title=`Help: ${s}`;const n={textContent:"?",className:"bm-10",onclick:()=>{this.ht(this.v,s)}};return e(this,i(this,g,w).call(this,"button",n,t)),this}dt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"input",{},t)),this}ut(t={},e=()=>{}){const s=t.textContent??"";delete t.textContent;const n=i(this,g,w).call(this,"div"),o=i(this,g,w).call(this,"input",{type:"file",tabindex:"-1","aria-hidden":"true"},t);this.D();const a=i(this,g,w).call(this,"button",{textContent:s});return this.D(),this.D(),a.addEventListener("click",()=>{o.click()}),o.addEventListener("change",()=>{a.style.maxWidth=`${a.offsetWidth}px`,o.files.length>0?a.textContent=o.files[0].name:a.textContent=s}),e(this,n,o,a),this}bt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"textarea",{},t)),this}ft(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{class:"bm-S"},t)),this}gt(t=Date.now(),e=500,s={},n=()=>{}){const o="bm--",a=s?.id||o+"-"+crypto.randomUUID().slice(0,8),r={class:o},l=i(this,g,w).call(this,"time",r,s);return l.id=a,l.dataset.endDate=t,setInterval(()=>{if(!l.isConnected)return;const t=Math.max(l.dataset.endDate-Date.now(),0),e=Math.floor(t/1e3),i=Math.floor(e/3600),s=Math.floor(e%60),n=Math.floor(e%3600/60);l.setAttribute("datetime",`PT${i}H${n}M${s}S`),l.textContent=String(i).padStart(2,"0")+":"+String(n).padStart(2,"0")+":"+String(s).padStart(2,"0")},e),n(this,l),this}ht(t,e,i=!1){const s=document.getElementById(t.replace(/^#/,""));s&&(s instanceof HTMLInputElement?s.value=e:i?s.textContent=e:s.innerHTML=e)}wt(t){if(t.disabled)return;t.disabled=!0,t.style.textDecoration="none";const e=t.closest(".bm-W"),i=t.closest(".bm-S"),s=e?.querySelector("h1"),n=e?.querySelector(".bm-m");if(!e||!i||!n)return t.disabled=!1,void(t.style.textDecoration="");const o=e=>{let i,s=!1;const o=()=>{s||(s=!0,clearTimeout(i),n.removeEventListener("transitionend",a),e(),t.disabled=!1,t.style.textDecoration="")},a=t=>{t.target==n&&"height"==t.propertyName&&o()};n.addEventListener("transitionend",a),i=setTimeout(o,360)};if(e.parentElement.append(e),"expanded"==t.dataset.buttonStatus){e.dataset.widthBeforeMinimize=e.style.width,e.dataset.heightBeforeMinimize=e.style.height,e.dataset.minHeightBeforeMinimize=e.style.minHeight,n.style.height=n.scrollHeight+"px",n.offsetHeight,e.style.width||(e.style.width=e.scrollWidth+"px"),o(()=>{n.style.display="none"}),n.style.height="0",(e.style.height||e.classList.contains("bm-N"))&&(e.style.minHeight="0px",e.style.height=(()=>{const t=getComputedStyle(e),s=t=>parseFloat(t)||0,n="border-box"==t.boxSizing?s(t.paddingTop)+s(t.paddingBottom)+s(t.borderTopWidth)+s(t.borderBottomWidth):0;return Math.ceil(i.getBoundingClientRect().height+n+2)})()+"px");const a=s?.cloneNode(!0)??document.createElement("h1"),r=a.textContent;t.nextElementSibling.appendChild(a),t.innerHTML=' ',t.dataset.buttonStatus="collapsed",t.ariaLabel=`Unminimize window "${r}"`}else{const s=i.querySelector("h1"),a=s.textContent;s.remove(),n.style.display="",n.style.height="0",e.style.width=e.dataset.widthBeforeMinimize??"",e.style.minHeight=e.dataset.minHeightBeforeMinimize??"",e.style.height=e.dataset.heightBeforeMinimize??"",n.offsetHeight,o(()=>{n.style.height="",delete e.dataset.widthBeforeMinimize,delete e.dataset.heightBeforeMinimize,delete e.dataset.minHeightBeforeMinimize}),n.style.height=n.scrollHeight+"px",t.innerHTML=$,t.dataset.buttonStatus="expanded",t.ariaLabel=`Minimize window "${a}"`}}xt(t,e,i={}){const s=document.querySelector(t),n=document.querySelector(e),o=i?.yt??(()=>{});if(!s||!n)return void this.$t(`Can not drag! ${s?"":"moveMe"} ${s||n?"":"and "}${n?"":"iMoveThings "}was not found!`);let a,r=!1,l=0,c=null,h=0,m=0,d=0,u=0,b=null;const p=()=>{if(r){const t=Math.abs(h-d),e=Math.abs(m-u);(t>.5||e>.5)&&(h=d,m=u,s.style.transform=`translate(${h}px, ${m}px)`,s.style.left="0px",s.style.top="0px",s.style.right=""),c=requestAnimationFrame(p)}},f=(t,e)=>{r=!0,b=s.getBoundingClientRect(),a=t-b.left,l=e-b.top;const i=window.getComputedStyle(s).transform;if(i&&"none"!==i){const t=new DOMMatrix(i);h=t.m41,m=t.m42}else h=b.left,m=b.top;d=h,u=m,document.body.style.userSelect="none",n.classList.add("bm-M"),document.addEventListener("mousemove",w),document.addEventListener("touchmove",x,{passive:!1}),document.addEventListener("mouseup",g),document.addEventListener("touchend",g),document.addEventListener("touchcancel",g),c&&cancelAnimationFrame(c),p()},g=()=>{r=!1,c&&(cancelAnimationFrame(c),c=null),document.body.style.userSelect="",n.classList.remove("bm-M"),document.removeEventListener("mousemove",w),document.removeEventListener("touchmove",x),document.removeEventListener("mouseup",g),document.removeEventListener("touchend",g),document.removeEventListener("touchcancel",g),o({element:s,x:h,y:m}),b=null},w=t=>{r&&b&&(d=t.clientX-a,u=t.clientY-l)},x=t=>{if(r&&b){const e=t.touches[0];if(!e)return;d=e.clientX-a,u=e.clientY-l,t.preventDefault()}};n.addEventListener("mousedown",function(t){t.preventDefault(),f(t.clientX,t.clientY)}),n.addEventListener("touchstart",function(t){const e=t?.touches?.[0];e&&(f(e.clientX,e.clientY),t.preventDefault())},{passive:!1})}vt(t,e,i={}){const s=document.querySelector(t),n=document.querySelector(e),o=i?.yt??(()=>{});if(!s||!n)return void this.$t(`Can not resize! ${s?"":"resizeMe"} ${s||n?"":"and "}${n?"":"iResizeThings "}was not found!`);let a=!1,r=0,l=0,c=0,h=0,m=0,d=0,u=0,b=0,p=null;const f=()=>Number.isFinite(i?.maxWidth)?i.maxWidth:window.innerWidth-16,g=()=>Number.isFinite(i?.maxHeight)?i.maxHeight:window.innerHeight-16,w=Number.isFinite(i?.minWidth)?i.minWidth:200,x=Number.isFinite(i?.minHeight)?i.minHeight:160,y=(t,e,i)=>Math.min(Math.max(t,e),Math.max(e,i)),$=()=>{if(a){const t=Math.abs(m-u),e=Math.abs(d-b);(t>.5||e>.5)&&(m=u,d=b,s.style.width=`${m}px`,s.style.height=`${d}px`),p=requestAnimationFrame($)}},v=(t,e)=>{a=!0,r=t,l=e,c=s.offsetWidth,h=s.offsetHeight,m=c,d=h,u=c,b=h,document.body.style.userSelect="none",n.classList.add("bm-2g"),document.addEventListener("mousemove",C),document.addEventListener("touchmove",T,{passive:!1}),document.addEventListener("mouseup",M),document.addEventListener("touchend",M),document.addEventListener("touchcancel",M),p&&cancelAnimationFrame(p),$()},M=()=>{a=!1,p&&(cancelAnimationFrame(p),p=null),document.body.style.userSelect="",n.classList.remove("bm-2g"),document.removeEventListener("mousemove",C),document.removeEventListener("touchmove",T),document.removeEventListener("mouseup",M),document.removeEventListener("touchend",M),document.removeEventListener("touchcancel",M),o({element:s,width:m,height:d})},C=t=>{a&&(u=y(c+(t.clientX-r),w,f()),b=y(h+(t.clientY-l),x,g()))},T=t=>{if(!a)return;const e=t?.touches?.[0];e&&(u=y(c+(e.clientX-r),w,f()),b=y(h+(e.clientY-l),x,g()),t.preventDefault())};n.addEventListener("mousedown",t=>{t.preventDefault(),t.stopPropagation(),v(t.clientX,t.clientY)}),n.addEventListener("touchstart",t=>{const e=t?.touches?.[0];e&&(t.preventDefault(),t.stopPropagation(),v(e.clientX,e.clientY))},{passive:!1})}Mt(t){(0,console.info)(`${this.name}: ${t}`),this.ht(this.v,"Status: "+t,!0)}$t(t){(0,console.error)(`${this.name}: ${t}`),this.ht(this.v,"Error: "+t,!0)}};g=new WeakSet,w=function(t,e={},s={}){const n=document.createElement(t);this.M?(this.C?.appendChild(n),this.T.push(this.C),this.C=n):(this.M=n,this.C=n);for(const[t,s]of Object.entries(e))i(this,g,x).call(this,n,t,s);for(const[t,e]of Object.entries(s))i(this,g,x).call(this,n,t,e);return n},x=function(t,e,i){"class"==e?t.classList.add(...i.split(/\s+/)):"for"==e?t.htmlFor=i:"tabindex"==e?t.tabIndex=Number(i):"readonly"==e?t.readOnly="true"==i||"1"==i:"maxlength"==e?t.maxLength=Number(i):e.startsWith("data")?t.dataset[e.slice(5).split("-").map((t,e)=>0==e?t:t[0].toUpperCase()+t.slice(1)).join("")]=i:e.startsWith("aria")?t.setAttribute(e,i):t[e]=i};var M,C,T,S,k,D,N,L=class extends v{constructor(t,i){super(t,i),e(this,M),this.window=null,this.Ct="bm-l",this.Tt=document.body}St(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.L({id:this.Ct,class:"bm-W"}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Settings"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',"aria-label":'Close window "Settings"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Settings"}).D().D().F().D().H({textContent:"Settings take 5 seconds to save."}).D().L({class:"bm-L bm-H"},(t,e)=>{this.kt(),this.Dt()}).D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}kt(){i(this,M,C).call(this,"Pixel Highlight")}Dt(){i(this,M,C).call(this,"Template")}};M=new WeakSet,C=function(t){this.window=this.L({class:"bm-L"}).W(2,{textContent:t}).D().F().D().H({innerHTML:`An error occured loading the ${t} category. SettingsManager failed to override the ${t} function inside WindowSettings.`}).D().D()},T=new WeakSet,S=function(t,e){t.disabled=!0;const i=t.dataset.status,s=this.Nt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];let n=[2,0,0];const o=s;switch(i){case"Disabled":t.dataset.status="Incorrect",t.ariaLabel="Sub-pixel incorrect",n=[1,...e];break;case"Incorrect":t.dataset.status="Template",t.ariaLabel="Sub-pixel template",n=[2,...e];break;case"Template":t.dataset.status="Disabled",t.ariaLabel="Sub-pixel disabled",n=[0,...e];break}const a=s.findIndex(([,t,e])=>t==n[1]&&e==n[2]);0!=n[0]?-1!=a?o[a]=n:o.push(n):-1!=a&&o.splice(a,1),this.Nt.highlight=o,t.disabled=!1},k=async function(t){const e=document.querySelectorAll(".bm-3 button");for(const t of e)t.disabled=!0;let i=[0,0,0,0,2,0,0,0,0];switch(t){case"Cross":i=[0,1,0,1,2,1,0,1,0];break;case"X":i=[1,0,1,0,2,0,1,0,1];break;case"Full":i=[2,2,2,2,2,2,2,2,2];break}const n=document.querySelector(".bm-n")?.childNodes??[];for(let t=0;t{const[s,n,o,a]=e.split(",").map(Number);(n>>24==0?0:n.get(e)??-2;const a=o.get(s);o.set(s,a?a+1:1)}return console.log(o),o};var O=class{constructor(){this.Xt=Math.ceil(80/1300*window.innerWidth),this._t=y.slice(1)}qt(t){const e=document.createElement("div");for(let t=0;t{t.parentNode.childElementCount<=1?t.parentNode.remove():t.remove()},e.appendChild(t)}t.appendChild(e)}},I=class extends HTMLElement{};customElements.define("confetti-piece",I);var B,P,A,z,W,F,U,V,G,R,j,E,Y,J,X,_,q,Z,K,Q,tt,et,it,st,nt,ot=' ',at=' ',rt=class extends v{constructor(t){super(t.name,t.version),e(this,B),this.window=null,this.Ct="bm-t",this.Zt="bm-E",this.Tt=document.body,this.$=t.$??null,this.Kt="ftr-oWin",this.Qt="windowFilter",this.te=null,this.ee=null,this.ie=null,this.se=null,this.ne=1e4,this.oe=360,this.ae=220,this.re=1e3,this.le=1400,this.ce=t.p?.ce,this.he=' ',this.me=' ';const{palette:i,Jt:s}=this.ce.de;this.palette=i,this.ue=0,this.be=0,this.pe=new Map,this.fe=new Map,this.ge=0,this.we=0,this.timeRemaining=0,this.xe="",this.sortPrimary="total",this.sortSecondary="descending",this.showUnused=!1}ye(){i(this,B,A).call(this)?this.$e():this.St()}St(){if(document.querySelector(`#${this.Ct}`))return void i(this,B,F).call(this);this.window=this.L({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:at,"aria-label":'Switch to windowed mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{i(this,B,z).call(this,!0),i(this,B,F).call(this),this.$e()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",innerHTML:ot,"aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>i(this,B,F).call(this),e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h bm-1N"}).W(1,{textContent:"Color Filter"}).D().D().F().D().L({class:"bm-L bm-x bm-h bm-1F",style:"gap: 1.5ch;"}).lt({class:"bm-1u",textContent:"Hide All Colors"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!1)}).D().lt({class:"bm-1u",textContent:"Show All Colors"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!0)}).D().D().F().D().L({class:"bm-L bm-H bm-1r"}).L({class:"bm-L bm-1A",style:"margin-left: 2.5ch; margin-right: 2.5ch;"}).L({class:"bm-L bm-1s"}).I({id:"bm-i",innerHTML:"Tiles Loaded: 0 / ???"}).D().U().D().I({id:"bm-d",innerHTML:"Correct Pixels: ???"}).D().U().D().I({id:"bm-j",innerHTML:"Total Pixels: ???"}).D().U().D().I({id:"bm-7",innerHTML:"Complete: ??? (???)"}).D().U().D().I({id:"bm-8",innerHTML:"??? ???"}).D().D().L({class:"bm-L bm-20"}).H({innerHTML:`Press the ${at.replace("{e.onclick=t=>{t.preventDefault();const e=new FormData(document.querySelector(`#${this.Ct} form`)),s={};for(const[t,i]of e)s[t]=i;console.log(`Primary: ${s.sortPrimary}; Secondary: ${s.sortSecondary}; Unused: ${"on"==s.showUnused}`),i(this,B,q).call(this,s.sortPrimary,s.sortSecondary,"on"==s.showUnused)}}).D().D().D().D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,_).call(this,t),i(this,B,W).call(this),i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),this.ht("#bm-i",`Tiles Loaded: ${n(this.ue)} / ${n(this.be)}`),this.ht("#bm-d",`Correct Pixels: ${n(this.ge)}`),this.ht("#bm-j",`Total Pixels: ${n(this.we)}`),this.ht("#bm-7",`Remaining: ${n((this.we||0)-(this.ge||0))} (${o(((this.we||0)-(this.ge||0))/(this.we||1))})`),this.ht("#bm-8",`Completed at: ${this.xe} `),i(this,B,U).call(this)}$e(){if(document.querySelector(`#${this.Ct}`))return void i(this,B,F).call(this);this.window=this.L({id:this.Ct,class:"bm-W bm-N",style:`width: 360px; height: min(70vh, 32rem); min-width: ${this.oe}px; min-height: ${this.ae}px; max-width: min(${this.re}px, calc(100vw - 16px)); max-height: min(${this.le}px, calc(100vh - 16px));`}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>{const i=document.querySelector("#bm-2");i&&(i.style.display="expanded"==e.dataset.buttonStatus?"none":""),t.wt(e)},e.ontouchend=()=>{e.click()}}).D().L().I({id:"bm-2",class:"bm-y",style:"font-weight: 700;"}).D().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',"aria-label":'Switch to fullscreen mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{i(this,B,z).call(this,!1),i(this,B,F).call(this),this.St()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",innerHTML:ot,"aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>i(this,B,F).call(this),e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h bm-1N"}).W(1,{textContent:"Color Filter"}).D().D().F().D().L({class:"bm-L bm-x bm-h bm-1F",style:"gap: 1.5ch;"}).lt({class:"bm-1u",textContent:"None"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!1)}).D().lt({class:"bm-1u",textContent:"All"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!0)}).D().D().F().D().L({class:"bm-L bm-H bm-1r"}).D().D().L({class:"bm-1O",title:"Resize Color Filter window","aria-label":"Resize Color Filter window",role:"presentation",textContent:"◢",style:"position: absolute; right: 0; bottom: 0; width: 28px; height: 28px; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 4px; padding-bottom: 4px; box-sizing: border-box; z-index: 5; cursor: nwse-resize; pointer-events: auto; touch-action: none; user-select: none; font-size: 8px; line-height: 1; color: rgba(255,255,255,0.95); background: transparent; border: none; box-shadow: none;"}).D().D().N(this.Tt),i(this,B,X).call(this);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,_).call(this,t),i(this,B,W).call(this),i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),i(this,B,U).call(this)}ve(){i(this,B,K).call(this);const t=document.querySelector(`#${this.Zt}`),e={};for(const t of this.palette){const i=this.pe.get(t.id)??0,s=n(i);let a=0,r="0",l=o(1);0!=i&&(a=this.fe.get(t.id)??"???","number"!=typeof a&&this.ue==this.be&&t.id&&(a=0),r="string"==typeof a?a:n(a),l=isNaN(a/i)?"???":o(a/i));const c=parseInt(i)-parseInt(a);e[t.id]={Me:i,Ce:s,Te:a,Se:r,ke:l,De:c}}if(document.querySelector("#bm-2")){const t=this.ge.toString().length>7?this.ge.toString().slice(0,2)+"…"+this.ge.toString().slice(-3):this.ge.toString(),e=this.we.toString().length>7?this.we.toString().slice(0,2)+"…"+this.we.toString().slice(-3):this.we.toString();this.ht("#bm-2",`${t}/${e}`,!0)}if(this.ht("#bm-i",`Tiles Loaded: ${n(this.ue)} / ${n(this.be)}`),this.ht("#bm-d",`Correct Pixels: ${n(this.ge)}`),this.ht("#bm-j",`Total Pixels: ${n(this.we)}`),this.ht("#bm-7",`Remaining: ${n((this.we||0)-(this.ge||0))} (${o(((this.we||0)-(this.ge||0))/(this.we||1))})`),this.ht("#bm-8",`Completed at: ${this.xe} `),!t)return e;const s=Array.from(t.children);for(const t of s){const i=parseInt(t.dataset.id),{Te:s,Se:n,ke:o,Me:a,Ce:r,De:l}=e[i];t.dataset.correct=Number.isNaN(parseInt(s))?"0":s,t.dataset.total=a,t.dataset.percent="%"==o.slice(-1)?o.slice(0,-1):"0",t.dataset.incorrect=l||0;const c=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-9`);c&&(c.textContent=`${n} / ${r}`);const h=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-6`);h&&(h.textContent=`${"number"!=typeof l||isNaN(l)?"???":l} incorrect pixel${1==l?"":"s"}. Completed: ${o}`)}i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}};B=new WeakSet,P=function(){var t,e;return this.$?((t=this.$.Nt)[e=this.Qt]??(t[e]={}),this.$.Nt[this.Qt]):null},A=function(){const t=i(this,B,P).call(this);return"windowed"==t?.mode||"fullscreen"!=t?.mode},z=function(t){const e=i(this,B,P).call(this);e&&(e.mode=t?"windowed":"fullscreen"),this.$&&(this.$.Ne(this.Kt,t),this.$.Le())},W=function(){const t=document.querySelector(`#${this.Ct} #bm-c`),e=document.querySelector(`#${this.Ct} #bm-5`),i=document.querySelector(`#${this.Ct} #bm-e`);t instanceof HTMLSelectElement&&(t.value=this.sortPrimary),e instanceof HTMLSelectElement&&(e.value=this.sortSecondary),i instanceof HTMLInputElement&&(i.checked=this.showUnused)},F=function(){const t=document.querySelector(`#${this.Ct}`);t?.classList.contains("bm-N")&&i(this,B,Y).call(this,t),i(this,B,V).call(this),i(this,B,G).call(this),t?.remove()},U=function(){i(this,B,V).call(this),this.se=setInterval(()=>{document.querySelector(`#${this.Ct}`)?this.ve():i(this,B,V).call(this)},this.ne)},V=function(){this.se&&(clearInterval(this.se),this.se=null)},G=function(){this.te&&(this.te.disconnect(),this.te=null),this.ee&&(window.removeEventListener("resize",this.ee),this.ee=null),this.ie&&(clearTimeout(this.ie),this.ie=null)},R=function(t,e,i){const s=Math.max(e,i);return Math.min(Math.max(Math.round(Number(t)||e),e),s)},j=function(t,e,i){const s=Math.max(8,window.innerWidth-t.offsetWidth-8),n=Math.max(8,window.innerHeight-t.offsetHeight-8);return{x:Math.min(Math.max(Math.round(Number(e)||8),8),s),y:Math.min(Math.max(Math.round(Number(i)||8),8),n)}},E=function(t){const e=i(this,B,P).call(this);if(!e||!t)return;const s=Number(e.width),n=Number(e.height),o=Number.isFinite(s),a=Number.isFinite(n);o&&(e.width=i(this,B,R).call(this,s,this.oe,Math.min(this.re,window.innerWidth-16)),t.style.width=`${e.width}px`),a&&(e.height=i(this,B,R).call(this,n,this.ae,Math.min(this.le,window.innerHeight-16)),t.style.height=`${e.height}px`),requestAnimationFrame(()=>{if(!t.isConnected)return;const s=Number(e.x),n=Number(e.y);if(!Number.isFinite(s)||!Number.isFinite(n))return;const o=i(this,B,j).call(this,t,s,n);t.style.left="0px",t.style.top="0px",t.style.right="",t.style.transform=`translate(${o.x}px, ${o.y}px)`,o.x==s&&o.y==n||(e.x=o.x,e.y=o.y,this.$?.Le())})},Y=function(t){const e=i(this,B,P).call(this);if(!e||!t?.isConnected||!t.classList.contains("bm-N"))return;if(t.querySelector('.bm-S button[data-button-status="collapsed"]'))return;const s=t.getBoundingClientRect(),n=i(this,B,R).call(this,s.width,this.oe,Math.min(this.re,window.innerWidth-16)),o=i(this,B,R).call(this,s.height,this.ae,Math.min(this.le,window.innerHeight-16));Math.round(s.width)!=n&&(t.style.width=`${n}px`),Math.round(s.height)!=o&&(t.style.height=`${o}px`);const a=i(this,B,j).call(this,t,s.left,s.top);t.style.left="0px",t.style.top="0px",t.style.right="",t.style.transform=`translate(${a.x}px, ${a.y}px)`,e.x=a.x,e.y=a.y,e.width=n,e.height=o,this.$?.Le()},J=function(t,e=150){this.ie&&clearTimeout(this.ie),this.ie=setTimeout(()=>{this.ie=null,i(this,B,Y).call(this,t)},e)},X=function(){const t=document.querySelector(`#${this.Ct}.bm-W`);t&&(i(this,B,G).call(this),i(this,B,E).call(this,t),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`,{yt:({element:t})=>i(this,B,Y).call(this,t)}),this.vt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-1O`,{minWidth:this.oe,minHeight:this.ae,maxWidth:Math.min(this.re,window.innerWidth-16),maxHeight:Math.min(this.le,window.innerHeight-16),yt:({element:t})=>i(this,B,Y).call(this,t)}),"function"==typeof ResizeObserver&&(this.te=new ResizeObserver(()=>i(this,B,J).call(this,t)),this.te.observe(t)),this.ee=()=>i(this,B,J).call(this,t,0),window.addEventListener("resize",this.ee))},_=function(t){const e=t.closest(`#${this.Ct}`)?.classList.contains("bm-N");console.log(`Is Windowed Mode: ${e}`);const i=new v(this.name,this.version);i.L({id:this.Zt});const s=this.ve();for(const t of this.palette){const n="#"+f(t.rgb).toUpperCase(),o=p(t.rgb);let a=1.05/(o+.05)>(o+.05)/.05?"white":"black";t.id||(a="transparent");const r="white"==a?"bm-f":"bm-g",{Te:l,Se:c,ke:h,Me:m,Ce:d,De:u}=s[t.id],b=!!this.ce.He.get(t.id);if(e){const e=`background-size: auto 100%; background-repeat: repeat-x; background-image: url("data:image/svg+xml;utf8, ");`;i.L({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==h.slice(-1)?h.slice(0,-1):"0","data-incorrect":u||0}).L({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});${t.premium?e:""}`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.me:this.he,style:`color: ${a};`},(e,i)=>{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.me,i.dataset.state="hidden",i.ariaLabel=`Show the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!0)):(i.innerHTML=this.he,i.dataset.state="shown",i.ariaLabel=`Hide the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!1)),i.disabled=!1,i.style.textDecoration=""},t.id||(i.disabled=!0)}).D().O({textContent:`#${t.id.toString().padStart(2,0)}`,style:`color: ${-1==t.id||0==t.id?"white":a}`}).D().W(2,{textContent:t.name,style:`color: ${-1==t.id||0==t.id?"white":a}`}).D().O({class:"bm-9",textContent:`${c} / ${d}`,style:`color: ${-1==t.id||0==t.id?"white":a}; flex: 1 1 auto; text-align: right;`}).D().D().D()}else i.L({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==h.slice(-1)?h.slice(0,-1):"0","data-incorrect":u||0}).L({class:"bm-D",style:"flex-direction: column;"}).L({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.me:this.he,style:`color: ${a};`},(e,i)=>{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.me,i.dataset.state="hidden",i.ariaLabel=`Show the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!0)):(i.innerHTML=this.he,i.dataset.state="shown",i.ariaLabel=`Hide the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!1)),i.disabled=!1,i.style.textDecoration=""},t.id||(i.disabled=!0)}).D().D().O({textContent:-2==t.id?"???????":n}).D().D().L({class:"bm-x"}).W(2,{textContent:(t.premium?"★ ":"")+t.name}).D().L({class:"bm-x",style:"gap: 1.5ch;"}).O({textContent:`#${t.id.toString().padStart(2,0)}`}).D().O({class:"bm-9",textContent:`${c} / ${d}`}).D().D().H({class:"bm-6",textContent:`${"number"!=typeof u||isNaN(u)?"???":u} incorrect pixel${1==u?"":"s"}. Completed: ${h}`}).D().D().D()}i.N(t)},q=function(t,e,i){this.sortPrimary=t,this.sortSecondary=e,this.showUnused=i;const s=document.querySelector(`#${this.Zt}`),n=Array.from(s.children);n.sort((s,n)=>{const o=s.getAttribute("data-"+t),a=n.getAttribute("data-"+t),r=parseFloat(o),l=parseFloat(a),c=!isNaN(r),h=!isNaN(l);if(i?s.classList.remove("bm-I"):Number(s.getAttribute("data-total"))||s.classList.add("bm-I"),c&&h)return"ascending"===e?r-l:l-r;{const t=o.toLowerCase(),i=a.toLowerCase();return ti?"ascending"===e?1:-1:0}}),n.forEach(t=>s.appendChild(t))},Z=function(t){const e=document.querySelector(`#${this.Zt}`),i=Array.from(e.children);for(const e of i){if(e.classList?.contains("bm-I"))continue;const i=e.querySelector(".bm-a button");("hidden"!=i.dataset.state||t)&&("shown"==i.dataset.state&&t||i.click())}},K=function(){this.ue=0,this.be=0,this.we=0,this.ge=0,this.fe=new Map,this.pe=new Map;for(const t of this.ce.Ie){const e=t.Pt?.total??0;this.we+=e??0;const i=t.Pt?.colors??new Map;for(const[t,e]of i){const i=Number(e)||0,s=this.pe.get(t)??0;this.pe.set(t,s+i)}const s=t.Pt?.correct??{};this.ue+=Object.keys(s).length,this.be+=Object.keys(t.Ot).length;for(const t of Object.values(s))for(const[e,i]of t){const t=Number(i)||0;this.ge+=t;const s=this.fe.get(e)??0;this.fe.set(e,s+t)}}console.log(`Tiles loaded: ${this.ue} / ${this.be}`),this.ge>=this.we&&this.we&&this.ue==this.be&&(new O).qt(document.querySelector(`#${this.Ct}`)),this.timeRemaining=new Date(30*(this.we-this.ge)*1e3+Date.now()),this.xe=a(this.timeRemaining)},Q=new WeakSet,tt=async function(t,e,i){i.preventDefault();const s=await async function(t){let e="";return t&&(e=t.clipboardData.getData("text/plain")),0!=e.length||(await navigator.clipboard.readText().then(t=>{e=t}).catch(t=>{l("Failed to retrieve clipboard data using navigator! Using fallback methods...")}),0!=e.length||(e=window.clipboardData?.getData("Text"))),e}(i),n=s.split(/[^a-zA-Z0-9]+/).filter(t=>t).map(Number).filter(t=>!isNaN(t));2==n.length&&"bm-O"==e.id?(t.ht("bm-O",n?.[0]||""),t.ht("bm-P",n?.[1]||"")):1==n.length?t.ht(e.id,n?.[0]||""):(t.ht("bm-Q",n?.[0]||""),t.ht("bm-R",n?.[1]||""),t.ht("bm-O",n?.[2]||""),t.ht("bm-P",n?.[3]||""))};var lt=class extends v{constructor(t,i,s,n=void 0){super(t,i),e(this,et),this.window=null,this.Ct="bm-u",this.Tt=document.body,this.Be=JSON.parse(GM_getValue("bmTemplates","{}")),this.scriptVersion=this.Be?.scriptVersion,this.schemaVersion=this.Be?.schemaVersion,this.Pe=void 0,this.Ae=s,this.ce=n}St(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();let t="";document.querySelector("#bm-F")||(t=t.concat("z-index: 9001;").trim()),this.window=this.L({id:this.Ct,class:"bm-W",style:t},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Template Wizard"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Template Wizard"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Status"}).D().H({id:"bm-v",textContent:"Loading template storage status..."}).D().D().L({class:"bm-L bm-H"}).W(2,{textContent:"Detected templates:"}).D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`),i(this,et,it).call(this),i(this,et,st).call(this)}};et=new WeakSet,it=function(){const t=this.schemaVersion.split(/[-\.\+]/),e=this.Ae.split(/[-\.\+]/);let s="";t[0]==e[0]?t[1]==e[1]?(s='Template storage health: Healthy! No futher action required. (Reason: Semantic version matches)',this.Pe="Good"):(s='Template storage health: Poor! You can still use your template, but some features may not work. It is recommended that you update Blue Marble\'s template storage. (Reason: MINOR version mismatch)',this.Pe="Poor"):t[0]Bad! It is guaranteed that some features are broken. You might still be able to use the template. It is HIGHLY recommended that you download all templates and update Blue Marble\'s template storage before continuing. (Reason: MAJOR version mismatch)',this.Pe="Bad"):(s='Template storage health: Dead! Blue Marble can not load the template storage. (Reason: MAJOR version unknown)',this.Pe="Dead");const n=` If you want to continue using your current templates, then make sure the template storage (schema) is up-to-date. If you don't want to update the template storage, then downgrade Blue Marble to version ${r(this.scriptVersion)} to continue using your templates. Alternatively, if you don't care about corrupting the templates listed below, you can fix any issues with the template storage by uploading a new template.`,o=function(){const t=[...document.querySelectorAll("body > div > .hidden")].filter(t=>/version:/i.test(t.textContent));if(t[0]){const e=t[0].textContent?.match(/\d+/);return e?new Date(Number(e[0])):void 0}}();let l=o?a(o):"???";this.ht("#bm-v",`${s} Your templates were created during Blue Marble version ${r(this.scriptVersion)} with schema version ${r(this.schemaVersion)} . The current Blue Marble version is ${r(this.version)} and requires schema version ${r(this.Ae)} . Wplace was last updated on ${l} .${"Good"!=this.Pe?n:""}`);const c=new v(this.name,this.version);"Dead"!=this.Pe&&(c.L({class:"bm-L bm-D bm-h",style:"gap: 1.5ch;"}),c.lt({textContent:"Download all templates"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.ce.ze().then(()=>{e.disabled=!1})}}).D()),"Poor"!=this.Pe&&"Bad"!=this.Pe||c.lt({textContent:`Update template storage to ${this.Ae}`},(t,e)=>{e.onclick=()=>{e.disabled=!0,i(this,et,nt).call(this,!0)}}).D(),c.D().N(document.querySelector("#bm-v").parentNode)},st=function(){const t=this.Be?.templates;if(Object.keys(t).length>0){const e=document.querySelector(`#${this.Ct} .bm-H`),i=new v(this.name,this.version);i.L({id:"bm-B",class:"bm-L"});for(const e in t){const s=e,o=t[e];if(t.hasOwnProperty(e)){const t=s.split(" "),e=Number(t?.[0]),a=d(t?.[1]||"0",this.ce.We),r=o.name||`Template ${e||""}`,l=o?.coords?.split(",").map(Number),c=o.pixels?.total??void 0,h=void 0,m="number"==typeof e?n(e):"???",u="number"==typeof a?n(a):"???",b="number"==typeof c?n(c):"???";i.L({class:"bm-L bm-D"}).L({class:"bm-D",style:"flex-direction: column; gap: 0;"}).L({class:"bm-1",textContent:h||"🖼️"}).D().O({textContent:`#${m}`}).D().D().L({class:"bm-D bm-0"}).W(3,{textContent:r}).D().I({textContent:`Uploaded by user #${u}`}).D().I({textContent:`Coordinates: ${l.join(", ")}`}).D().I({textContent:`Total Pixels: ${b}`}).D().D().D()}}i.D().N(e)}},nt=async function(t){if(t){const t=document.querySelector(`#${this.Ct} .bm-m`);t.innerHTML="",new v(this.name,this.version).L({class:"bm-L"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Status"}).D().H({textContent:"Updating template storage. Please wait..."}).D().D().D().N(t)}GM_deleteValue("bmCoords");const e=this.Be?.templates;if(Object.keys(e).length>0)for(const[t,i]of Object.entries(e))if(e.hasOwnProperty(t)){const t=new H({displayName:i.name,Ot:i.tiles});t.Yt();const e=await this.ce.Fe(t);await this.ce.Ue(e,t.displayName,t.coords)}t&&(console.log("Restarting Template Wizard..."),document.querySelector(`#${this.Ct}`).remove(),new lt(this.name,this.version,this.Ae,this.ce).St())};var ct,ht,mt,dt,ut,bt,pt,ft,gt=lt;ct=new WeakSet,ht=function(){const t=this.$?.Nt?.filter,e=Array.isArray(t)?t:[];this.He.clear();for(const t of e){const e=Number(t);Number.isFinite(e)&&this.He.set(e,!0)}},mt=function(){this.$&&(this.$.Nt.filter=Array.from(this.He.keys()).map(t=>Number(t)).filter(t=>Number.isFinite(t)).sort((t,e)=>t-e),this.$.Le())},dt=async function(){GM.setValue("bmTemplates",JSON.stringify(this.Ve))},ut=async function(t){console.log("Parsing BlueMarble...");const e=t.templates;console.log(`BlueMarble length: ${Object.keys(e).length}`);const i=t?.schemaVersion,s=i.split(/[-\.\+]/),n=this.schemaVersion.split(/[-\.\+]/),o=t?.scriptVersion;console.log(`BlueMarble Template Schema: ${i}; Script Version: ${o}`),s[0]==n[0]?(s[1]!=n[1]&&new gt(this.name,this.version,this.schemaVersion,this).St(),this.Ie=await async function({Bt:t,Ge:i,Ie:s}){if(Object.keys(e).length>0)for(const n in e){const o=n,a=e[n];if(console.log(`Template Key: ${o}`),e.hasOwnProperty(n)){const e=o.split(" "),n=Number(e?.[0]),r=e?.[1]||"0",l=a.name||`Template ${n||""}`,c={total:a.pixels?.total,colors:new Map(Object.entries(a.pixels?.colors||{}).map(([t,e])=>[Number(t),e]))},h=a.tiles,m={},d={},u=t*i;for(const t in h)if(console.log(t),h.hasOwnProperty(t)){const e=b(h[t]),i=new Blob([e],{type:"image/png"}),s=await createImageBitmap(i);m[t]=s;const n=new OffscreenCanvas(u,u).getContext("2d");n.drawImage(s,0,0);const o=n.getImageData(0,0,s.width,s.height);d[t]=new Uint32Array(o.data.buffer)}const p=new H({displayName:l,Lt:n||this.Ie?.length||0,Ht:r||""});p.Pt=c,p.Ot=m,p.It=d,s.push(p),console.log(this.Ie),console.log("^^^ This ^^^")}}return s}({Bt:this.Bt,Ge:this.Ge,Ie:this.Ie})):s[0]>>24&255,y=g>>>24&255,$=b.get(w)??-2,v=b.get(g)??-2;if(this.He.get($)&&(e[i*c+h]=g),-1==$){const t=536870912;this.He.get($)?e[i*c+h]=0:(u/o&1)==(f/o&1)?(e[i*c+h]=t,e[(i-1)*c+(h-1)]=t,e[(i-1)*c+(h+1)]=t,e[(i+1)*c+(h-1)]=t,e[(i+1)*c+(h+1)]=t):(e[i*c+h]=0,e[(i-1)*c+h]=t,e[(i+1)*c+h]=t,e[i*c+(h-1)]=t,e[i*c+(h+1)]=t)}if(!n&&x>m&&v!=$&&(d||y>m)){const t=e[i*c+h];for(const n of s){const[s,o,a]=n,r=0!=s?1!=s?t:4278190335:0;e[(i+a)*c+(h+o)]=r}}if(-1==$&&g<=m){const t=p.get($);p.set($,t?t+1:1);continue}if(x<=m||y<=m)continue;if(v!=$)continue;const M=p.get($);p.set($,M?M+1:1)}return console.log("List of template pixels that match the tile:"),console.log(p),{qe:p,Ze:e}},pt=new WeakSet,ft=function(t){const e=JSON.parse(GM_getValue("bmUserSettings","{}"));e.telemetry=t,GM.setValue("bmUserSettings",JSON.stringify(e))};var wt=GM_info.script.name.toString(),xt=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-11",wt),e.setAttribute("bm-X","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement?.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=t?.getAttribute("bm-11")||"Blue Marble",i=t?.getAttribute("bm-X")||"",s=new Map;window.addEventListener("message",t=>{const{source:n,endpoint:o,blobID:a,blobData:r,blink:l}=t.data,c=Date.now()-l;if(console.groupCollapsed(`%c${e}%c: ${s.size} Recieved IMAGE message about blob "${a}"`,i,""),console.log(`Blob fetch took %c${String(Math.floor(c/6e4)).padStart(2,"0")}:${String(Math.floor(c/1e3)%60).padStart(2,"0")}.${String(c%1e3).padStart(3,"0")}%c MM:SS.mmm`,i,""),console.log(s),console.groupEnd(),"blue-marble"==n&&a&&r&&!o){const t=s.get(a);"function"==typeof t?t(r):h(`%c${e}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`,i,"",a),s.delete(a)}});const n=window.fetch;window.fetch=async function(...t){const o=await n.apply(this,t),a=o.clone(),r=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",l=a.headers.get("content-type")||"";if(l.includes("application/json"))console.log(`%c${e}%c: Sending JSON message about endpoint "${r}"`,i,""),a.json().then(t=>{const s=r?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();if("me"==s)try{sessionStorage.setItem("bm-2m",JSON.stringify(t))}catch(t){console.warn(`%c${e}%c: Failed to cache "/me" payload`,i,"",t)}window.postMessage({source:"blue-marble",endpoint:r,jsonData:t},"*")}).catch(t=>{console.error(`%c${e}%c: Failed to parse JSON: `,i,"",t)});else if(l.includes("image/")&&!r.includes("openfreemap")&&!r.includes("maps")){const t=Date.now(),n=await a.blob();return console.log(`%c${e}%c: ${s.size} Sending IMAGE message about endpoint "${r}"`,i,""),new Promise(o=>{const l=crypto.randomUUID();s.set(l,t=>{o(new Response(t,{headers:a.headers,status:a.status,statusText:a.statusText})),console.log(`%c${e}%c: ${s.size} Processed blob "${l}"`,i,"")}),window.postMessage({source:"blue-marble",endpoint:r,blobID:l,blobData:n,blink:t})}).catch(n=>{const o=Date.now();console.error(`%c${e}%c: Failed to Promise blob!`,i,""),console.groupCollapsed(`%c${e}%c: Details of failed blob Promise:`,i,""),console.log(`Endpoint: ${r}\nThere are ${s.size} blobs processing...\nBlink: ${t.toLocaleString()}\nTime Since Blink: ${String(Math.floor(o/6e4)).padStart(2,"0")}:${String(Math.floor(o/1e3)%60).padStart(2,"0")}.${String(o%1e3).padStart(3,"0")} MM:SS.mmm`),console.error("Exception stack:",n),console.groupEnd()})}return o}});var yt=`div:has(>confetti-piece){position:absolute;inset:0;overflow:hidden;pointer-events:none}confetti-piece{position:absolute;top:-10px;width:var(--size);height:var(--size);background:currentColor;transform:translate3d(var(--x),-10vh,0) rotate(var(--rot));animation:fall var(--duration) linear var(--delay);will-change:transform;pointer-events:none}@keyframes fall{to{transform:translate3d(var(--x),110vh,0) rotate(calc(var(--rot) + 720deg))}}.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-W{--bm-surface-strong: rgba(9, 20, 42, .5);--bm-surface-soft: rgba(24, 41, 74, .28);--bm-surface-glass: rgba(255, 255, 255, .1);--bm-surface-glass-strong: rgba(255, 255, 255, .18);--bm-border-soft: rgba(255, 255, 255, .18);--bm-border-strong: rgba(163, 228, 255, .34);--bm-text-primary: rgba(17, 36, 66, .96);--bm-text-secondary: rgba(36, 57, 90, .84);--bm-accent-start: #baf6ff;--bm-accent-end: #81b6ff;--bm-accent-shadow: rgba(132, 182, 255, .22);--bm-font-body: "Rajdhani", "Segoe UI Variable Text", "Segoe UI", sans-serif;--bm-font-display: "Michroma", "Orbitron", "Segoe UI", sans-serif;--bm-font-mono: "Roboto Mono", "Rajdhani", "Courier New", monospace;position:fixed;isolation:isolate;overflow:hidden;background:radial-gradient(circle at 14% 12%,rgba(255,255,255,.24),transparent 18%),radial-gradient(circle at 86% 8%,rgba(186,246,255,.22),transparent 24%),radial-gradient(circle at 82% 84%,rgba(129,182,255,.18),transparent 28%),linear-gradient(145deg,#ffffff2e,#ffffff0f 22%,#6991d414 54%,#09142a2e);color:var(--bm-text-primary);padding:6px;border-radius:16px;border:1px solid var(--bm-border-soft);box-shadow:0 18px 40px #0003,inset 0 1px #ffffff38,inset 0 -1px #ffffff0d;z-index:9000;transition:background .32s ease,border-color .22s ease,box-shadow .22s ease,opacity .22s ease,transform 0s,width .22s ease,max-width .22s ease,max-height .22s ease;top:75px;left:60px;width:auto;max-height:fit-content;max-width:calc(100% - 135px);backdrop-filter:blur(26px) saturate(1.25);font-family:var(--bm-font-body);letter-spacing:.04em}.bm-W:before,.bm-W:after{content:"";position:absolute;inset:0;pointer-events:none}.bm-W:before{border-radius:inherit;padding:1px;background:linear-gradient(135deg,#ffffff73,#ffffff1f 24%,#baf6ff38 58%,#81b6ff4d);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.85}.bm-W:after{border-radius:inherit;background:linear-gradient(180deg,rgba(255,255,255,.2),transparent 24%),radial-gradient(circle at 18% 0%,rgba(255,255,255,.22),transparent 22%),radial-gradient(circle at 88% 16%,rgba(186,246,255,.18),transparent 18%);opacity:1}.bm-S{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.28ch;padding:.18rem .24rem;border-radius:10px;border:1px solid rgba(255,255,255,.16);background:radial-gradient(circle at 0 0,rgba(255,255,255,.22) 0,transparent 42%),linear-gradient(135deg,#ffffff2e,#ffffff14);cursor:grab;width:100%;height:fit-content;box-shadow:inset 0 1px #ffffff29,0 6px 18px #0000001a}.bm-S.bm-M{cursor:grabbing}.bm-W:has(.bm-S.bm-M){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-S.bm-M{pointer-events:auto}.bm-T{display:inline-block;height:2.2em;margin-right:.45ch;padding:.2rem;border-radius:12px;vertical-align:middle;background:linear-gradient(135deg,#ffffff38,#ffffff14);box-shadow:inset 0 1px #ffffff29,0 8px 18px #0000001f}.bm-W h1{display:inline-block;font-size:1rem;font-weight:700;vertical-align:middle;font-family:var(--bm-font-display);text-transform:uppercase;letter-spacing:.14em;color:#10213cf5}.bm-S h1,.bm-y{font-size:.78rem;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-family:var(--bm-font-display);letter-spacing:.14em;color:#122542f2;text-shadow:0 1px 0 rgba(255,255,255,.28)}.bm-S div:has(h1){display:contents}.bm-W h2{display:inline-block;font-size:.88rem;font-weight:700;vertical-align:middle;font-family:var(--bm-font-display);letter-spacing:.1em;color:#122340f5}.bm-W h3{display:inline-block;font-size:large;font-weight:700;font-family:var(--bm-font-display);letter-spacing:.08em;color:#122340f5}.bm-W p{color:var(--bm-text-secondary);line-height:1.5;letter-spacing:.035em}.bm-W hr{border:none;height:1px;margin:.32rem 0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.28),transparent)}.bm-L.bm-h{width:fit-content;margin-left:auto;margin-right:auto}.bm-L{margin:.24em 0}.bm-W input,.bm-W select,.bm-W textarea,.bm-W button{font:inherit}.bm-W button{appearance:none;color:var(--bm-text-primary);font-family:var(--bm-font-display);background:linear-gradient(135deg,#ffffff38,#a4d0ff24);border:1px solid rgba(255,255,255,.22);border-radius:999px;padding:.28em .62em;min-height:1.78em;font-weight:600;letter-spacing:.1em;box-shadow:inset 0 1px #ffffff38,0 8px 20px #0000001a;transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,filter .18s ease,opacity .18s ease,transform .18s ease}.bm-W button:hover,.bm-W button:focus-visible{background:linear-gradient(135deg,#ffffff47,#baf6ff2e);border-color:#e6f5ff5c;box-shadow:inset 0 1px #ffffff42,0 10px 22px #0000001f;transform:translateY(-1px)}.bm-W button:focus-visible,.bm-W input:focus-visible,.bm-W select:focus-visible,.bm-W textarea:focus-visible{outline:none;border-color:#74e7ff9e;box-shadow:0 0 0 3px #74e7ff26,inset 0 1px #ffffff1f}.bm-W button:active{transform:translateY(0) scale(.98);filter:brightness(.98);box-shadow:inset 0 1px #ffffff1f,0 8px 16px #12244e3d}.bm-W button:disabled{opacity:.56;cursor:not-allowed;text-decoration:none;transform:none;filter:saturate(.72);box-shadow:none}.bm-W button.bm-1G{color:#07203b;background:linear-gradient(135deg,#ffffff6b,#baf6ff57);border-color:#eff8ff6b;box-shadow:inset 0 1px #ffffff70,0 10px 22px #81b6ff24}.bm-W button.bm-1G:hover,.bm-W button.bm-1G:focus-visible{background:linear-gradient(135deg,#ffffff85,#ccfaff5c)}.bm-W button.bm-1u{background:linear-gradient(135deg,#ffffff29,#ffffff14);border-color:#ffffff2e;box-shadow:inset 0 1px #ffffff24,0 8px 20px #00000014}.bm-W button.bm-1u:hover,.bm-W button.bm-1u:focus-visible{background:linear-gradient(135deg,#ffffff3d,#baf6ff1f)}.bm-s{display:inline-flex;align-items:center;justify-content:center;border:1px solid rgba(255,255,255,.16);inline-size:1.62rem;block-size:1.62rem;min-height:1.62rem!important;min-width:1.62rem;margin-top:0;text-align:center;line-height:1;padding:0!important;aspect-ratio:1 / 1;border-radius:50%!important;background:linear-gradient(135deg,#ffffff3d,#ffffff1a)!important;box-shadow:inset 0 1px #ffffff2e,0 8px 18px #0000001a!important;overflow:hidden;flex:0 0 auto;font-size:.74rem}.bm-s svg{display:block;width:70%;height:70%;flex:0 0 auto;margin:auto;color:currentColor;pointer-events:none;transform:translateZ(0);overflow:visible}.bm-s .bm-1g{width:68%;height:68%;transform:translateY(.5px) translateZ(0)}.bm-s .bm-1b{width:62%;height:62%;transform:translateY(.75px) translateZ(0)}#bm-t.bm-N .bm-s .bm-1b{transform:translateZ(0)}.bm-J{vertical-align:middle}.bm-J svg{width:50%;margin:0 auto;fill:#111}.bm-W button.bm-A{background:transparent!important;box-shadow:none!important;border-color:transparent!important}.bm-A.bm-f:hover,.bm-A.bm-f:focus{background-color:#ffffff2e!important}.bm-A.bm-f:active{background-color:#ffffff3d!important}.bm-A.bm-g:hover,.bm-A.bm-g:focus{background-color:#00000024!important}.bm-A.bm-g:active{background-color:#0003!important}.bm-W input[type=number],.bm-W select,.bm-W textarea{color:var(--bm-text-primary);font-family:var(--bm-font-body);background:linear-gradient(180deg,#ffffff29,#ffffff0f);border:1px solid rgba(255,255,255,.18);border-radius:12px;box-shadow:inset 0 1px #ffffff1f}input[type=number].bm-C{appearance:auto;-moz-appearance:textfield;width:5.9ch;margin-left:1ch;padding:.2em .35ch;font-size:small}input[type=number].bm-C::-webkit-outer-spin-button,input[type=number].bm-C::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-K)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-K,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-W select{padding:.22em .45ch}.bm-W label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch;align-items:center;color:var(--bm-text-secondary)}.bm-W input[type=checkbox]{width:1em;accent-color:#74e7ff}.bm-m{overflow:hidden;max-height:calc(100% - 5px);transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-W textarea{font-size:small;padding:.38em .52em;height:4em;width:100%;resize:vertical;line-height:1.45}.bm-W textarea::placeholder,.bm-W input::placeholder{color:#2f446699}.bm-W a:not(:has(*)){color:#1558a4f0;text-decoration:underline;text-decoration-color:#1558a459}.bm-W small{font-size:x-small;font-family:var(--bm-font-display);letter-spacing:.12em;color:var(--bm-text-secondary)}.bm-W ul li{list-style:disc;margin-left:5ch}.bm-W .bm-L.bm-H{max-height:var(--bm-H-max-height, calc(80vh - 150px) );overflow:auto;scrollbar-width:thin;scrollbar-color:rgba(146,221,255,.42) rgba(255,255,255,.05)}.bm-W .bm-L.bm-H::-webkit-scrollbar{width:10px;height:10px}.bm-W .bm-L.bm-H::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#74e7ff7a,#538dff66);border-radius:999px;border:2px solid transparent;background-clip:padding-box}.bm-W .bm-L.bm-H::-webkit-scrollbar-track{background:#ffffff0a;border-radius:999px}.bm-x{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.35ch}.bm-D{display:flex;align-content:center;justify-content:center;align-items:center;gap:.35ch}.bm-_{white-space:pre;letter-spacing:0;line-height:1!important;font-size:1.6em;font-family:monospace}.bm-N .bm-L:not(#bm-F .bm-L){margin-top:.18em;margin-bottom:.18em}.bm-N h1:not(#bm-F h1){font-size:1em}#bm-t .bm-1z{display:block;width:1.28em;height:1.28em;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;overflow:visible}#bm-t p svg{display:inline-block;height:1em;width:1em;color:currentColor;vertical-align:-.16em}#bm-t:not(.bm-N){width:min(50rem,calc(100vw - .55rem));max-width:min(50rem,calc(100vw - .55rem))!important}#bm-t .bm-1N{padding-top:.08rem}#bm-t .bm-1F{gap:.22rem;flex-wrap:wrap;width:100%;padding:.16rem}#bm-t .bm-1F>button{flex:1 1 10rem}#bm-t .bm-1r{padding-right:.08rem}#bm-t .bm-1A{display:grid;grid-template-columns:minmax(16rem,20rem) minmax(0,1fr);gap:.24rem .3rem;align-items:stretch}#bm-t .bm-1A>hr,#bm-t .bm-1t{grid-column:1 / -1}#bm-t .bm-1s,#bm-t .bm-20,#bm-t .bm-1t{padding:.38rem .48rem;border-radius:13px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(155deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29,0 8px 20px #00000014}#bm-t .bm-1s{display:grid;gap:.18rem}#bm-t .bm-1s br{display:none}#bm-t .bm-1s span{display:block;padding:.28rem .38rem;border-radius:10px;background:linear-gradient(180deg,#fff3,#ffffff14)}#bm-t .bm-20 p{margin:0}#bm-t .bm-1t fieldset{border:none;padding:0;margin:0}#bm-t .bm-1t legend{margin-bottom:.12rem}#bm-t .bm-1t .bm-L{margin-top:.1rem;margin-bottom:.1rem}#bm-t .bm-1l{display:flex;justify-content:flex-start}#bm-t .bm-1l button{min-width:7.8rem}#bm-E{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;align-items:stretch;gap:.3rem}#bm-t .bm-z{position:relative;overflow:hidden;width:fit-content;max-width:35ch;padding:.28rem;gap:.28rem;border-radius:13px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(160deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29,0 8px 20px #00000014;transition:background .25s ease,border-color .25s ease,box-shadow .25s ease,transform .25s ease}#bm-t .bm-z:before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at top right,rgba(255,255,255,.18),transparent 24%),radial-gradient(circle at 18% 0%,rgba(186,246,255,.12),transparent 22%)}#bm-t .bm-z:hover,#bm-t .bm-z:focus-within{transform:translateY(-2px);border-color:#92ddff42;background:linear-gradient(160deg,#ffffff38,#baf6ff1a);box-shadow:inset 0 1px #fff3,0 10px 24px #0000001a}#bm-t .bm-a{display:block;width:fit-content;height:fit-content;padding:.26rem;border:1px solid rgba(255,255,255,.18);border-radius:10px;box-shadow:inset 0 1px #ffffff24,0 6px 16px #00000014}#bm-t .bm-z[data-id="-2"] .bm-a{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}#bm-t .bm-z[data-id="-1"] .bm-a{background:url('data:image/svg+xml;utf8, ') repeat;background-color:transparent!important}#bm-t .bm-z[data-id="-1"] .bm-a svg{color:#fff!important}#bm-t .bm-z[data-id="0"] .bm-a{background-color:transparent!important}#bm-t .bm-a button{display:inline-flex;align-items:center;justify-content:center;padding:.24em;min-width:2.05rem;min-height:2.05rem;border-radius:999px;line-height:1}#bm-t .bm-a .bm-1z{width:1.55rem;height:1.55rem;filter:drop-shadow(0 1px 0 rgba(255,255,255,.18))}#bm-t .bm-z>.bm-x{flex-direction:column;align-items:flex-start;gap:.02rem}#bm-t .bm-z h2{margin:.04rem 0 0}#bm-t .bm-z .bm-6{margin:.1rem 0 0}#bm-t .bm-z small{font-size:.75em}#bm-t .bm-z.bm-I{display:none}#bm-t.bm-N{--bm-H-max-height: 100%;display:grid;grid-template-rows:auto minmax(0,1fr);width:360px;height:min(60vh,22rem);min-width:360px;min-height:180px;max-width:min(1000px,calc(100vw - 16px))!important;max-height:min(1400px,calc(100vh - 16px))!important;overflow:hidden;box-sizing:border-box;position:fixed;transition:background .32s ease,border-color .22s ease,box-shadow .22s ease,transform 0s}#bm-t.bm-N .bm-m{display:grid;grid-template-rows:auto auto auto auto minmax(0,1fr);grid-row:2;min-height:0;min-width:0;overflow:hidden}#bm-t.bm-N .bm-1F{gap:.16rem;flex-wrap:nowrap;width:100%;padding:.12rem}#bm-t.bm-N .bm-1F>button{flex:1 1 0;min-width:0}#bm-t.bm-N #bm-E{flex-direction:column;align-items:stretch;gap:.16rem;width:100%;align-self:stretch;min-width:0;box-sizing:border-box}#bm-t.bm-N .bm-z{width:100%;max-width:none;align-self:stretch;flex:1 1 auto;min-width:0;margin:0;padding:.12rem;border-radius:11px;box-sizing:border-box}#bm-t.bm-N .bm-z>.bm-x{width:100%;min-width:0;flex:1 1 auto}#bm-t.bm-N .bm-L.bm-H{display:block;grid-row:5;min-height:0;min-width:0;height:100%;width:100%;max-height:100%!important;overflow:auto;box-sizing:border-box}#bm-t.bm-N .bm-1O{position:absolute;right:4px;bottom:4px;display:flex;width:20px;height:20px;align-items:flex-end;justify-content:flex-end;padding-right:4px;padding-bottom:4px;box-sizing:border-box;z-index:5;cursor:nwse-resize;pointer-events:auto;opacity:.78;touch-action:none;user-select:none;color:#ffffffdb;background:linear-gradient(135deg,#ffffff24,#538dff24);border:1px solid rgba(255,255,255,.12);border-radius:8px;box-shadow:inset 0 1px #ffffff14}#bm-t.bm-N .bm-1O:hover,#bm-t.bm-N .bm-1O.bm-2g{opacity:1;border-color:#92ddff5c;box-shadow:inset 0 1px #ffffff1a,0 8px 16px #00000029}#bm-t.bm-N .bm-a{display:flex;width:100%;min-width:0;flex:1 1 auto;gap:.5ch;align-items:center;padding:.1rem .2rem;border:none;border-radius:8px;box-sizing:border-box}#bm-t.bm-N .bm-a button{padding:.2em;flex:0 0 auto}#bm-t.bm-N .bm-a .bm-1z{width:1.36rem;height:1.36rem}#bm-t.bm-N .bm-z h2{font-size:.78rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#bm-t #bm-2{display:inline-flex;align-items:center;padding:.08rem .24rem;border-radius:999px;background:#ffffff1f;box-shadow:inset 0 1px #ffffff14;font-size:1em}@media (max-width: 980px){#bm-t:not(.bm-N){width:min(100vw - .35rem,50rem);max-width:min(100vw - .35rem,50rem)!important}#bm-t .bm-1A{grid-template-columns:1fr}#bm-t .bm-20,#bm-t .bm-1t,#bm-t .bm-1A>hr{grid-column:auto}}#bm-F{width:min(25.5rem,calc(100vw - .65rem));max-width:min(25.5rem,calc(100vw - .65rem))!important}#bm-F .bm-m{display:flex;flex-direction:column}#bm-F .bm-2d,#bm-F .bm-29{position:relative;overflow:hidden;border-radius:14px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(150deg,#ffffff2e,#ffffff14);box-shadow:inset 0 1px #fff3,0 10px 24px #0000001a}#bm-F .bm-2d:before,#bm-F .bm-29:before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at top right,rgba(255,255,255,.2),transparent 26%),radial-gradient(circle at 20% 0%,rgba(186,246,255,.16),transparent 24%)}#bm-F .bm-2d{display:flex;align-items:center;gap:.5rem;padding:.48rem .58rem}#bm-F .bm-2d h1{margin:0}#bm-F .bm-28{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.24rem}#bm-F .bm-1I{min-width:0;min-height:2.55rem;display:flex;align-items:flex-start;padding:.34rem .45rem;border-radius:11px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(160deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29;color:#12233ff5;line-height:1.22}#bm-F .bm-1I>span,#bm-F .bm-1I>time{min-width:0}#bm-F .bm-1h,#bm-F .bm-1i{flex-direction:column;justify-content:center;gap:.08rem}#bm-F .bm-1C{display:inline-block;font-size:1.3em;color:#0f1f38f5;white-space:normal;overflow-wrap:anywhere}#bm-F .bm-1C b{font-size:1em}#bm-F .bm-1B{color:#314769d1;font-size:.62rem;letter-spacing:.08em;font-family:var(--bm-font-display);text-transform:uppercase}#bm-F .bm-1I time{white-space:nowrap;font-family:var(--bm-font-mono);letter-spacing:.06em}#bm-F .bm-29{padding:.48rem}#bm-F .bm-22{display:grid;grid-template-columns:auto repeat(4,minmax(0,1fr));gap:.22rem;align-items:center}#bm-F .bm-22 .bm-J{width:1.8rem;height:1.8rem}#bm-F .bm-22 .bm-C{width:100%;margin-left:0;text-align:center}#bm-F .bm-23,#bm-F .bm-24{margin-top:.24rem}#bm-F .bm-23>div{width:100%}#bm-F .bm-1Y{gap:.24rem;margin-top:.3rem}#bm-F .bm-1Y>button{flex:1 1 0}#bm-F .bm-24 textarea{min-height:4.1rem}@media (max-width: 720px){#bm-F{width:min(100vw - .45rem,25.5rem);max-width:min(100vw - .45rem,25.5rem)!important}#bm-F .bm-28{grid-template-columns:1fr}#bm-F .bm-22{grid-template-columns:repeat(2,minmax(0,1fr))}#bm-F .bm-22 .bm-J{grid-column:1 / -1;width:100%;aspect-ratio:auto;height:1.8rem}#bm-F .bm-1Y{flex-direction:column}}#bm-l div:has(>.bm-3){width:fit-content;justify-content:flex-start}#bm-l .bm-3{display:flex;flex-direction:column;width:13%}#bm-l .bm-3 span{width:fit-content;margin:auto;font-size:.7em}#bm-l .bm-3 button{width:fit-content;padding:0;border-radius:0}#bm-l .bm-3 svg{stroke:#333;stroke-width:.02px;width:100%;min-width:1.5ch;max-width:14.5ch}#bm-l .bm-3 button:hover svg,#bm-l .bm-3 button:focus svg{opacity:.9}#bm-l .bm-n{display:grid;grid-template-columns:1fr 1fr 1fr;width:25%;min-width:3ch;max-width:15ch}#bm-l .bm-n>button{width:100%;padding:0;aspect-ratio:1 / 1;background-color:#fff;border:#333 1px solid;border-radius:0;box-sizing:border-box}#bm-l .bm-n>button[data-status=Incorrect]{background-color:brown}#bm-l .bm-n>button[data-status=Template]{background-color:#2f4f4f}#bm-l .bm-n>button:hover,#bm-l .bm-n>button:focus{opacity:.8}#bm-B{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-start}#bm-B>.bm-L{width:100%;justify-content:flex-start;background-color:#153063e6;border-radius:1em;padding:.5em;transition:background-color .3s ease}#bm-B>.bm-L:hover,#bm-B>.bm-L:focus-within{background-color:#112855e6}#bm-B .bm-1{height:100%;font-size:xxx-large}#bm-B .bm-0{flex-direction:column;align-items:flex-start;gap:0}`;function $t(t){const e=document.createElement("link");e.href=t,e.rel="preload",e.as="style",e.onload=function(){this.onload=null,this.rel="stylesheet"},document.head?.appendChild(e)}GM_addStyle(yt);var vt="@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:400;src:url(data:font/woff2;base64,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)format('woff2');}";$t("https://fonts.googleapis.com/css2?family=Michroma&family=Rajdhani:wght@400;500;600;700&display=swap"),vt.indexOf("@font-face")+1?(console.log("Loading Roboto Mono as a file..."),GM_addStyle(vt)):$t("https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap");var Mt=JSON.parse(GM_getValue("bmUserSettings","{}")),Ct=(new class{constructor(){this.Ke=null,this.Qe=null,this.ti="#bm-p"}ei(t){return this.Qe=t,this.Ke=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.ti)}),this}ii(){return this.Ke}observe(t,e=!1,i=!1){t.observe(this.Qe,{childList:e,subtree:i})}},new class extends v{constructor(t,i){super(t,i),e(this,Q),this.window=null,this.Ct="bm-F",this.Tt=document.body}St(){document.querySelector(`#${this.Ct}`)?this.$t("Main window already exists!"):(this.window=this.L({id:this.Ct,class:"bm-W bm-N",style:"top: 10px; left: unset; right: 75px;"},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Blue Marble"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',title:"Settings","aria-label":"Open settings"},(t,e)=>{e.onclick=()=>{t.$.St()}}).D().D().D().L({class:"bm-m"}).F().D().L({class:"bm-L bm-2d"}).A({class:"bm-T",src:"data:image/png;base64,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"},(t,e)=>{const i=new Date;204==Math.floor((i.getTime()-new Date(i.getFullYear(),0,1))/864e5)+1&&(e.parentNode.style.position="relative",e.parentNode.innerHTML=e.parentNode.innerHTML+' ',e.onload=()=>{(new O).qt(document.querySelector(`#${this.Ct}`))})}).D().W(1,{textContent:this.name}).D().D().F().D().L({class:"bm-L bm-28"}).L({class:"bm-1I bm-1h"}).I({class:"bm-1B",textContent:"Droplets"}).D().I({id:"bm-w",class:"bm-1C",textContent:"0"}).D().D().L({class:"bm-1I bm-1h"}).I({class:"bm-1B",textContent:"Next Level"}).D().I({id:"bm-q",class:"bm-1C",textContent:"0 px"}).D().D().L({class:"bm-1I bm-1i"}).I({class:"bm-1B",textContent:"Charges"}).D().gt(Date.now(),1e3,{class:"bm-1C",style:"font-weight: 700;"},(t,e)=>{t.p.si=e.id}).D().D().D().F().D().L({class:"bm-L bm-29"}).L({class:"bm-L bm-22"}).lt({class:"bm-s bm-J",style:"margin-top: 0;",innerHTML:' '},(t,e)=>{e.onclick=()=>{const e=t.p?.ni;e?.[0]?(t.ht("bm-Q",e?.[0]||""),t.ht("bm-R",e?.[1]||""),t.ht("bm-O",e?.[2]||""),t.ht("bm-P",e?.[3]||"")):t.$t("Coordinates are malformed! Did you try clicking on the canvas first?")}}).D().dt({type:"number",id:"bm-Q",class:"bm-C",placeholder:"Tl X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-R",class:"bm-C",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-O",class:"bm-C",placeholder:"Px X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-P",class:"bm-C",placeholder:"Px Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().D().L({class:"bm-L bm-23"}).ut({class:"bm-K",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).D().D().L({class:"bm-L bm-x bm-1Y"}).lt({class:"bm-1u",textContent:"Disable","data-button-status":"shown"},(t,e)=>{e.onclick=()=>{e.disabled=!0,"shown"==e.dataset.buttonStatus?(t.p?.ce?.oi(!1),e.dataset.buttonStatus="hidden",e.textContent="Enable",t.Mt("Disabled templates!")):(t.p?.ce?.oi(!0),e.dataset.buttonStatus="shown",e.textContent="Disable",t.Mt("Enabled templates!")),e.disabled=!1}}).D().lt({class:"bm-1G",textContent:"Create"},(t,e)=>{e.onclick=()=>{const e=document.querySelector(`#${this.Ct} .bm-K`),i=document.querySelector("#bm-Q");if(!i.checkValidity())return i.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-R");if(!s.checkValidity())return s.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const n=document.querySelector("#bm-O");if(!n.checkValidity())return n.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-P");if(!o.checkValidity())return o.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(t?.p?.ce.Ue(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(i.value),Number(s.value),Number(n.value),Number(o.value)]),t.Mt("Drew to canvas!")):t.$t("No file selected!")}}).D().lt({class:"bm-1u",textContent:"Filter"},(t,e)=>{e.onclick=()=>this.ai()}).D().D().L({class:"bm-L bm-24"}).bt({id:this.v,placeholder:`Status: Sleeping...\nVersion: ${this.version}`,readOnly:!0}).D().D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}ai(){new rt(this).ye()}}(wt,xt)),Tt=new class{constructor(t,i){e(this,ct),this.name=t,this.version=i,this.Re=null,this.$=null,this.schemaVersion="2.0.0",this.ri=null,this.We="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.Bt=1e3,this.Ge=3,this._e=3,this.de=function(t){const e=y;e.unshift({id:-1,premium:!1,name:"Erased",rgb:[222,250,206]}),e.unshift({id:-2,premium:!1,name:"Other",rgb:[0,0,0]});const i=new Map;for(const s of e){if(0==s.id||-2==s.id)continue;const e=s.rgb[0],n=s.rgb[1],o=s.rgb[2];for(let a=-t;a<=t;a++)for(let r=-t;r<=t;r++)for(let l=-t;l<=t;l++){const t=e+a,c=n+r,h=o+l;if(t<0||t>255||c<0||c>255||h<0||h>255)continue;const m=(255<<24|h<<16|c<<8|t)>>>0;i.has(m)||i.set(m,s.id)}}return{palette:e,Jt:i}}(this._e),this.Ee=null,this.li="",this.Ie=[],this.Ve=null,this.ci=!0,this.hi=null,this.He=new Map}mi(t){this.Re=t}k(t){this.$=t,i(this,ct,ht).call(this)}Oe(t,e){const s=Number(t);Number.isFinite(s)&&(e?this.He.set(s,!0):this.He.delete(s),i(this,ct,mt).call(this))}async di(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.schemaVersion,templates:{}}}async Ue(t,e,s){this.Ve||(this.Ve=await this.di(),console.log("Creating JSON...")),this.Re.Mt(`Creating template at ${s.join(", ")}...`);const n=new H({displayName:e,Lt:0,Ht:m(this.ri||0,this.We),file:t,coords:s}),o=!this.$?.Nt?.flags?.includes("hl-noSkip"),a=this.$?.Nt?.flags?.includes("hl-agSkip");console.log(`Should Skip: ${o}; Should Agg Skip: ${a}`);const{jt:r,Et:l}=await n.Wt(this.Bt,this.de,o,a);n.Ot=r;const c={total:n.Pt.total,colors:Object.fromEntries(n.Pt.colors)};this.Ve.templates[`${n.Lt} ${n.Ht}`]={name:n.displayName,coords:s.join(", "),enabled:!0,pixels:c,tiles:l},this.Ie=[],this.Ie.push(n),this.Re.Mt(`Template created at ${s.join(", ")}!`),console.log(Object.keys(this.Ve.templates).length),console.log(this.Ve),console.log(this.Ie),console.log(JSON.stringify(this.Ve)),await i(this,ct,dt).call(this)}ui(){}async bi(){this.Ve||(this.Ve=await this.di(),console.log("Creating JSON..."))}async pi(){l("Downloading all templates..."),console.log(this.Ie);for(const t of this.Ie)await this.fi(t),await s(500)}async ze(){const t=JSON.parse(GM_getValue("bmTemplates","{}"))?.templates;if(console.log(t),Object.keys(t).length>0)for(const[e,i]of Object.entries(t))t.hasOwnProperty(e)&&(await this.fi(new H({displayName:i.name,Lt:e.split(" ")?.[0],Ht:e.split(" ")?.[1],Ot:i.tiles})),await s(500))}async fi(t){t.Yt();const e=`${t.coords.join("-")}_${t.displayName.replaceAll(" ","-")}`,i=await this.Fe(t);await GM.download({url:URL.createObjectURL(i),name:e+".png",gi:"uniquify",onload:()=>{l(`Download of template '${e}' complete!`)},onerror:(t,i)=>{c(`Download of template '${e}' failed because ${t}! Details: ${i}`)},ontimeout:()=>{h(`Download of template '${e}' has timed out!`)}})}async Fe(t){console.log(t);const e=t.Ot,i=Object.keys(e).sort(),s=await Promise.all(i.map(t=>{return i=e[t],new Promise((t,e)=>{const s=new Image;s.onload=()=>t(s),s.onerror=e,s.src="data:image/png;base64,"+i});var i}));let n=1/0,o=1/0,a=0,r=0;i.forEach((t,e)=>{const[i,l,c,h]=t.split(",").map(Number),m=s[e],d=i*this.Bt+c,u=l*this.Bt+h;n=Math.min(n,d),o=Math.min(o,u),a=Math.max(a,d+m.width/this.Ge),r=Math.max(r,u+m.height/this.Ge)}),console.log(`Absolute coordinates: (${n}, ${o}) and (${a}, ${r})`);const l=a-n,c=r-o,h=l*this.Ge,m=c*this.Ge;console.log(`Template Width: ${l}\nTemplate Height: ${c}\nCanvas Width: ${h}\nCanvas Height: ${m}`);const d=new OffscreenCanvas(h,m),u=d.getContext("2d");i.forEach((t,e)=>{const[i,a,r,l]=t.split(",").map(Number),c=s[e],h=i*this.Bt+r,m=a*this.Bt+l;console.log(`Drawing tile (${i}, ${a}, ${r}, ${l}) (${h}, ${m}) at (${h-n}, ${m-o}) on the canvas...`),u.drawImage(c,(h-n)*this.Ge,(m-o)*this.Ge,c.width,c.height)}),u.globalCompositeOperation="destination-over",u.drawImage(d,0,-1),u.drawImage(d,0,1),u.drawImage(d,-1,0),u.drawImage(d,1,0);const b=new OffscreenCanvas(l,c),p=b.getContext("2d");return p.imageSmoothingEnabled=!1,p.drawImage(d,0,0,l*this.Ge,c*this.Ge,0,0,l,c),b.convertToBlob({type:"image/png"})}async wi(t,e){if(!this.ci)return t;const s=this.Bt*this.Ge;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0"),console.log(`Searching for templates in tile: "${e}"`);const o=this.Ie;console.log(o),o.sort((t,e)=>t.Lt-e.Lt),console.log(o);const a=o.map(t=>{const i=Object.keys(t.Ot).filter(t=>t.startsWith(e));if(0===i.length)return null;const s=i.map(e=>{const i=e.split(",");return{xi:t,Ut:t.Ot[e],It:t.It?.[e],yi:[i[0],i[1]],$i:[i[2],i[3]]}});return s?.[0]}).filter(Boolean);console.log(a);const r=a?.length||0;if(console.log(`templateCount = ${r}`),!(r>0))return this.Re.Mt(`Sleeping\nVersion: ${this.version}`),t;{const t=n(o.filter(t=>Object.keys(t.Ot).filter(t=>t.startsWith(e)).length>0).reduce((t,e)=>t+(e.Pt.total||0),0));this.Re.Mt(`Displaying ${r} template${1==r?"":"s"}.\nTotal pixels: ${t}`)}const l=await createImageBitmap(t),c=new OffscreenCanvas(s,s),h=c.getContext("2d");h.imageSmoothingEnabled=!1,h.beginPath(),h.rect(0,0,s,s),h.clip(),h.clearRect(0,0,s,s),h.drawImage(l,0,0,s,s);const m=h.getImageData(0,0,s,s),d=new Uint32Array(m.data.buffer),u=this.$?.Nt?.highlight||[[2,0,0]],b=u?.[0],p=1==u?.length&&2==b?.[0]&&0==b?.[1]&&0==b?.[2];for(const t of a){console.log("Template:"),console.log(t);const s=!!t.xi.Pt?.colors?.get(-1);let n=t.It.slice();const o=Number(t.$i[0])*this.Ge,a=Number(t.$i[1])*this.Ge;if(0!=this.He.size||s||h.drawImage(t.Ut,o,a),!n){const e=h.getImageData(o,a,t.Ut.width,t.Ut.height);n=new Uint32Array(e.data.buffer)}const r=Date.now(),{qe:l,Ze:c}=i(this,ct,bt).call(this,{je:d,Ee:n,Ye:[o,a,t.Ut.width,t.Ut.height],Je:u,Xe:p});let m=0;const b=0;for(const[t,e]of l)t!=b&&(m+=e);0==this.He.size&&!s&&p||(console.log("Colors to filter: ",this.He),h.drawImage(await createImageBitmap(new ImageData(new Uint8ClampedArray(c.buffer),t.Ut.width,t.Ut.height)),o,a)),console.log(`Finished calculating correct pixels & filtering colors for the tile ${e} in ${(Date.now()-r)/1e3} seconds!\nThere are ${m} correct pixels.`),void 0===t.xi.Pt.correct&&(t.xi.Pt.correct={}),t.xi.Pt.correct[e]=l}return await c.convertToBlob({type:"image/png"})}async Mi(t){console.log("Importing JSON..."),console.log(t),"BlueMarble"==t?.whoami&&await i(this,ct,ut).call(this,t)}oi(t){this.ci=t}}(wt,xt),St=new class{constructor(t){this.ce=t,this.Ci=!1,this.si="",this.ni=[],this.Ti=[]}Si(t){window.addEventListener("message",async e=>{const i=e.data,s=i.jsonData;if(!i||"blue-marble"!==i.source)return;if(!i.endpoint)return;const n=i.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(console.log('%cBlue Marble%c: Recieved message about "%s"',"color: cornflowerblue;","",n),n){case"me":this.ki(t,s);break;case"pixel":const e=i.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),n=new URLSearchParams(i.endpoint.split("?")[1]),r=[n.get("x"),n.get("y")];if(this.ni.length&&(!e.length||!r.length))return void t.$t("Coordinates are malformed!\nDid you try clicking the canvas first?");this.ni=[...e,...r];const l=(o=e,a=r,[parseInt(o[0])%4*1e3+parseInt(a[0]),parseInt(o[1])%4*1e3+parseInt(a[1])]),c=document.querySelectorAll("span");for(const t of c){const i=t.textContent.trim();if(i.includes(l[0])&&i.includes(l[1])){let i=document.querySelector("#bm-p");e[0],e[1],r[0],r[1];const s=["Tl X:","Tl Y:","Px X:","Px Y:"],n=["bm-Y","bm-Z","bm-U","bm-V"],o=[...e,...r];if(i)for(const[t,e]of n.entries())document.getElementById(e).textContent=`${s[t]??"??:"} ${o[t]}`;else{i=document.createElement("span"),i.id="bm-p",i.style="display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;";for(const[t,e]of o.entries()){const a=document.createElement("span");a.id=n[o.indexOf(e)??""],a.textContent=`${s[t]??"??:"} ${e}`,i.appendChild(a)}t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",i)}}}break;case"tile":case"tiles":let h=i.endpoint.split("/");h=[parseInt(h[h.length-2]),parseInt(h[h.length-1].replace(".png",""))];const m=i.blobID,d=i.blobData,u=Date.now(),b=await this.ce.wi(d,h);console.log(`Finished loading the tile in ${(Date.now()-u)/1e3} seconds!`),window.postMessage({source:"blue-marble",blobID:m,blobData:b,blink:i.blink});break;case"robots":this.Ci="false"==s.userscript?.toString().toLowerCase();break}var o,a})}ki(t,e){if(e.status&&"2"!=e.status?.toString()[0])return void t.$t("You are not logged in or Wplace is offline!\nCould not fetch userdata.");const i=Math.ceil(Math.pow(Math.floor(e.level)*Math.pow(30,.65),1/.65)-e.pixelsPainted);if(console.log(e.id),(e.id||0===e.id)&&console.log(m(e.id,"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")),this.ce.ri=e.id,0!=this.si.length){const t=document.querySelector("#"+this.si);if(t){const i=e.charges;t.dataset.endDate=Date.now()+(i.max-i.count)*i.cooldownMs}}t.ht("bm-w",`${n(e.droplets)} `),t.ht("bm-q",`${n(i)} px`)}async Di(t){try{const e=await fetch(`${window.location.origin}/api/me`,{credentials:"include"});if(!e.ok)return void t.$t(`Could not fetch userdata.\nHTTP ${e.status}`);const i=await e.json();this.ki(t,i)}catch(t){c("Failed to fetch current user data:",t)}}Ni(t){try{const e=sessionStorage.getItem("bm-2m");if(!e)return!1;const i=JSON.parse(e);return this.ki(t,i),!0}catch(t){return c("Failed to apply cached user data:",t),!1}}async Li(t){console.log("Sending heartbeat to telemetry server...");let e=GM_getValue("bmUserSettings","{}");if(e=JSON.parse(e),!e||!e.telemetry||!e.uuid)return void console.log("Telemetry is disabled, not sending heartbeat.");const i=navigator.userAgent;let s=await this.Hi(i),n=this.Oi(i);GM_xmlhttpRequest({method:"POST",url:"https://telemetry.thebluecorner.net/heartbeat",headers:{"Content-Type":"application/json"},data:JSON.stringify({uuid:e.uuid,version:t,browser:s,os:n}),onload:t=>{200!==t.status&&c("Failed to send heartbeat:",t.statusText)},onerror:t=>{c("Error sending heartbeat:",t)}})}async Hi(t=navigator.userAgent){return(t=t||"").includes("OPR/")||t.includes("Opera")?"Opera":t.includes("Edg/")?"Edge":t.includes("Vivaldi")?"Vivaldi":t.includes("YaBrowser")?"Yandex":t.includes("Kiwi")?"Kiwi":t.includes("Brave")?"Brave":t.includes("Firefox/")?"Firefox":t.includes("Chrome/")?"Chrome":t.includes("Safari/")?"Safari":navigator.brave&&"function"==typeof navigator.brave.isBrave&&await navigator.brave.isBrave()?"Brave":"Unknown"}Oi(t=navigator.userAgent){return/Windows NT 11/i.test(t=t||"")?"Windows 11":/Windows NT 10/i.test(t)?"Windows 10":/Windows NT 6\.3/i.test(t)?"Windows 8.1":/Windows NT 6\.2/i.test(t)?"Windows 8":/Windows NT 6\.1/i.test(t)?"Windows 7":/Windows NT 6\.0/i.test(t)?"Windows Vista":/Windows NT 5\.1|Windows XP/i.test(t)?"Windows XP":/Mac OS X 10[_\.]15/i.test(t)?"macOS Catalina":/Mac OS X 10[_\.]14/i.test(t)?"macOS Mojave":/Mac OS X 10[_\.]13/i.test(t)?"macOS High Sierra":/Mac OS X 10[_\.]12/i.test(t)?"macOS Sierra":/Mac OS X 10[_\.]11/i.test(t)?"OS X El Capitan":/Mac OS X 10[_\.]10/i.test(t)?"OS X Yosemite":/Mac OS X 10[_\.]/i.test(t)?"macOS":/Android/i.test(t)?"Android":/iPhone|iPad|iPod/i.test(t)?"iOS":/Linux/i.test(t)?"Linux":"Unknown"}}(Tt),kt=new class extends L{constructor(t,i,s){var n;super(t,i),e(this,T),this.Nt=s,(n=this.Nt).flags??(n.flags=[]),this.Ii=structuredClone(this.Nt),this.Bi="bmUserSettings",this.Pi=5e3,this.Ai=0,setInterval(this.zi.bind(this),this.Pi)}async zi(){await this.Wi()}async Wi(t=!1){const e=JSON.stringify(this.Nt);e!=JSON.stringify(this.Ii)&&(t||Date.now()-this.Ai>this.Pi)&&(await GM.setValue(this.Bi,e),this.Ii=structuredClone(this.Nt),this.Ai=Date.now(),console.log(e))}async Le(){await this.Wi(!0)}Ne(t,e=void 0){const i=this.Nt?.flags?.indexOf(t)??-1;-1!=i&&!0!==e?this.Nt?.flags?.splice(i,1):-1==i&&!1!==e&&this.Nt?.flags?.push(t)}kt(){const t=' ',e=' ',s=this.Nt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];this.window=this.L({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().F().D().L({class:"bm-L",style:"margin-left: 1.5ch;"}).j({textContent:"Highlight transparent pixels"},(t,e,i)=>{i.checked=!this.Nt?.flags?.includes("hl-noTrans"),i.onchange=t=>this.Ne("hl-noTrans",!t.target.checked)}).D().H({id:"bm-4",textContent:"Choose a preset:",style:"font-weight: 700;"}).D().L({class:"bm-D",role:"group","aria-labelledby":"bm-4"}).L({class:"bm-3"}).I({textContent:"None"}).D().lt({innerHTML:t,"aria-label":'Preset "None"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"None")}).D().D().L({class:"bm-3"}).I({textContent:"Cross"}).D().lt({innerHTML:e,"aria-label":'Preset "Cross Shape"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"Cross")}).D().D().L({class:"bm-3"}).I({textContent:"X"}).D().lt({innerHTML:e.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"','d="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'),"aria-label":'Preset "X Shape"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"X")}).D().D().L({class:"bm-3"}).I({textContent:"Full"}).D().lt({innerHTML:t.replace("#fff","#2f4f4f"),"aria-label":'Preset "Full Template"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"Full")}).D().D().D().H({id:"bm-b",textContent:"Create a custom pattern:",style:"font-weight: 700;"}).D().L({class:"bm-n",role:"group","aria-labelledby":"bm-b"});for(let t=-1;t<=1;t++)for(let e=-1;e<=1;e++){const n=s[s.findIndex(([,i,s])=>i==e&&s==t)]?.[0]??0;let o="Disabled";1==n?o="Incorrect":2==n&&(o="Template"),this.window=this.lt({"data-status":o,"aria-label":`Sub-pixel ${o.toLowerCase()}`},(s,n)=>{n.onclick=()=>i(this,T,S).call(this,n,[e,t])}).D()}this.window=this.D().D().D()}Dt(){this.window=this.L({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().F().D().L({class:"bm-L",style:"margin-left: 1.5ch;"}).j({textContent:"Template creation should skip transparent tiles"},(t,e,i)=>{i.checked=!this.Nt?.flags?.includes("hl-noSkip"),i.onchange=t=>this.Ne("hl-noSkip",!t.target.checked)}).D().j({innerHTML:"Experimental: Template creation should aggressively skip transparent tiles"},(t,e,i)=>{i.checked=this.Nt?.flags?.includes("hl-agSkip"),i.onchange=t=>this.Ne("hl-agSkip",t.target.checked)}).D().D().D()}}(wt,xt,Mt);Ct.k(kt),Ct.S(St),Tt.mi(Ct),Tt.k(kt);var Dt=JSON.parse(GM_getValue("bmTemplates","{}"));if(console.log(Dt),console.log(Mt),console.log(Object.keys(Mt).length),0==Object.keys(Mt).length){const t=crypto.randomUUID();console.log(t),GM.setValue("bmUserSettings",JSON.stringify({uuid:t}))}setInterval(()=>St.Li(xt),18e5);var Nt=Mt?.telemetry;if(console.log(`Telemetry is ${!(null==Nt)}`),null==Nt||Nt>1){const t=new class extends v{constructor(t,i,s,n){super(t,i),e(this,pt),this.window=null,this.Ct="bm-k",this.Tt=document.body,this.Fi=s,this.uuid=n}async St(){if(document.querySelector(`#${this.Ct}`))return void this.$t("Telemetry window already exists!");const t=await this.p.Hi(navigator.userAgent),e=this.p.Oi(navigator.userAgent);this.window=this.L({id:this.Ct,class:"bm-W",style:"height: 80vh; z-index: 9998;"}).L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:`${this.name} Telemetry`}).D().D().F().D().L({class:"bm-L bm-D",style:"gap: 1.5ch; flex-wrap: wrap;"}).lt({textContent:"Enable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,pt,ft).call(this,this.Fi);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"Disable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,pt,ft).call(this,0);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"More Information"},(t,e)=>{e.onclick=()=>{window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data","_blank","noopener noreferrer")}}).D().D().L({class:"bm-L bm-H"}).L({class:"bm-L"}).W(2,{textContent:"Legal"}).D().H({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}!`}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Non-Legal Summary"}).D().H({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. This is the data stored on our servers:'}).D()._().Z({innerHTML:`A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID. Your UUID is: ${r(this.uuid)} `}).D().Z({innerHTML:`The version of Blue Marble you are using. Your version is: ${r(this.version)} `}).D().Z({innerHTML:`Your browser type, which is used to determine Blue Marble outages and browser popularity. Your browser type is: ${r(t)} `}).D().Z({innerHTML:`Your OS type, which is used to determine Blue Marble outages and OS popularity. Your OS type is: ${r(e)} `}).D().Z({innerHTML:"The date and time that Blue Marble sent the telemetry information."}).D().D().H({innerHTML:'All of the data mentioned above is aggregated every hour . 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.'}).D().D().D().D().D().N(this.Tt)}}(wt,xt,1,Mt?.uuid);t.S(St),t.St()}!async function(){await Tt.Mi(Dt),St.Si(Ct),Ct.St(),Ct.ai(),St.Ni(Ct),St.Di(Ct),new MutationObserver((t,e)=>{const i=document.querySelector("#color-1");if(!i)return;let s=document.querySelector("#bm-G");if(!s){s=document.createElement("button"),s.id="bm-G",s.textContent="Move ↑",s.className="btn btn-soft",s.onclick=function(){const t=this.parentNode.parentNode.parentNode.parentNode,e="Move ↑"==this.textContent;t.parentNode.className=t.parentNode.className.replace(e?"bottom":"top",e?"top":"bottom"),t.style.borderTopLeftRadius=e?"0px":"var(--radius-box)",t.style.borderTopRightRadius=e?"0px":"var(--radius-box)",t.style.borderBottomLeftRadius=e?"var(--radius-box)":"0px",t.style.borderBottomRightRadius=e?"var(--radius-box)":"0px",this.textContent=e?"Move ↓":"Move ↑"};const t=i.parentNode.parentNode.parentNode.parentNode.querySelector("h2");t.parentNode?.appendChild(s)}}).observe(document.body,{childList:!0,subtree:!0}),l(`%c${wt}%c (${xt}) userscript has loaded!`,"color: cornflowerblue;","")}()})();
+
+// Build Hash: cb988ac65208
diff --git a/dist/BlueMarble.user.css b/dist/BlueMarble.user.css
index e2e47fa..385f24c 100644
--- a/dist/BlueMarble.user.css
+++ b/dist/BlueMarble.user.css
@@ -1 +1,3 @@
-#bm-t p svg{display:inline;height:1em;fill:#fff}#bm-E{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;gap:1em 3ch}#bm-t .bm-z{width:fit-content;max-width:35ch;background-color:#153063e6;border-radius:1em;padding:.5em;gap:1ch;transition:background-color .3s ease}#bm-t .bm-z:hover,#bm-t.bm-z:focus-within{background-color:#112855e6}#bm-t .bm-a{display:block;border:thick double darkslategray;width:fit-content;height:fit-content;padding:1ch}#bm-t .bm-z[data-id="-2"] .bm-a{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}#bm-t .bm-z[data-id="-1"] .bm-a{background:url('data:image/svg+xml;utf8, ') repeat;background-color:transparent!important}#bm-t .bm-z[data-id="-1"] .bm-a svg{fill:#fff!important}#bm-t .bm-z[data-id="0"] .bm-a{background-color:transparent!important}#bm-t .bm-a button{padding:.75em .5ch}#bm-t .bm-a svg{width:4ch}#bm-t .bm-z>.bm-x{flex-direction:column;align-items:flex-start;gap:0}#bm-t .bm-z small{font-size:.75em}#bm-t .bm-z.bm-I{display:none}#bm-t.bm-N #bm-E{flex-direction:column;gap:.25em}#bm-t.bm-N .bm-z{width:auto;margin:0;padding:0}#bm-t.bm-N .bm-a{display:flex;width:100%;gap:.5ch;align-items:center;padding:.1em .5ch;border:none;border-radius:1em}#bm-t.bm-N .bm-a button{padding:.5em .25ch}#bm-t.bm-N .bm-a svg{width:3ch}#bm-t.bm-N .bm-z h2{font-size:.75em}#bm-t #bm-2{font-size:1em}#bm-l div:has(>.bm-3){width:fit-content;justify-content:flex-start}#bm-l .bm-3{display:flex;flex-direction:column;width:13%}#bm-l .bm-3 span{width:fit-content;margin:auto;font-size:.7em}#bm-l .bm-3 button{width:fit-content;padding:0;border-radius:0}#bm-l .bm-3 svg{stroke:#333;stroke-width:.02px;width:100%;min-width:1.5ch;max-width:14.5ch}#bm-l .bm-3 button:hover svg,#bm-l .bm-3 button:focus svg{opacity:.9}#bm-l .bm-n{display:grid;grid-template-columns:1fr 1fr 1fr;width:25%;min-width:3ch;max-width:15ch}#bm-l .bm-n>button{width:100%;padding:0;aspect-ratio:1 / 1;background-color:#fff;border:#333 1px solid;border-radius:0;box-sizing:border-box}#bm-l .bm-n>button[data-status=Incorrect]{background-color:brown}#bm-l .bm-n>button[data-status=Template]{background-color:#2f4f4f}#bm-l .bm-n>button:hover,#bm-l .bm-n>button:focus{opacity:.8}#bm-B{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-start}#bm-B>.bm-L{width:100%;justify-content:flex-start;background-color:#153063e6;border-radius:1em;padding:.5em;transition:background-color .3s ease}#bm-B>.bm-L:hover,#bm-B>.bm-L:focus-within{background-color:#112855e6}#bm-B .bm-1{height:100%;font-size:xxx-large}#bm-B .bm-0{flex-direction:column;align-items:flex-start;gap:0}div:has(>confetti-piece){position:absolute;inset:0;overflow:hidden;pointer-events:none}confetti-piece{position:absolute;top:-10px;width:var(--size);height:var(--size);background:currentColor;transform:translate3d(var(--x),-10vh,0) rotate(var(--rot));animation:fall var(--duration) linear var(--delay);will-change:transform;pointer-events:none}@keyframes fall{to{transform:translate3d(var(--x),110vh,0) rotate(calc(var(--rot) + 720deg))}}.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-W{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:fit-content;max-width:calc(100% - 135px);font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}.bm-W.bm-N{max-width:300px}.bm-S{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.5ch;background:url('data:image/svg+xml;utf8, ') repeat;cursor:grab;width:100%;height:fit-content}.bm-S.bm-M{cursor:grabbing}.bm-W:has(.bm-S.bm-M){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-S.bm-M{pointer-events:auto}.bm-T{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}.bm-W h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}.bm-S h1,.bm-y{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-S div:has(h1){display:contents}.bm-W h2{display:inline-block;font-size:larger;font-weight:700;vertical-align:middle}.bm-W h3{display:inline-block;font-size:large;font-weight:700}.bm-L.bm-h{width:fit-content;margin-left:auto;margin-right:auto}.bm-L{margin:.5em 0}.bm-W button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}.bm-W button:hover,.bm-W button:focus-visible{background-color:#1061e5}.bm-W button:active,.bm-W button:disabled{background-color:#2e97ff}.bm-W button:disabled{text-decoration:line-through;cursor:not-allowed}.bm-s{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}.bm-J{vertical-align:middle}.bm-J svg{width:50%;margin:0 auto;fill:#111}.bm-W button.bm-A{background-color:unset}.bm-A.bm-f:hover,.bm-A.bm-f:focus{background-color:#ffffff2b}.bm-A.bm-f:active{background-color:#ffffff38}.bm-A.bm-g:hover,.bm-A.bm-g:focus{background-color:#0000002b}.bm-A.bm-g:active{background-color:#00000038}input[type=number].bm-C{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}input[type=number].bm-C::-webkit-outer-spin-button,input[type=number].bm-C::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-K)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-K,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-W select{color:#fff;background-color:#144eb9;border-radius:1em;padding:0 .5ch}.bm-W label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch}.bm-W input[type=checkbox]{width:1em}.bm-m{overflow:hidden;transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-W textarea{font-size:small;background-color:#0003;padding:0 .5ch;height:5.25em;width:100%}.bm-W a:not(:has(*)){text-decoration:underline}.bm-W small{font-size:x-small;color:#d3d3d3}.bm-W ul li{list-style:disc;margin-left:5ch}.bm-W .bm-L.bm-H{max-height:calc(80vh - 150px);overflow:auto}.bm-x{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.5ch}.bm-D{display:flex;align-content:center;justify-content:center;align-items:center;gap:.5ch}.bm-_{white-space:pre;letter-spacing:0;line-height:1!important;font-size:1.6em;font-family:monospace}.bm-N .bm-L:not(#bm-F .bm-L){margin-top:.25em;margin-bottom:.25em}.bm-N h1:not(#bm-F h1){font-size:1em}
+div:has(>confetti-piece){position:absolute;inset:0;overflow:hidden;pointer-events:none}confetti-piece{position:absolute;top:-10px;width:var(--size);height:var(--size);background:currentColor;transform:translate3d(var(--x),-10vh,0) rotate(var(--rot));animation:fall var(--duration) linear var(--delay);will-change:transform;pointer-events:none}@keyframes fall{to{transform:translate3d(var(--x),110vh,0) rotate(calc(var(--rot) + 720deg))}}.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-W{--bm-surface-strong: rgba(9, 20, 42, .5);--bm-surface-soft: rgba(24, 41, 74, .28);--bm-surface-glass: rgba(255, 255, 255, .1);--bm-surface-glass-strong: rgba(255, 255, 255, .18);--bm-border-soft: rgba(255, 255, 255, .18);--bm-border-strong: rgba(163, 228, 255, .34);--bm-text-primary: rgba(17, 36, 66, .96);--bm-text-secondary: rgba(36, 57, 90, .84);--bm-accent-start: #baf6ff;--bm-accent-end: #81b6ff;--bm-accent-shadow: rgba(132, 182, 255, .22);--bm-font-body: "Rajdhani", "Segoe UI Variable Text", "Segoe UI", sans-serif;--bm-font-display: "Michroma", "Orbitron", "Segoe UI", sans-serif;--bm-font-mono: "Roboto Mono", "Rajdhani", "Courier New", monospace;position:fixed;isolation:isolate;overflow:hidden;background:radial-gradient(circle at 14% 12%,rgba(255,255,255,.24),transparent 18%),radial-gradient(circle at 86% 8%,rgba(186,246,255,.22),transparent 24%),radial-gradient(circle at 82% 84%,rgba(129,182,255,.18),transparent 28%),linear-gradient(145deg,#ffffff2e,#ffffff0f 22%,#6991d414 54%,#09142a2e);color:var(--bm-text-primary);padding:6px;border-radius:16px;border:1px solid var(--bm-border-soft);box-shadow:0 18px 40px #0003,inset 0 1px #ffffff38,inset 0 -1px #ffffff0d;z-index:9000;transition:background .32s ease,border-color .22s ease,box-shadow .22s ease,opacity .22s ease,transform 0s,width .22s ease,max-width .22s ease,max-height .22s ease;top:75px;left:60px;width:auto;max-height:fit-content;max-width:calc(100% - 135px);backdrop-filter:blur(26px) saturate(1.25);font-family:var(--bm-font-body);letter-spacing:.04em}.bm-W:before,.bm-W:after{content:"";position:absolute;inset:0;pointer-events:none}.bm-W:before{border-radius:inherit;padding:1px;background:linear-gradient(135deg,#ffffff73,#ffffff1f 24%,#baf6ff38 58%,#81b6ff4d);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.85}.bm-W:after{border-radius:inherit;background:linear-gradient(180deg,rgba(255,255,255,.2),transparent 24%),radial-gradient(circle at 18% 0%,rgba(255,255,255,.22),transparent 22%),radial-gradient(circle at 88% 16%,rgba(186,246,255,.18),transparent 18%);opacity:1}.bm-S{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:.28ch;padding:.18rem .24rem;border-radius:10px;border:1px solid rgba(255,255,255,.16);background:radial-gradient(circle at 0 0,rgba(255,255,255,.22) 0,transparent 42%),linear-gradient(135deg,#ffffff2e,#ffffff14);cursor:grab;width:100%;height:fit-content;box-shadow:inset 0 1px #ffffff29,0 6px 18px #0000001a}.bm-S.bm-M{cursor:grabbing}.bm-W:has(.bm-S.bm-M){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.bm-S.bm-M{pointer-events:auto}.bm-T{display:inline-block;height:2.2em;margin-right:.45ch;padding:.2rem;border-radius:12px;vertical-align:middle;background:linear-gradient(135deg,#ffffff38,#ffffff14);box-shadow:inset 0 1px #ffffff29,0 8px 18px #0000001f}.bm-W h1{display:inline-block;font-size:1rem;font-weight:700;vertical-align:middle;font-family:var(--bm-font-display);text-transform:uppercase;letter-spacing:.14em;color:#10213cf5}.bm-S h1,.bm-y{font-size:.78rem;user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-family:var(--bm-font-display);letter-spacing:.14em;color:#122542f2;text-shadow:0 1px 0 rgba(255,255,255,.28)}.bm-S div:has(h1){display:contents}.bm-W h2{display:inline-block;font-size:.88rem;font-weight:700;vertical-align:middle;font-family:var(--bm-font-display);letter-spacing:.1em;color:#122340f5}.bm-W h3{display:inline-block;font-size:large;font-weight:700;font-family:var(--bm-font-display);letter-spacing:.08em;color:#122340f5}.bm-W p{color:var(--bm-text-secondary);line-height:1.5;letter-spacing:.035em}.bm-W hr{border:none;height:1px;margin:.32rem 0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.28),transparent)}.bm-L.bm-h{width:fit-content;margin-left:auto;margin-right:auto}.bm-L{margin:.24em 0}.bm-W input,.bm-W select,.bm-W textarea,.bm-W button{font:inherit}.bm-W button{appearance:none;color:var(--bm-text-primary);font-family:var(--bm-font-display);background:linear-gradient(135deg,#ffffff38,#a4d0ff24);border:1px solid rgba(255,255,255,.22);border-radius:999px;padding:.28em .62em;min-height:1.78em;font-weight:600;letter-spacing:.1em;box-shadow:inset 0 1px #ffffff38,0 8px 20px #0000001a;transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,filter .18s ease,opacity .18s ease,transform .18s ease}.bm-W button:hover,.bm-W button:focus-visible{background:linear-gradient(135deg,#ffffff47,#baf6ff2e);border-color:#e6f5ff5c;box-shadow:inset 0 1px #ffffff42,0 10px 22px #0000001f;transform:translateY(-1px)}.bm-W button:focus-visible,.bm-W input:focus-visible,.bm-W select:focus-visible,.bm-W textarea:focus-visible{outline:none;border-color:#74e7ff9e;box-shadow:0 0 0 3px #74e7ff26,inset 0 1px #ffffff1f}.bm-W button:active{transform:translateY(0) scale(.98);filter:brightness(.98);box-shadow:inset 0 1px #ffffff1f,0 8px 16px #12244e3d}.bm-W button:disabled{opacity:.56;cursor:not-allowed;text-decoration:none;transform:none;filter:saturate(.72);box-shadow:none}.bm-W button.bm-1G{color:#07203b;background:linear-gradient(135deg,#ffffff6b,#baf6ff57);border-color:#eff8ff6b;box-shadow:inset 0 1px #ffffff70,0 10px 22px #81b6ff24}.bm-W button.bm-1G:hover,.bm-W button.bm-1G:focus-visible{background:linear-gradient(135deg,#ffffff85,#ccfaff5c)}.bm-W button.bm-1u{background:linear-gradient(135deg,#ffffff29,#ffffff14);border-color:#ffffff2e;box-shadow:inset 0 1px #ffffff24,0 8px 20px #00000014}.bm-W button.bm-1u:hover,.bm-W button.bm-1u:focus-visible{background:linear-gradient(135deg,#ffffff3d,#baf6ff1f)}.bm-s{display:inline-flex;align-items:center;justify-content:center;border:1px solid rgba(255,255,255,.16);inline-size:1.62rem;block-size:1.62rem;min-height:1.62rem!important;min-width:1.62rem;margin-top:0;text-align:center;line-height:1;padding:0!important;aspect-ratio:1 / 1;border-radius:50%!important;background:linear-gradient(135deg,#ffffff3d,#ffffff1a)!important;box-shadow:inset 0 1px #ffffff2e,0 8px 18px #0000001a!important;overflow:hidden;flex:0 0 auto;font-size:.74rem}.bm-s svg{display:block;width:70%;height:70%;flex:0 0 auto;margin:auto;color:currentColor;pointer-events:none;transform:translateZ(0);overflow:visible}.bm-s .bm-1g{width:68%;height:68%;transform:translateY(.5px) translateZ(0)}.bm-s .bm-1b{width:62%;height:62%;transform:translateY(.75px) translateZ(0)}#bm-t.bm-N .bm-s .bm-1b{transform:translateZ(0)}.bm-J{vertical-align:middle}.bm-J svg{width:50%;margin:0 auto;fill:#111}.bm-W button.bm-A{background:transparent!important;box-shadow:none!important;border-color:transparent!important}.bm-A.bm-f:hover,.bm-A.bm-f:focus{background-color:#ffffff2e!important}.bm-A.bm-f:active{background-color:#ffffff3d!important}.bm-A.bm-g:hover,.bm-A.bm-g:focus{background-color:#00000024!important}.bm-A.bm-g:active{background-color:#0003!important}.bm-W input[type=number],.bm-W select,.bm-W textarea{color:var(--bm-text-primary);font-family:var(--bm-font-body);background:linear-gradient(180deg,#ffffff29,#ffffff0f);border:1px solid rgba(255,255,255,.18);border-radius:12px;box-shadow:inset 0 1px #ffffff1f}input[type=number].bm-C{appearance:auto;-moz-appearance:textfield;width:5.9ch;margin-left:1ch;padding:.2em .35ch;font-size:small}input[type=number].bm-C::-webkit-outer-spin-button,input[type=number].bm-C::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}div:has(>.bm-K)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.bm-K,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-W select{padding:.22em .45ch}.bm-W label:has(input[type=checkbox]){display:flex;width:fit-content;gap:1ch;align-items:center;color:var(--bm-text-secondary)}.bm-W input[type=checkbox]{width:1em;accent-color:#74e7ff}.bm-m{overflow:hidden;max-height:calc(100% - 5px);transition:height .3s cubic-bezier(.4,0,.2,1)}.bm-W textarea{font-size:small;padding:.38em .52em;height:4em;width:100%;resize:vertical;line-height:1.45}.bm-W textarea::placeholder,.bm-W input::placeholder{color:#2f446699}.bm-W a:not(:has(*)){color:#1558a4f0;text-decoration:underline;text-decoration-color:#1558a459}.bm-W small{font-size:x-small;font-family:var(--bm-font-display);letter-spacing:.12em;color:var(--bm-text-secondary)}.bm-W ul li{list-style:disc;margin-left:5ch}.bm-W .bm-L.bm-H{max-height:var(--bm-H-max-height, calc(80vh - 150px) );overflow:auto;scrollbar-width:thin;scrollbar-color:rgba(146,221,255,.42) rgba(255,255,255,.05)}.bm-W .bm-L.bm-H::-webkit-scrollbar{width:10px;height:10px}.bm-W .bm-L.bm-H::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#74e7ff7a,#538dff66);border-radius:999px;border:2px solid transparent;background-clip:padding-box}.bm-W .bm-L.bm-H::-webkit-scrollbar-track{background:#ffffff0a;border-radius:999px}.bm-x{display:flex;align-content:center;justify-content:space-between;align-items:center;gap:.35ch}.bm-D{display:flex;align-content:center;justify-content:center;align-items:center;gap:.35ch}.bm-_{white-space:pre;letter-spacing:0;line-height:1!important;font-size:1.6em;font-family:monospace}.bm-N .bm-L:not(#bm-F .bm-L){margin-top:.18em;margin-bottom:.18em}.bm-N h1:not(#bm-F h1){font-size:1em}#bm-t .bm-1z{display:block;width:1.28em;height:1.28em;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;overflow:visible}#bm-t p svg{display:inline-block;height:1em;width:1em;color:currentColor;vertical-align:-.16em}#bm-t:not(.bm-N){width:min(50rem,calc(100vw - .55rem));max-width:min(50rem,calc(100vw - .55rem))!important}#bm-t .bm-1N{padding-top:.08rem}#bm-t .bm-1F{gap:.22rem;flex-wrap:wrap;width:100%;padding:.16rem}#bm-t .bm-1F>button{flex:1 1 10rem}#bm-t .bm-1r{padding-right:.08rem}#bm-t .bm-1A{display:grid;grid-template-columns:minmax(16rem,20rem) minmax(0,1fr);gap:.24rem .3rem;align-items:stretch}#bm-t .bm-1A>hr,#bm-t .bm-1t{grid-column:1 / -1}#bm-t .bm-1s,#bm-t .bm-20,#bm-t .bm-1t{padding:.38rem .48rem;border-radius:13px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(155deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29,0 8px 20px #00000014}#bm-t .bm-1s{display:grid;gap:.18rem}#bm-t .bm-1s br{display:none}#bm-t .bm-1s span{display:block;padding:.28rem .38rem;border-radius:10px;background:linear-gradient(180deg,#fff3,#ffffff14)}#bm-t .bm-20 p{margin:0}#bm-t .bm-1t fieldset{border:none;padding:0;margin:0}#bm-t .bm-1t legend{margin-bottom:.12rem}#bm-t .bm-1t .bm-L{margin-top:.1rem;margin-bottom:.1rem}#bm-t .bm-1l{display:flex;justify-content:flex-start}#bm-t .bm-1l button{min-width:7.8rem}#bm-E{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;align-items:stretch;gap:.3rem}#bm-t .bm-z{position:relative;overflow:hidden;width:fit-content;max-width:35ch;padding:.28rem;gap:.28rem;border-radius:13px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(160deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29,0 8px 20px #00000014;transition:background .25s ease,border-color .25s ease,box-shadow .25s ease,transform .25s ease}#bm-t .bm-z:before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at top right,rgba(255,255,255,.18),transparent 24%),radial-gradient(circle at 18% 0%,rgba(186,246,255,.12),transparent 22%)}#bm-t .bm-z:hover,#bm-t .bm-z:focus-within{transform:translateY(-2px);border-color:#92ddff42;background:linear-gradient(160deg,#ffffff38,#baf6ff1a);box-shadow:inset 0 1px #fff3,0 10px 24px #0000001a}#bm-t .bm-a{display:block;width:fit-content;height:fit-content;padding:.26rem;border:1px solid rgba(255,255,255,.18);border-radius:10px;box-shadow:inset 0 1px #ffffff24,0 6px 16px #00000014}#bm-t .bm-z[data-id="-2"] .bm-a{background:conic-gradient(#a00,#aa0 16.6%,#0a0,#0aa 50%,#00a 66.6%,#a0a,#a00)}#bm-t .bm-z[data-id="-1"] .bm-a{background:url('data:image/svg+xml;utf8, ') repeat;background-color:transparent!important}#bm-t .bm-z[data-id="-1"] .bm-a svg{color:#fff!important}#bm-t .bm-z[data-id="0"] .bm-a{background-color:transparent!important}#bm-t .bm-a button{display:inline-flex;align-items:center;justify-content:center;padding:.24em;min-width:2.05rem;min-height:2.05rem;border-radius:999px;line-height:1}#bm-t .bm-a .bm-1z{width:1.55rem;height:1.55rem;filter:drop-shadow(0 1px 0 rgba(255,255,255,.18))}#bm-t .bm-z>.bm-x{flex-direction:column;align-items:flex-start;gap:.02rem}#bm-t .bm-z h2{margin:.04rem 0 0}#bm-t .bm-z .bm-6{margin:.1rem 0 0}#bm-t .bm-z small{font-size:.75em}#bm-t .bm-z.bm-I{display:none}#bm-t.bm-N{--bm-H-max-height: 100%;display:grid;grid-template-rows:auto minmax(0,1fr);width:360px;height:min(60vh,22rem);min-width:360px;min-height:180px;max-width:min(1000px,calc(100vw - 16px))!important;max-height:min(1400px,calc(100vh - 16px))!important;overflow:hidden;box-sizing:border-box;position:fixed;transition:background .32s ease,border-color .22s ease,box-shadow .22s ease,transform 0s}#bm-t.bm-N .bm-m{display:grid;grid-template-rows:auto auto auto auto minmax(0,1fr);grid-row:2;min-height:0;min-width:0;overflow:hidden}#bm-t.bm-N .bm-1F{gap:.16rem;flex-wrap:nowrap;width:100%;padding:.12rem}#bm-t.bm-N .bm-1F>button{flex:1 1 0;min-width:0}#bm-t.bm-N #bm-E{flex-direction:column;align-items:stretch;gap:.16rem;width:100%;align-self:stretch;min-width:0;box-sizing:border-box}#bm-t.bm-N .bm-z{width:100%;max-width:none;align-self:stretch;flex:1 1 auto;min-width:0;margin:0;padding:.12rem;border-radius:11px;box-sizing:border-box}#bm-t.bm-N .bm-z>.bm-x{width:100%;min-width:0;flex:1 1 auto}#bm-t.bm-N .bm-L.bm-H{display:block;grid-row:5;min-height:0;min-width:0;height:100%;width:100%;max-height:100%!important;overflow:auto;box-sizing:border-box}#bm-t.bm-N .bm-1O{position:absolute;right:4px;bottom:4px;display:flex;width:20px;height:20px;align-items:flex-end;justify-content:flex-end;padding-right:4px;padding-bottom:4px;box-sizing:border-box;z-index:5;cursor:nwse-resize;pointer-events:auto;opacity:.78;touch-action:none;user-select:none;color:#ffffffdb;background:linear-gradient(135deg,#ffffff24,#538dff24);border:1px solid rgba(255,255,255,.12);border-radius:8px;box-shadow:inset 0 1px #ffffff14}#bm-t.bm-N .bm-1O:hover,#bm-t.bm-N .bm-1O.bm-2g{opacity:1;border-color:#92ddff5c;box-shadow:inset 0 1px #ffffff1a,0 8px 16px #00000029}#bm-t.bm-N .bm-a{display:flex;width:100%;min-width:0;flex:1 1 auto;gap:.5ch;align-items:center;padding:.1rem .2rem;border:none;border-radius:8px;box-sizing:border-box}#bm-t.bm-N .bm-a button{padding:.2em;flex:0 0 auto}#bm-t.bm-N .bm-a .bm-1z{width:1.36rem;height:1.36rem}#bm-t.bm-N .bm-z h2{font-size:.78rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#bm-t #bm-2{display:inline-flex;align-items:center;padding:.08rem .24rem;border-radius:999px;background:#ffffff1f;box-shadow:inset 0 1px #ffffff14;font-size:1em}@media (max-width: 980px){#bm-t:not(.bm-N){width:min(100vw - .35rem,50rem);max-width:min(100vw - .35rem,50rem)!important}#bm-t .bm-1A{grid-template-columns:1fr}#bm-t .bm-20,#bm-t .bm-1t,#bm-t .bm-1A>hr{grid-column:auto}}#bm-F{width:min(25.5rem,calc(100vw - .65rem));max-width:min(25.5rem,calc(100vw - .65rem))!important}#bm-F .bm-m{display:flex;flex-direction:column}#bm-F .bm-2d,#bm-F .bm-29{position:relative;overflow:hidden;border-radius:14px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(150deg,#ffffff2e,#ffffff14);box-shadow:inset 0 1px #fff3,0 10px 24px #0000001a}#bm-F .bm-2d:before,#bm-F .bm-29:before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at top right,rgba(255,255,255,.2),transparent 26%),radial-gradient(circle at 20% 0%,rgba(186,246,255,.16),transparent 24%)}#bm-F .bm-2d{display:flex;align-items:center;gap:.5rem;padding:.48rem .58rem}#bm-F .bm-2d h1{margin:0}#bm-F .bm-28{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.24rem}#bm-F .bm-1I{min-width:0;min-height:2.55rem;display:flex;align-items:flex-start;padding:.34rem .45rem;border-radius:11px;border:1px solid rgba(255,255,255,.18);background:linear-gradient(160deg,#ffffff2e,#ffffff12);box-shadow:inset 0 1px #ffffff29;color:#12233ff5;line-height:1.22}#bm-F .bm-1I>span,#bm-F .bm-1I>time{min-width:0}#bm-F .bm-1h,#bm-F .bm-1i{flex-direction:column;justify-content:center;gap:.08rem}#bm-F .bm-1C{display:inline-block;font-size:1.3em;color:#0f1f38f5;white-space:normal;overflow-wrap:anywhere}#bm-F .bm-1C b{font-size:1em}#bm-F .bm-1B{color:#314769d1;font-size:.62rem;letter-spacing:.08em;font-family:var(--bm-font-display);text-transform:uppercase}#bm-F .bm-1I time{white-space:nowrap;font-family:var(--bm-font-mono);letter-spacing:.06em}#bm-F .bm-29{padding:.48rem}#bm-F .bm-22{display:grid;grid-template-columns:auto repeat(4,minmax(0,1fr));gap:.22rem;align-items:center}#bm-F .bm-22 .bm-J{width:1.8rem;height:1.8rem}#bm-F .bm-22 .bm-C{width:100%;margin-left:0;text-align:center}#bm-F .bm-23,#bm-F .bm-24{margin-top:.24rem}#bm-F .bm-23>div{width:100%}#bm-F .bm-1Y{gap:.24rem;margin-top:.3rem}#bm-F .bm-1Y>button{flex:1 1 0}#bm-F .bm-24 textarea{min-height:4.1rem}@media (max-width: 720px){#bm-F{width:min(100vw - .45rem,25.5rem);max-width:min(100vw - .45rem,25.5rem)!important}#bm-F .bm-28{grid-template-columns:1fr}#bm-F .bm-22{grid-template-columns:repeat(2,minmax(0,1fr))}#bm-F .bm-22 .bm-J{grid-column:1 / -1;width:100%;aspect-ratio:auto;height:1.8rem}#bm-F .bm-1Y{flex-direction:column}}#bm-l div:has(>.bm-3){width:fit-content;justify-content:flex-start}#bm-l .bm-3{display:flex;flex-direction:column;width:13%}#bm-l .bm-3 span{width:fit-content;margin:auto;font-size:.7em}#bm-l .bm-3 button{width:fit-content;padding:0;border-radius:0}#bm-l .bm-3 svg{stroke:#333;stroke-width:.02px;width:100%;min-width:1.5ch;max-width:14.5ch}#bm-l .bm-3 button:hover svg,#bm-l .bm-3 button:focus svg{opacity:.9}#bm-l .bm-n{display:grid;grid-template-columns:1fr 1fr 1fr;width:25%;min-width:3ch;max-width:15ch}#bm-l .bm-n>button{width:100%;padding:0;aspect-ratio:1 / 1;background-color:#fff;border:#333 1px solid;border-radius:0;box-sizing:border-box}#bm-l .bm-n>button[data-status=Incorrect]{background-color:brown}#bm-l .bm-n>button[data-status=Template]{background-color:#2f4f4f}#bm-l .bm-n>button:hover,#bm-l .bm-n>button:focus{opacity:.8}#bm-B{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-start}#bm-B>.bm-L{width:100%;justify-content:flex-start;background-color:#153063e6;border-radius:1em;padding:.5em;transition:background-color .3s ease}#bm-B>.bm-L:hover,#bm-B>.bm-L:focus-within{background-color:#112855e6}#bm-B .bm-1{height:100%;font-size:xxx-large}#bm-B .bm-0{flex-direction:column;align-items:flex-start;gap:0}
+
+/* Build Hash: 3c5ee345af9c */
diff --git a/dist/BlueMarble.user.js b/dist/BlueMarble.user.js
index 6fecec5..495d9b9 100644
--- a/dist/BlueMarble.user.js
+++ b/dist/BlueMarble.user.js
@@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.94.0
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
@@ -39,4 +39,6 @@
The "Blue Marble" image is owned by NASA.
*/
-(()=>{var t=t=>{throw TypeError(t)},e=(e,i,n)=>i.has(e)?t("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(e):i.set(e,n),i=(e,i,n)=>(((e,i)=>{i.has(e)||t("Cannot access private method")})(e,i),n);function n(t){return new Promise(e=>setTimeout(e,t))}function s(t){return(new Intl.NumberFormat).format(t)}function o(t){return new Intl.NumberFormat(void 0,{style:"percent",t:2,i:2}).format(t)}function a(t){return t.toLocaleString(void 0,{o:"long",l:"numeric",h:"2-digit",m:"2-digit",u:"2-digit"})}function r(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function l(...t){(0,console.log)(...t)}function h(...t){(0,console.error)(...t)}function c(...t){(0,console.warn)(...t)}function m(t,e){if(0===t)return e[0];let i="";const n=e.length;for(;t>0;)i=e[t%n]+i,t=Math.floor(t/n);return i}function d(t,e){let i=0;const n=e.length;for(const s of t){const t=e.indexOf(s);-1==t&&h(`Invalid character '${s}' encountered whilst decoding! Is the decode alphabet/base incorrect?`),i=i*n+t}return i}function u(t){let e="";for(let i=0;i(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*e[0]+.7152*e[1]+.0722*e[2]}function f(t,e,i){return Array.isArray(t)&&([t,e,i]=t),(1<<24|t<<16|e<<8|i).toString(16).slice(1)}var g,w,x,y,v,C=[{id:0,premium:!1,name:"Transparent",rgb:[0,0,0]},{id:1,premium:!1,name:"Black",rgb:[0,0,0]},{id:2,premium:!1,name:"Dark Gray",rgb:[60,60,60]},{id:3,premium:!1,name:"Gray",rgb:[120,120,120]},{id:4,premium:!1,name:"Light Gray",rgb:[210,210,210]},{id:5,premium:!1,name:"White",rgb:[255,255,255]},{id:6,premium:!1,name:"Deep Red",rgb:[96,0,24]},{id:7,premium:!1,name:"Red",rgb:[237,28,36]},{id:8,premium:!1,name:"Orange",rgb:[255,127,39]},{id:9,premium:!1,name:"Gold",rgb:[246,170,9]},{id:10,premium:!1,name:"Yellow",rgb:[249,221,59]},{id:11,premium:!1,name:"Light Yellow",rgb:[255,250,188]},{id:12,premium:!1,name:"Dark Green",rgb:[14,185,104]},{id:13,premium:!1,name:"Green",rgb:[19,230,123]},{id:14,premium:!1,name:"Light Green",rgb:[135,255,94]},{id:15,premium:!1,name:"Dark Teal",rgb:[12,129,110]},{id:16,premium:!1,name:"Teal",rgb:[16,174,166]},{id:17,premium:!1,name:"Light Teal",rgb:[19,225,190]},{id:18,premium:!1,name:"Dark Blue",rgb:[40,80,158]},{id:19,premium:!1,name:"Blue",rgb:[64,147,228]},{id:20,premium:!1,name:"Cyan",rgb:[96,247,242]},{id:21,premium:!1,name:"Indigo",rgb:[107,80,246]},{id:22,premium:!1,name:"Light Indigo",rgb:[153,177,251]},{id:23,premium:!1,name:"Dark Purple",rgb:[120,12,153]},{id:24,premium:!1,name:"Purple",rgb:[170,56,185]},{id:25,premium:!1,name:"Light Purple",rgb:[224,159,249]},{id:26,premium:!1,name:"Dark Pink",rgb:[203,0,122]},{id:27,premium:!1,name:"Pink",rgb:[236,31,128]},{id:28,premium:!1,name:"Light Pink",rgb:[243,141,169]},{id:29,premium:!1,name:"Dark Brown",rgb:[104,70,52]},{id:30,premium:!1,name:"Brown",rgb:[149,104,42]},{id:31,premium:!1,name:"Beige",rgb:[248,178,119]},{id:32,premium:!0,name:"Medium Gray",rgb:[170,170,170]},{id:33,premium:!0,name:"Dark Red",rgb:[165,14,30]},{id:34,premium:!0,name:"Light Red",rgb:[250,128,114]},{id:35,premium:!0,name:"Dark Orange",rgb:[228,92,26]},{id:36,premium:!0,name:"Light Tan",rgb:[214,181,148]},{id:37,premium:!0,name:"Dark Goldenrod",rgb:[156,132,49]},{id:38,premium:!0,name:"Goldenrod",rgb:[197,173,49]},{id:39,premium:!0,name:"Light Goldenrod",rgb:[232,212,95]},{id:40,premium:!0,name:"Dark Olive",rgb:[74,107,58]},{id:41,premium:!0,name:"Olive",rgb:[90,148,74]},{id:42,premium:!0,name:"Light Olive",rgb:[132,197,115]},{id:43,premium:!0,name:"Dark Cyan",rgb:[15,121,159]},{id:44,premium:!0,name:"Light Cyan",rgb:[187,250,242]},{id:45,premium:!0,name:"Light Blue",rgb:[125,199,255]},{id:46,premium:!0,name:"Dark Indigo",rgb:[77,49,184]},{id:47,premium:!0,name:"Dark Slate Blue",rgb:[74,66,132]},{id:48,premium:!0,name:"Slate Blue",rgb:[122,113,196]},{id:49,premium:!0,name:"Light Slate Blue",rgb:[181,174,241]},{id:50,premium:!0,name:"Light Brown",rgb:[219,164,99]},{id:51,premium:!0,name:"Dark Beige",rgb:[209,128,81]},{id:52,premium:!0,name:"Light Beige",rgb:[255,197,165]},{id:53,premium:!0,name:"Dark Peach",rgb:[155,82,73]},{id:54,premium:!0,name:"Peach",rgb:[209,128,120]},{id:55,premium:!0,name:"Light Peach",rgb:[250,182,164]},{id:56,premium:!0,name:"Dark Tan",rgb:[123,99,82]},{id:57,premium:!0,name:"Tan",rgb:[156,132,107]},{id:58,premium:!0,name:"Dark Slate",rgb:[51,57,65]},{id:59,premium:!0,name:"Slate",rgb:[109,117,141]},{id:60,premium:!0,name:"Light Slate",rgb:[179,185,209]},{id:61,premium:!0,name:"Dark Stone",rgb:[109,100,63]},{id:62,premium:!0,name:"Stone",rgb:[148,140,107]},{id:63,premium:!0,name:"Light Stone",rgb:[205,197,158]}],M=class{constructor(t,i){e(this,g),this.name=t,this.version=i,this.p=null,this.v=null,this.C="bm-r",this.M=null,this.T=null,this.$=[]}S(t){this.p=t}k(t){this.v=t}D(){return this.$.length>0&&(this.T=this.$.pop()),this}L(t){t?.appendChild(this.M),this.M=null,this.T=null,this.$=[]}H(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{},t)),this}N(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"p",{},t)),this}O(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"small",{},t)),this}B(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"span",{},t)),this}I(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"details",{},t)),this}P(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"summary",{},t)),this}A(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"img",{},t)),this}W(t,e={},n=()=>{}){return n(this,i(this,g,w).call(this,"h"+t,{},e)),this}V(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"hr",{},t)),this}_(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"br",{},t)),this}F(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"form",{},t)),this}U(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"fieldset",{},t)),this}G(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"legend",{},t)),this}R(t={},e=()=>{}){const n={};t.textContent?(n.textContent=t.textContent,delete t.textContent):t.innerHTML&&(n.innerHTML=t.innerHTML,delete t.textContent);const s=i(this,g,w).call(this,"label",n),o=i(this,g,w).call(this,"input",{type:"checkbox"},t);return s.insertBefore(o,s.firstChild),this.D(),e(this,s,o),this}j(t={},e=()=>{}){const n=i(this,g,w).call(this,"label",{textContent:t.textContent??"",for:t.id??""});return delete t.textContent,this.D(),e(this,n,i(this,g,w).call(this,"select",{},t)),this}Y(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"option",{},t)),this}X(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ol",{},t)),this}J(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ul",{},t)),this}q(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"menu",{},t)),this}Z(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"li",{},t)),this}K(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"table",{},t)),this}tt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"caption",{},t)),this}et(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"thead",{},t)),this}it(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tbody",{},t)),this}nt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tfoot",{},t)),this}st(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tr",{},t)),this}ot(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"th",{},t)),this}rt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"td",{},t)),this}lt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"button",{},t)),this}ht(t={},e=()=>{}){const n=t.title??t.textContent??"Help: No info";delete t.textContent,t.title=`Help: ${n}`;const s={textContent:"?",className:"bm-10",onclick:()=>{this.ct(this.C,n)}};return e(this,i(this,g,w).call(this,"button",s,t)),this}dt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"input",{},t)),this}ut(t={},e=()=>{}){const n=t.textContent??"";delete t.textContent;const s=i(this,g,w).call(this,"div"),o=i(this,g,w).call(this,"input",{type:"file",tabindex:"-1","aria-hidden":"true"},t);this.D();const a=i(this,g,w).call(this,"button",{textContent:n});return this.D(),this.D(),a.addEventListener("click",()=>{o.click()}),o.addEventListener("change",()=>{a.style.maxWidth=`${a.offsetWidth}px`,o.files.length>0?a.textContent=o.files[0].name:a.textContent=n}),e(this,s,o,a),this}bt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"textarea",{},t)),this}ft(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{class:"bm-S"},t)),this}gt(t=Date.now(),e=500,n={},s=()=>{}){const o="bm--",a=n?.id||o+"-"+crypto.randomUUID().slice(0,8),r={class:o},l=i(this,g,w).call(this,"time",r,n);return l.id=a,l.dataset.endDate=t,setInterval(()=>{if(!l.isConnected)return;const t=Math.max(l.dataset.endDate-Date.now(),0),e=Math.floor(t/1e3),i=Math.floor(e/3600),n=Math.floor(e%60),s=Math.floor(e%3600/60);l.setAttribute("datetime",`PT${i}H${s}M${n}S`),l.textContent=String(i).padStart(2,"0")+":"+String(s).padStart(2,"0")+":"+String(n).padStart(2,"0")},e),s(this,l),this}ct(t,e,i=!1){const n=document.getElementById(t.replace(/^#/,""));n&&(n instanceof HTMLInputElement?n.value=e:i?n.textContent=e:n.innerHTML=e)}wt(t){if(t.disabled)return;t.disabled=!0,t.style.textDecoration="none";const e=t.closest(".bm-W"),i=t.closest(".bm-S"),n=e.querySelector("h1"),s=e.querySelector(".bm-m");if(e.parentElement.append(e),"expanded"==t.dataset.buttonStatus){s.style.height=s.scrollHeight+"px",e.style.width=e.scrollWidth+"px",s.style.height="0",s.addEventListener("transitionend",function e(){s.style.display="none",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)});const i=n.cloneNode(!0),o=i.textContent;t.nextElementSibling.appendChild(i),t.textContent="▶",t.dataset.buttonStatus="collapsed",t.ariaLabel=`Unminimize window "${o}"`}else{const n=i.querySelector("h1"),o=n.textContent;n.remove(),s.style.display="",s.style.height="0",e.style.width="",s.style.height=s.scrollHeight+"px",s.addEventListener("transitionend",function e(){s.style.height="",t.disabled=!1,t.style.textDecoration="",s.removeEventListener("transitionend",e)}),t.textContent="▼",t.dataset.buttonStatus="expanded",t.ariaLabel=`Minimize window "${o}"`}}xt(t,e){const i=document.querySelector(t),n=document.querySelector(e);if(!i||!n)return void this.yt(`Can not drag! ${i?"":"moveMe"} ${i||n?"":"and "}${n?"":"iMoveThings "}was not found!`);let s,o=!1,a=0,r=null,l=0,h=0,c=0,m=0,d=null;const u=()=>{if(o){const t=Math.abs(l-c),e=Math.abs(h-m);(t>.5||e>.5)&&(l=c,h=m,i.style.transform=`translate(${l}px, ${h}px)`,i.style.left="0px",i.style.top="0px",i.style.right=""),r=requestAnimationFrame(u)}},b=(t,e)=>{o=!0,d=i.getBoundingClientRect(),s=t-d.left,a=e-d.top;const b=window.getComputedStyle(i).transform;if(b&&"none"!==b){const t=new DOMMatrix(b);l=t.m41,h=t.m42}else l=d.left,h=d.top;c=l,m=h,document.body.style.userSelect="none",n.classList.add("bm-M"),document.addEventListener("mousemove",f),document.addEventListener("touchmove",g,{passive:!1}),document.addEventListener("mouseup",p),document.addEventListener("touchend",p),document.addEventListener("touchcancel",p),r&&cancelAnimationFrame(r),u()},p=()=>{o=!1,r&&(cancelAnimationFrame(r),r=null),document.body.style.userSelect="",n.classList.remove("bm-M"),document.removeEventListener("mousemove",f),document.removeEventListener("touchmove",g),document.removeEventListener("mouseup",p),document.removeEventListener("touchend",p),document.removeEventListener("touchcancel",p)},f=t=>{o&&d&&(c=t.clientX-s,m=t.clientY-a)},g=t=>{if(o&&d){const e=t.touches[0];if(!e)return;c=e.clientX-s,m=e.clientY-a,t.preventDefault()}};n.addEventListener("mousedown",function(t){t.preventDefault(),b(t.clientX,t.clientY)}),n.addEventListener("touchstart",function(t){const e=t?.touches?.[0];e&&(b(e.clientX,e.clientY),t.preventDefault())},{passive:!1})}vt(t){(0,console.info)(`${this.name}: ${t}`),this.ct(this.C,"Status: "+t,!0)}yt(t){(0,console.error)(`${this.name}: ${t}`),this.ct(this.C,"Error: "+t,!0)}};g=new WeakSet,w=function(t,e={},n={}){const s=document.createElement(t);this.M?(this.T?.appendChild(s),this.$.push(this.T),this.T=s):(this.M=s,this.T=s);for(const[t,n]of Object.entries(e))i(this,g,x).call(this,s,t,n);for(const[t,e]of Object.entries(n))i(this,g,x).call(this,s,t,e);return s},x=function(t,e,i){"class"==e?t.classList.add(...i.split(/\s+/)):"for"==e?t.htmlFor=i:"tabindex"==e?t.tabIndex=Number(i):"readonly"==e?t.readOnly="true"==i||"1"==i:"maxlength"==e?t.maxLength=Number(i):e.startsWith("data")?t.dataset[e.slice(5).split("-").map((t,e)=>0==e?t:t[0].toUpperCase()+t.slice(1)).join("")]=i:e.startsWith("aria")?t.setAttribute(e,i):t[e]=i};var T,$,S,k,D,L=class extends M{constructor(t,i){super(t,i),e(this,y),this.window=null,this.Ct="bm-l",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Settings"}).D().D().V().D().N({textContent:"Settings take 5 seconds to save."}).D().H({class:"bm-L bm-H"},(t,e)=>{this.$t(),this.St()}).D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}$t(){i(this,y,v).call(this,"Pixel Highlight")}St(){i(this,y,v).call(this,"Template")}};y=new WeakSet,v=function(t){this.window=this.H({class:"bm-L"}).W(2,{textContent:t}).D().V().D().N({innerHTML:`An error occured loading the ${t} category. SettingsManager failed to override the ${t} function inside WindowSettings.`}).D().D()},T=new WeakSet,$=function(t,e){t.disabled=!0;const i=t.dataset.status,n=this.kt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];let s=[2,0,0];const o=n;switch(i){case"Disabled":t.dataset.status="Incorrect",t.ariaLabel="Sub-pixel incorrect",s=[1,...e];break;case"Incorrect":t.dataset.status="Template",t.ariaLabel="Sub-pixel template",s=[2,...e];break;case"Template":t.dataset.status="Disabled",t.ariaLabel="Sub-pixel disabled",s=[0,...e]}const a=n.findIndex(([,t,e])=>t==s[1]&&e==s[2]);0!=s[0]?-1!=a?o[a]=s:o.push(s):-1!=a&&o.splice(a,1),this.kt.highlight=o,t.disabled=!1},S=async function(t){const e=document.querySelectorAll(".bm-3 button");for(const t of e)t.disabled=!0;let i=[0,0,0,0,2,0,0,0,0];switch(t){case"Cross":i=[0,1,0,1,2,1,0,1,0];break;case"X":i=[1,0,1,0,2,0,1,0,1];break;case"Full":i=[2,2,2,2,2,2,2,2,2]}const s=document.querySelector(".bm-n")?.childNodes??[];for(let t=0;t{const[n,s,o,a]=e.split(",").map(Number);(s>>24==0?0:s.get(e)??-2;const a=o.get(n);o.set(n,a?a+1:1)}return o};var N=class{constructor(){this.Et=Math.ceil(80/1300*window.innerWidth),this.Yt=C.slice(1)}Xt(t){const e=document.createElement("div");for(let t=0;t{t.parentNode.childElementCount<=1?t.parentNode.remove():t.remove()},e.appendChild(t)}t.appendChild(e)}},O=class extends HTMLElement{};customElements.define("confetti-piece",O);var B,I,P,A,W,V,_,z,F,U=class extends M{constructor(t,e){super(t,e),this.window=null,this.Ct="bm-o",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Credits"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Credits"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Credits"}).D().D().V().D().H({class:"bm-L bm-H"}).B({role:"img","aria-label":this.name}).B({innerHTML:"\n██████╗ ██╗ ██╗ ██╗███████╗\n██╔══██╗██║ ██║ ██║██╔════╝\n██████╔╝██║ ██║ ██║█████╗ \n██╔══██╗██║ ██║ ██║██╔══╝ \n██████╔╝███████╗╚██████╔╝███████╗\n╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝\n\n███╗ ███╗ █████╗ ██████╗ ██████╗ ██╗ ███████╗\n████╗ ████║██╔══██╗██╔══██╗██╔══██╗██║ ██╔════╝\n██╔████╔██║███████║██████╔╝██████╔╝██║ █████╗ \n██║╚██╔╝██║██╔══██║██╔══██╗██╔══██╗██║ ██╔══╝ \n██║ ╚═╝ ██║██║ ██║██║ ██║██████╔╝███████╗███████╗\n╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝\n",class:"bm-_","aria-hidden":"true"}).D().D()._().D().V().D()._().D().B({textContent:'"Blue Marble" userscript is made by SwingTheVine.'}).D()._().D().B({innerHTML:'The Blue Marble Website is made by crqch .'}).D()._().D().B({textContent:`The Blue Marble Website used until ${a(new Date(175606932e4))} was made by Camille Daguin.`}).D()._().D().B({textContent:'The favicon "Blue Marble" is owned by NASA. (The image of the Earth is owned by NASA)'}).D()._().D().B({textContent:"Special Thanks:"}).D().J().Z({textContent:"Espresso, Meqa, and Robot for moderating SwingTheVine's community."}).D().Z({innerHTML:'nof, darkness for creating similar userscripts!'}).D().Z({innerHTML:'Wonda for the Blue Marble banner image!'}).D().Z({innerHTML:'BullStein , allanf181 for being early beta testers!'}).D().Z({innerHTML:'guidu_ and Nick-machado for the original "Minimize" Button code!'}).D().Z({innerHTML:'Nomad and Gustav for the tutorials!'}).D().Z({innerHTML:'cfp for creating the template overlay that Blue Marble was based on!'}).D().Z({innerHTML:'Force Network for hosting the telemetry server !'}).D().Z({innerHTML:'TheBlueCorner for getting me interested in online pixel canvases!'}).D().D()._().D().B({innerHTML:'Donators :'}).D().J().Z({textContent:"Soultree"}).D().Z({textContent:"Espresso"}).D().Z({textContent:"BEST FAN"}).D().Z({textContent:"FuchsDresden"}).D().Z({textContent:"Jack"}).D().Z({textContent:"raiken_au"}).D().Z({textContent:"Jacob"}).D().Z({textContent:"StupidOne"}).D().Z({textContent:"2 Anonymous Supporters"}).D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}},G=class extends M{constructor(t){super(t.name,t.version),e(this,B),this.window=null,this.Ct="bm-t",this.Jt="bm-E",this.Mt=document.body,this.qt=t.p?.qt,this.Zt=' ',this.Qt=' ';const{palette:i,jt:n}=this.qt.Kt;this.palette=i,this.te=0,this.ee=0,this.ie=new Map,this.ne=new Map,this.se=0,this.oe=0,this.timeRemaining=0,this.ae="",this.sortPrimary="id",this.sortSecondary="ascending",this.showUnused=!1}Tt(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();this.window=this.H({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"🗗","aria-label":'Switch to windowed mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove(),this.re()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Color Filter"}).D().D().V().D().H({class:"bm-L bm-x bm-h",style:"gap: 1.5ch;"}).lt({textContent:"Hide All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!1)}).D().lt({textContent:"Refresh Data"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.le(),e.disabled=!1}}).D().lt({textContent:"Show All Colors"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!0)}).D().D().H({class:"bm-L bm-H"}).H({class:"bm-L",style:"margin-left: 2.5ch; margin-right: 2.5ch;"}).H({class:"bm-L"}).B({id:"bm-i",innerHTML:"Tiles Loaded: 0 / ???"}).D()._().D().B({id:"bm-d",innerHTML:"Correct Pixels: ???"}).D()._().D().B({id:"bm-j",innerHTML:"Total Pixels: ???"}).D()._().D().B({id:"bm-7",innerHTML:"Complete: ??? (???)"}).D()._().D().B({id:"bm-8",innerHTML:"??? ???"}).D().D().H({class:"bm-L"}).N({innerHTML:`Press the 🗗 button to make this window smaller. Colors with the icon ${this.Zt.replace("{e.onclick=t=>{t.preventDefault();const e=new FormData(document.querySelector(`#${this.Ct} form`)),n={};for(const[t,i]of e)n[t]=i;i(this,B,P).call(this,n.sortPrimary,n.sortSecondary,"on"==n.showUnused)}}).D().D().D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,I).call(this,t),i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),this.ct("#bm-i",`Tiles Loaded: ${s(this.te)} / ${s(this.ee)}`),this.ct("#bm-d",`Correct Pixels: ${s(this.se)}`),this.ct("#bm-j",`Total Pixels: ${s(this.oe)}`),this.ct("#bm-7",`Remaining: ${s((this.oe||0)-(this.se||0))} (${o(((this.oe||0)-(this.se||0))/(this.oe||1))})`),this.ct("#bm-8",`Completed at: ${this.ae} `)}re(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();this.window=this.H({id:this.Ct,class:"bm-W bm-N"}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>{const i=document.querySelector("#bm-2");i&&(i.style.display="expanded"==e.dataset.buttonStatus?"none":""),t.wt(e)},e.ontouchend=()=>{e.click()}}).D().H().B({id:"bm-2",class:"bm-y",style:"font-weight: 700;"}).D().D().H({class:"bm-D"}).lt({class:"bm-s",textContent:"🗖","aria-label":'Switch to fullscreen mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove(),this.Tt()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Color Filter"}).D().D().V().D().H({class:"bm-L bm-x bm-h",style:"gap: 1.5ch;"}).lt({textContent:"None"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!1)}).D().lt({textContent:"Refresh"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.le(),e.disabled=!1}}).D().lt({textContent:"All"},(t,e)=>{e.onclick=()=>i(this,B,A).call(this,!0)}).D().D().H({class:"bm-L bm-H"}).D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,I).call(this,t),i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}le(){i(this,B,W).call(this);const t=document.querySelector(`#${this.Jt}`),e={};for(const t of this.palette){const i=this.ie.get(t.id)??0,n=s(i);let a=0,r="0",l=o(1);0!=i&&(a=this.ne.get(t.id)??"???","number"!=typeof a&&this.te==this.ee&&t.id&&(a=0),r="string"==typeof a?a:s(a),l=isNaN(a/i)?"???":o(a/i));const h=parseInt(i)-parseInt(a);e[t.id]={he:i,ce:n,me:a,de:r,ue:l,be:h}}if(document.querySelector("#bm-2")){const t=this.se.toString().length>7?this.se.toString().slice(0,2)+"…"+this.se.toString().slice(-3):this.se.toString(),e=this.oe.toString().length>7?this.oe.toString().slice(0,2)+"…"+this.oe.toString().slice(-3):this.oe.toString();this.ct("#bm-2",`${t}/${e}`,!0)}if(!t)return e;const n=Array.from(t.children);for(const t of n){const i=parseInt(t.dataset.id),{me:n,de:s,ue:o,he:a,ce:r,be:l}=e[i];t.dataset.correct=Number.isNaN(parseInt(n))?"0":n,t.dataset.total=a,t.dataset.percent="%"==o.slice(-1)?o.slice(0,-1):"0",t.dataset.incorrect=l||0;const h=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-9`);h&&(h.textContent=`${s} / ${r}`);const c=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-6`);c&&(c.textContent=`${"number"!=typeof l||isNaN(l)?"???":l} incorrect pixel${1==l?"":"s"}. Completed: ${o}`)}i(this,B,P).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}};B=new WeakSet,I=function(t){const e=t.closest(`#${this.Ct}`)?.classList.contains("bm-N"),i=new M(this.name,this.version);i.H({id:this.Jt});const n=this.le();for(const t of this.palette){const s="#"+f(t.rgb).toUpperCase(),o=p(t.rgb);let a=1.05/(o+.05)>(o+.05)/.05?"white":"black";t.id||(a="transparent");const r="white"==a?"bm-f":"bm-g",{me:l,de:h,ue:c,he:m,ce:d,be:u}=n[t.id],b=!!this.qt.pe.get(t.id);if(e){const e=`background-size: auto 100%; background-repeat: repeat-x; background-image: url("data:image/svg+xml;utf8, ");`;i.H({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==c.slice(-1)?c.slice(0,-1):"0","data-incorrect":u||0}).H({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});${t.premium?e:""}`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.Qt.replace("{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.Qt.replace("Number(t)||0).join(",")});`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.Qt.replace("{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.Qt.replace("{const o=n.getAttribute("data-"+t),a=s.getAttribute("data-"+t),r=parseFloat(o),l=parseFloat(a),h=!isNaN(r),c=!isNaN(l);if(i?n.classList.remove("bm-I"):Number(n.getAttribute("data-total"))||n.classList.add("bm-I"),h&&c)return"ascending"===e?r-l:l-r;{const t=o.toLowerCase(),i=a.toLowerCase();return ti?"ascending"===e?1:-1:0}}),s.forEach(t=>n.appendChild(t))},A=function(t){const e=document.querySelector(`#${this.Jt}`),i=Array.from(e.children);for(const e of i){if(e.classList?.contains("bm-I"))continue;const i=e.querySelector(".bm-a button");("hidden"!=i.dataset.state||t)&&("shown"==i.dataset.state&&t||i.click())}},W=function(){this.oe=0,this.se=0,this.ne=new Map,this.ie=new Map;for(const t of this.qt.fe){const e=t.Bt?.total??0;this.oe+=e??0;const i=t.Bt?.colors??new Map;for(const[t,e]of i){const i=Number(e)||0,n=this.ie.get(t)??0;this.ie.set(t,n+i)}const n=t.Bt?.correct??{};this.te+=Object.keys(n).length,this.ee+=Object.keys(t.Ht).length;for(const t of Object.values(n))for(const[e,i]of t){const t=Number(i)||0;this.se+=t;const n=this.ne.get(e)??0;this.ne.set(e,n+t)}}this.se>=this.oe&&this.oe&&this.te==this.ee&&(new N).Xt(document.querySelector(`#${this.Ct}`)),this.timeRemaining=new Date(30*(this.oe-this.se)*1e3+Date.now()),this.ae=a(this.timeRemaining)};var R=class extends M{constructor(t,i,n,s=void 0){super(t,i),e(this,V),this.window=null,this.Ct="bm-u",this.Mt=document.body,this.ge=JSON.parse(GM_getValue("bmTemplates","{}")),this.scriptVersion=this.ge?.scriptVersion,this.schemaVersion=this.ge?.schemaVersion,this.we=void 0,this.xe=n,this.qt=s}Tt(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();let t="";document.querySelector("#bm-F")||(t=t.concat("z-index: 9001;").trim()),this.window=this.H({id:this.Ct,class:"bm-W",style:t},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Template Wizard"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Template Wizard"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Status"}).D().N({id:"bm-v",textContent:"Loading template storage status..."}).D().D().H({class:"bm-L bm-H"}).W(2,{textContent:"Detected templates:"}).D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`),i(this,V,_).call(this),i(this,V,z).call(this)}};V=new WeakSet,_=function(){const t=this.schemaVersion.split(/[-\.\+]/),e=this.xe.split(/[-\.\+]/);let n="";t[0]==e[0]?t[1]==e[1]?(n='Template storage health: Healthy! No futher action required. (Reason: Semantic version matches)',this.we="Good"):(n='Template storage health: Poor! You can still use your template, but some features may not work. It is recommended that you update Blue Marble\'s template storage. (Reason: MINOR version mismatch)',this.we="Poor"):t[0]Bad! It is guaranteed that some features are broken. You might still be able to use the template. It is HIGHLY recommended that you download all templates and update Blue Marble\'s template storage before continuing. (Reason: MAJOR version mismatch)',this.we="Bad"):(n='Template storage health: Dead! Blue Marble can not load the template storage. (Reason: MAJOR version unknown)',this.we="Dead");const s=` If you want to continue using your current templates, then make sure the template storage (schema) is up-to-date. If you don't want to update the template storage, then downgrade Blue Marble to version ${r(this.scriptVersion)} to continue using your templates. Alternatively, if you don't care about corrupting the templates listed below, you can fix any issues with the template storage by uploading a new template.`,o=function(){const t=[...document.querySelectorAll("body > div > .hidden")].filter(t=>/version:/i.test(t.textContent));if(t[0]){const e=t[0].textContent?.match(/\d+/);return e?new Date(Number(e[0])):void 0}}();let l=o?a(o):"???";this.ct("#bm-v",`${n} Your templates were created during Blue Marble version ${r(this.scriptVersion)} with schema version ${r(this.schemaVersion)} . The current Blue Marble version is ${r(this.version)} and requires schema version ${r(this.xe)} . Wplace was last updated on ${l} .${"Good"!=this.we?s:""}`);const h=new M(this.name,this.version);"Dead"!=this.we&&(h.H({class:"bm-L bm-D bm-h",style:"gap: 1.5ch;"}),h.lt({textContent:"Download all templates"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.qt.ye().then(()=>{e.disabled=!1})}}).D()),"Poor"!=this.we&&"Bad"!=this.we||h.lt({textContent:`Update template storage to ${this.xe}`},(t,e)=>{e.onclick=()=>{e.disabled=!0,i(this,V,F).call(this,!0)}}).D(),h.D().L(document.querySelector("#bm-v").parentNode)},z=function(){const t=this.ge?.templates;if(Object.keys(t).length>0){const e=document.querySelector(`#${this.Ct} .bm-H`),i=new M(this.name,this.version);i.H({id:"bm-B",class:"bm-L"});for(const e in t){const n=e,o=t[e];if(t.hasOwnProperty(e)){const t=n.split(" "),e=Number(t?.[0]),a=d(t?.[1]||"0",this.qt.ve),r=o.name||`Template ${e||""}`,l=o?.coords?.split(",").map(Number),h=o.pixels?.total??void 0,c=void 0,m="number"==typeof e?s(e):"???",u="number"==typeof a?s(a):"???",b="number"==typeof h?s(h):"???";i.H({class:"bm-L bm-D"}).H({class:"bm-D",style:"flex-direction: column; gap: 0;"}).H({class:"bm-1",textContent:c||"🖼️"}).D().O({textContent:`#${m}`}).D().D().H({class:"bm-D bm-0"}).W(3,{textContent:r}).D().B({textContent:`Uploaded by user #${u}`}).D().B({textContent:`Coordinates: ${l.join(", ")}`}).D().B({textContent:`Total Pixels: ${b}`}).D().D().D()}}i.D().L(e)}},F=async function(t){if(t){const t=document.querySelector(`#${this.Ct} .bm-m`);t.innerHTML="",new M(this.name,this.version).H({class:"bm-L"}).H({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Status"}).D().N({textContent:"Updating template storage. Please wait..."}).D().D().D().L(t)}GM_deleteValue("bmCoords");const e=this.ge?.templates;if(Object.keys(e).length>0)for(const[t,i]of Object.entries(e))if(e.hasOwnProperty(t)){const t=new H({displayName:i.name,Ht:i.tiles});t.Rt();const e=await this.qt.Ce(t);await this.qt.Me(e,t.displayName,t.coords)}t&&(document.querySelector(`#${this.Ct}`).remove(),new R(this.name,this.version,this.xe,this.qt).Tt())};var j,E,Y,X,J,q,Z,Q,K,tt=R;j=new WeakSet,E=function(){new G(this).Tt()},Y=async function(t,e,i){i.preventDefault();const n=await async function(t){let e="";return t&&(e=t.clipboardData.getData("text/plain")),0!=e.length||(await navigator.clipboard.readText().then(t=>{e=t}).catch(t=>{l("Failed to retrieve clipboard data using navigator! Using fallback methods...")}),0!=e.length||(e=window.clipboardData?.getData("Text"))),e}(i),s=n.split(/[^a-zA-Z0-9]+/).filter(t=>t).map(Number).filter(t=>!isNaN(t));2==s.length&&"bm-O"==e.id?(t.ct("bm-O",s?.[0]||""),t.ct("bm-P",s?.[1]||"")):1==s.length?t.ct(e.id,s?.[0]||""):(t.ct("bm-Q",s?.[0]||""),t.ct("bm-R",s?.[1]||""),t.ct("bm-O",s?.[2]||""),t.ct("bm-P",s?.[3]||""))},X=new WeakSet,J=async function(){GM.setValue("bmTemplates",JSON.stringify(this.Te))},q=async function(t){const e=t.templates,i=t?.schemaVersion,n=i.split(/[-\.\+]/),s=this.schemaVersion.split(/[-\.\+]/),o=t?.scriptVersion;n[0]==s[0]?(n[1]!=s[1]&&new tt(this.name,this.version,this.schemaVersion,this).Tt(),this.fe=await async function({Ot:t,$e:i,fe:n}){if(Object.keys(e).length>0)for(const s in e){const o=s,a=e[s];if(e.hasOwnProperty(s)){const e=o.split(" "),s=Number(e?.[0]),r=e?.[1]||"0",l=a.name||`Template ${s||""}`,h={total:a.pixels?.total,colors:new Map(Object.entries(a.pixels?.colors||{}).map(([t,e])=>[Number(t),e]))},c=a.tiles,m={},d={},u=t*i;for(const t in c)if(c.hasOwnProperty(t)){const e=b(c[t]),i=new Blob([e],{type:"image/png"}),n=await createImageBitmap(i);m[t]=n;const s=new OffscreenCanvas(u,u).getContext("2d");s.drawImage(n,0,0);const o=s.getImageData(0,0,n.width,n.height);d[t]=new Uint32Array(o.data.buffer)}const p=new H({displayName:l,Dt:s||this.fe?.length||0,Lt:r||""});p.Bt=h,p.Ht=m,p.Nt=d,n.push(p)}}return n}({Ot:this.Ot,$e:this.$e,fe:this.fe})):n[0]>>24&255,y=g>>>24&255,v=b.get(w)??-2,C=b.get(g)??-2;if(this.pe.get(v)&&(e[i*h+c]=g),-1==v){const t=536870912;this.pe.get(v)?e[i*h+c]=0:(u/o&1)==(f/o&1)?(e[i*h+c]=t,e[(i-1)*h+(c-1)]=t,e[(i-1)*h+(c+1)]=t,e[(i+1)*h+(c-1)]=t,e[(i+1)*h+(c+1)]=t):(e[i*h+c]=0,e[(i-1)*h+c]=t,e[(i+1)*h+c]=t,e[i*h+(c-1)]=t,e[i*h+(c+1)]=t)}if(!s&&x>m&&C!=v&&(d||y>m)){const t=e[i*h+c];for(const s of n){const[n,o,a]=s,r=0!=n?1!=n?t:4278190335:0;e[(i+a)*h+(c+o)]=r}}if(-1==v&&g<=m){const t=p.get(v);p.set(v,t?t+1:1);continue}if(x<=m||y<=m)continue;if(C!=v)continue;const M=p.get(v);p.set(v,M?M+1:1)}return{Be:p,Ie:e}},Q=new WeakSet,K=function(t){const e=JSON.parse(GM_getValue("bmUserSettings","{}"));e.telemetry=t,GM.setValue("bmUserSettings",JSON.stringify(e))};var et=GM_info.script.name.toString(),it=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-11",et),e.setAttribute("bm-X","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement?.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=t?.getAttribute("bm-11")||"Blue Marble",i=t?.getAttribute("bm-X")||"",n=new Map;window.addEventListener("message",t=>{const{source:s,endpoint:o,blobID:a,blobData:r,blink:l}=t.data;if(Date.now(),"blue-marble"==s&&a&&r&&!o){const t=n.get(a);"function"==typeof t?t(r):c(`%c${e}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`,i,"",a),n.delete(a)}});const s=window.fetch;window.fetch=async function(...t){const e=await s.apply(this,t),i=e.clone(),o=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",a=i.headers.get("content-type")||"";if(a.includes("application/json"))i.json().then(t=>{window.postMessage({source:"blue-marble",endpoint:o,jsonData:t},"*")}).catch(t=>{});else if(a.includes("image/")&&!o.includes("openfreemap")&&!o.includes("maps")){const t=Date.now(),e=await i.blob();return new Promise(s=>{const a=crypto.randomUUID();n.set(a,t=>{s(new Response(t,{headers:i.headers,status:i.status,statusText:i.statusText}))}),window.postMessage({source:"blue-marble",endpoint:o,blobID:a,blobData:e,blink:t})}).catch(t=>{Date.now()})}return e}});var nt=GM_getResourceText("CSS-BM-File");GM_addStyle(nt);var st,ot="robotoMonoInjectionPoint";ot.indexOf("@font-face")+1?GM_addStyle(ot):((st=document.createElement("link")).href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap",st.rel="preload",st.as="style",st.onload=function(){this.onload=null,this.rel="stylesheet"},document.head?.appendChild(st));var at=JSON.parse(GM_getValue("bmUserSettings","{}")),rt=(new class{constructor(){this.Pe=null,this.Ae=null,this.We="#bm-p"}Ve(t){return this.Ae=t,this.Pe=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.We)}),this}_e(){return this.Pe}observe(t,e=!1,i=!1){t.observe(this.Ae,{childList:e,subtree:i})}},new class extends M{constructor(t,i){super(t,i),e(this,j),this.window=null,this.Ct="bm-F",this.Mt=document.body}Tt(){document.querySelector(`#${this.Ct}`)?this.yt("Main window already exists!"):(this.window=this.H({id:this.Ct,class:"bm-W bm-N",style:"top: 10px; left: unset; right: 75px;"},(t,e)=>{}).ft().lt({class:"bm-s",textContent:"▼","aria-label":'Minimize window "Blue Marble"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().H().D().D().H({class:"bm-m"}).H({class:"bm-L"}).A({class:"bm-T",src:"https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png"},(t,e)=>{const i=new Date;204==Math.floor((i.getTime()-new Date(i.getFullYear(),0,1))/864e5)+1&&(e.parentNode.style.position="relative",e.parentNode.innerHTML=e.parentNode.innerHTML+' ',e.onload=()=>{(new N).Xt(document.querySelector(`#${this.Ct}`))})}).D().W(1,{textContent:this.name}).D().D().V().D().H({class:"bm-L"}).B({id:"bm-w",textContent:"Droplets:"}).D()._().D().B({id:"bm-q",textContent:"Next level in..."}).D()._().D().B({textContent:"Charges: "}).gt(Date.now(),1e3,{style:"font-weight: 700;"},(t,e)=>{t.p.ze=e.id}).D().D().D().V().D().H({class:"bm-L"}).H({class:"bm-L"}).lt({class:"bm-s bm-J",style:"margin-top: 0;",innerHTML:' '},(t,e)=>{e.onclick=()=>{const e=t.p?.Fe;e?.[0]?(t.ct("bm-Q",e?.[0]||""),t.ct("bm-R",e?.[1]||""),t.ct("bm-O",e?.[2]||""),t.ct("bm-P",e?.[3]||"")):t.yt("Coordinates are malformed! Did you try clicking on the canvas first?")}}).D().dt({type:"number",id:"bm-Q",class:"bm-C",placeholder:"Tl X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-R",class:"bm-C",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-O",class:"bm-C",placeholder:"Px X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().dt({type:"number",id:"bm-P",class:"bm-C",placeholder:"Px Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",n=>i(this,j,Y).call(this,t,e,n))}).D().D().H({class:"bm-L"}).ut({class:"bm-K",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).D().D().H({class:"bm-L bm-x"}).lt({textContent:"Disable","data-button-status":"shown"},(t,e)=>{e.onclick=()=>{e.disabled=!0,"shown"==e.dataset.buttonStatus?(t.p?.qt?.Ue(!1),e.dataset.buttonStatus="hidden",e.textContent="Enable",t.vt("Disabled templates!")):(t.p?.qt?.Ue(!0),e.dataset.buttonStatus="shown",e.textContent="Disable",t.vt("Enabled templates!")),e.disabled=!1}}).D().lt({textContent:"Create"},(t,e)=>{e.onclick=()=>{const e=document.querySelector(`#${this.Ct} .bm-K`),i=document.querySelector("#bm-Q");if(!i.checkValidity())return i.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const n=document.querySelector("#bm-R");if(!n.checkValidity())return n.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-O");if(!s.checkValidity())return s.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-P");if(!o.checkValidity())return o.reportValidity(),void t.yt("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(t?.p?.qt.Me(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(i.value),Number(n.value),Number(s.value),Number(o.value)]),t.vt("Drew to canvas!")):t.yt("No file selected!")}}).D().lt({textContent:"Filter"},(t,e)=>{e.onclick=()=>i(this,j,E).call(this)}).D().D().H({class:"bm-L"}).bt({id:this.C,placeholder:`Status: Sleeping...\nVersion: ${this.version}`,readOnly:!0}).D().D().H({class:"bm-L bm-x",style:"margin-bottom: 0; flex-direction: column;"}).H({class:"bm-x"}).lt({class:"bm-s",innerHTML:"⚙️",title:"Settings"},(t,e)=>{e.onclick=()=>{t.v.Tt()}}).D().lt({class:"bm-s",innerHTML:"🧙",title:"Template Wizard"},(t,e)=>{e.onclick=()=>{const e=t.p?.qt;new tt(this.name,this.version,e?.schemaVersion,e).Tt()}}).D().lt({class:"bm-s",innerHTML:"🎨",title:"Template Color Converter"},(t,e)=>{e.onclick=()=>{window.open("https://pepoafonso.github.io/color_converter_wplace/","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",innerHTML:"🌐",title:"Official Blue Marble Website"},(t,e)=>{e.onclick=()=>{window.open("https://bluemarble.lol/","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",title:"Donate to SwingTheVine",innerHTML:' '},(t,e)=>{e.onclick=()=>{window.open("https://ko-fi.com/swingthevine","_blank","noopener noreferrer")}}).D().lt({class:"bm-s",innerHTML:"🤝",title:"Credits"},(t,e)=>{e.onclick=()=>{new U(this.name,this.version).Tt()}}).D().D().O({textContent:"Made by SwingTheVine",style:"margin-top: auto;"}).D().D().D().D().D().L(this.Mt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}}(et,it)),lt=new class{constructor(t,i){e(this,X),this.name=t,this.version=i,this.Se=null,this.v=null,this.schemaVersion="2.0.0",this.Ge=null,this.ve="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.Ot=1e3,this.$e=3,this.Oe=3,this.Kt=function(t){const e=C;e.unshift({id:-1,premium:!1,name:"Erased",rgb:[222,250,206]}),e.unshift({id:-2,premium:!1,name:"Other",rgb:[0,0,0]});const i=new Map;for(const n of e){if(0==n.id||-2==n.id)continue;const e=n.rgb[0],s=n.rgb[1],o=n.rgb[2];for(let a=-t;a<=t;a++)for(let r=-t;r<=t;r++)for(let l=-t;l<=t;l++){const t=e+a,h=s+r,c=o+l;if(t<0||t>255||h<0||h>255||c<0||c>255)continue;const m=(255<<24|c<<16|h<<8|t)>>>0;i.has(m)||i.set(m,n.id)}}return{palette:e,jt:i}}(this.Oe),this.De=null,this.Re="",this.fe=[],this.Te=null,this.je=!0,this.Ee=null,this.pe=new Map}Ye(t){this.Se=t}k(t){this.v=t}async Xe(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.schemaVersion,templates:{}}}async Me(t,e,n){this.Te||(this.Te=await this.Xe()),this.Se.vt(`Creating template at ${n.join(", ")}...`);const s=new H({displayName:e,Dt:0,Lt:m(this.Ge||0,this.ve),file:t,coords:n}),o=!this.v?.kt?.flags?.includes("hl-noSkip"),a=this.v?.kt?.flags?.includes("hl-agSkip"),{Ut:r,Gt:l}=await s.At(this.Ot,this.Kt,o,a);s.Ht=r;const h={total:s.Bt.total,colors:Object.fromEntries(s.Bt.colors)};this.Te.templates[`${s.Dt} ${s.Lt}`]={name:s.displayName,coords:n.join(", "),enabled:!0,pixels:h,tiles:l},this.fe=[],this.fe.push(s),this.Se.vt(`Template created at ${n.join(", ")}!`),await i(this,X,J).call(this)}Je(){}async qe(){this.Te||(this.Te=await this.Xe())}async Ze(){l("Downloading all templates...");for(const t of this.fe)await this.Qe(t),await n(500)}async ye(){const t=JSON.parse(GM_getValue("bmTemplates","{}"))?.templates;if(Object.keys(t).length>0)for(const[e,i]of Object.entries(t))t.hasOwnProperty(e)&&(await this.Qe(new H({displayName:i.name,Dt:e.split(" ")?.[0],Lt:e.split(" ")?.[1],Ht:i.tiles})),await n(500))}async Qe(t){t.Rt();const e=`${t.coords.join("-")}_${t.displayName.replaceAll(" ","-")}`,i=await this.Ce(t);await GM.download({url:URL.createObjectURL(i),name:e+".png",Ke:"uniquify",onload:()=>{l(`Download of template '${e}' complete!`)},onerror:(t,i)=>{h(`Download of template '${e}' failed because ${t}! Details: ${i}`)},ontimeout:()=>{c(`Download of template '${e}' has timed out!`)}})}async Ce(t){const e=t.Ht,i=Object.keys(e).sort(),n=await Promise.all(i.map(t=>{return i=e[t],new Promise((t,e)=>{const n=new Image;n.onload=()=>t(n),n.onerror=e,n.src="data:image/png;base64,"+i});var i}));let s=1/0,o=1/0,a=0,r=0;i.forEach((t,e)=>{const[i,l,h,c]=t.split(",").map(Number),m=n[e],d=i*this.Ot+h,u=l*this.Ot+c;s=Math.min(s,d),o=Math.min(o,u),a=Math.max(a,d+m.width/this.$e),r=Math.max(r,u+m.height/this.$e)});const l=a-s,h=r-o,c=l*this.$e,m=h*this.$e,d=new OffscreenCanvas(c,m),u=d.getContext("2d");i.forEach((t,e)=>{const[i,a,r,l]=t.split(",").map(Number),h=n[e],c=i*this.Ot+r,m=a*this.Ot+l;u.drawImage(h,(c-s)*this.$e,(m-o)*this.$e,h.width,h.height)}),u.globalCompositeOperation="destination-over",u.drawImage(d,0,-1),u.drawImage(d,0,1),u.drawImage(d,-1,0),u.drawImage(d,1,0);const b=new OffscreenCanvas(l,h),p=b.getContext("2d");return p.imageSmoothingEnabled=!1,p.drawImage(d,0,0,l*this.$e,h*this.$e,0,0,l,h),b.convertToBlob({type:"image/png"})}async ti(t,e){if(!this.je)return t;const n=this.Ot*this.$e;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0");const o=this.fe;o.sort((t,e)=>t.Dt-e.Dt);const a=o.map(t=>{const i=Object.keys(t.Ht).filter(t=>t.startsWith(e));if(0===i.length)return null;const n=i.map(e=>{const i=e.split(",");return{ei:t,Vt:t.Ht[e],Nt:t.Nt?.[e],ii:[i[0],i[1]],ni:[i[2],i[3]]}});return n?.[0]}).filter(Boolean),r=a?.length||0;if(!(r>0))return this.Se.vt(`Sleeping\nVersion: ${this.version}`),t;{const t=s(o.filter(t=>Object.keys(t.Ht).filter(t=>t.startsWith(e)).length>0).reduce((t,e)=>t+(e.Bt.total||0),0));this.Se.vt(`Displaying ${r} template${1==r?"":"s"}.\nTotal pixels: ${t}`)}const l=await createImageBitmap(t),h=new OffscreenCanvas(n,n),c=h.getContext("2d");c.imageSmoothingEnabled=!1,c.beginPath(),c.rect(0,0,n,n),c.clip(),c.clearRect(0,0,n,n),c.drawImage(l,0,0,n,n);const m=c.getImageData(0,0,n,n),d=new Uint32Array(m.data.buffer),u=this.v?.kt?.highlight||[[2,0,0]],b=u?.[0],p=1==u?.length&&2==b?.[0]&&0==b?.[1]&&0==b?.[2];for(const t of a){const n=!!t.ei.Bt?.colors?.get(-1);let s=t.Nt.slice();const o=Number(t.ni[0])*this.$e,a=Number(t.ni[1])*this.$e;if(0!=this.pe.size||n||c.drawImage(t.Vt,o,a),!s){const e=c.getImageData(o,a,t.Vt.width,t.Vt.height);s=new Uint32Array(e.data.buffer)}Date.now();const{Be:r,Ie:l}=i(this,X,Z).call(this,{ke:d,De:s,Le:[o,a,t.Vt.width,t.Vt.height],He:u,Ne:p});let h=0;const m=0;for(const[t,e]of r)t!=m&&(h+=e);0==this.pe.size&&!n&&p||c.drawImage(await createImageBitmap(new ImageData(new Uint8ClampedArray(l.buffer),t.Vt.width,t.Vt.height)),o,a),void 0===t.ei.Bt.correct&&(t.ei.Bt.correct={}),t.ei.Bt.correct[e]=r}return await h.convertToBlob({type:"image/png"})}si(t){"BlueMarble"==t?.whoami&&i(this,X,q).call(this,t)}Ue(t){this.je=t}}(et,it),ht=new class{constructor(t){this.qt=t,this.oi=!1,this.ze="",this.Fe=[],this.ai=[]}ri(t){window.addEventListener("message",async e=>{const i=e.data,n=i.jsonData;if(!i||"blue-marble"!==i.source)return;if(!i.endpoint)return;const o=i.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(o){case"me":if(n.status&&"2"!=n.status?.toString()[0])return void t.yt("You are not logged in or Wplace is offline!\nCould not fetch userdata.");const e=Math.ceil(Math.pow(Math.floor(n.level)*Math.pow(30,.65),1/.65)-n.pixelsPainted);if(n.id||n.id,this.qt.Ge=n.id,0!=this.ze.length){const t=document.querySelector("#"+this.ze);if(t){const e=n.charges;t.dataset.endDate=Date.now()+(e.max-e.count)*e.cooldownMs}}t.ct("bm-w",`Droplets: ${s(n.droplets)} `),t.ct("bm-q",`Next level in ${s(e)} pixel${1==e?"":"s"}`);break;case"pixel":const o=i.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),l=new URLSearchParams(i.endpoint.split("?")[1]),h=[l.get("x"),l.get("y")];if(this.Fe.length&&(!o.length||!h.length))return void t.yt("Coordinates are malformed!\nDid you try clicking the canvas first?");this.Fe=[...o,...h];const c=(a=o,r=h,[parseInt(a[0])%4*1e3+parseInt(r[0]),parseInt(a[1])%4*1e3+parseInt(r[1])]),m=document.querySelectorAll("span");for(const t of m){const e=t.textContent.trim();if(e.includes(c[0])&&e.includes(c[1])){let e=document.querySelector("#bm-p");o[0],o[1],h[0],h[1];const i=["Tl X:","Tl Y:","Px X:","Px Y:"],n=["bm-Y","bm-Z","bm-U","bm-V"],s=[...o,...h];if(e)for(const[t,e]of n.entries())document.getElementById(e).textContent=`${i[t]??"??:"} ${s[t]}`;else{e=document.createElement("span"),e.id="bm-p",e.style="display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;";for(const[t,o]of s.entries()){const a=document.createElement("span");a.id=n[s.indexOf(o)??""],a.textContent=`${i[t]??"??:"} ${o}`,e.appendChild(a)}t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",e)}}}break;case"tile":case"tiles":let d=i.endpoint.split("/");d=[parseInt(d[d.length-2]),parseInt(d[d.length-1].replace(".png",""))];const u=i.blobID,b=i.blobData,p=(Date.now(),await this.qt.ti(b,d));window.postMessage({source:"blue-marble",blobID:u,blobData:p,blink:i.blink});break;case"robots":this.oi="false"==n.userscript?.toString().toLowerCase()}var a,r})}async li(t){let e=GM_getValue("bmUserSettings","{}");if(e=JSON.parse(e),!e||!e.telemetry||!e.uuid)return;const i=navigator.userAgent;let n=await this.hi(i),s=this.ci(i);GM_xmlhttpRequest({method:"POST",url:"https://telemetry.thebluecorner.net/heartbeat",headers:{"Content-Type":"application/json"},data:JSON.stringify({uuid:e.uuid,version:t,browser:n,os:s}),onload:t=>{200!==t.status&&h("Failed to send heartbeat:",t.statusText)},onerror:t=>{h("Error sending heartbeat:",t)}})}async hi(t=navigator.userAgent){return(t=t||"").includes("OPR/")||t.includes("Opera")?"Opera":t.includes("Edg/")?"Edge":t.includes("Vivaldi")?"Vivaldi":t.includes("YaBrowser")?"Yandex":t.includes("Kiwi")?"Kiwi":t.includes("Brave")?"Brave":t.includes("Firefox/")?"Firefox":t.includes("Chrome/")?"Chrome":t.includes("Safari/")?"Safari":navigator.brave&&"function"==typeof navigator.brave.isBrave&&await navigator.brave.isBrave()?"Brave":"Unknown"}ci(t=navigator.userAgent){return/Windows NT 11/i.test(t=t||"")?"Windows 11":/Windows NT 10/i.test(t)?"Windows 10":/Windows NT 6\.3/i.test(t)?"Windows 8.1":/Windows NT 6\.2/i.test(t)?"Windows 8":/Windows NT 6\.1/i.test(t)?"Windows 7":/Windows NT 6\.0/i.test(t)?"Windows Vista":/Windows NT 5\.1|Windows XP/i.test(t)?"Windows XP":/Mac OS X 10[_\.]15/i.test(t)?"macOS Catalina":/Mac OS X 10[_\.]14/i.test(t)?"macOS Mojave":/Mac OS X 10[_\.]13/i.test(t)?"macOS High Sierra":/Mac OS X 10[_\.]12/i.test(t)?"macOS Sierra":/Mac OS X 10[_\.]11/i.test(t)?"OS X El Capitan":/Mac OS X 10[_\.]10/i.test(t)?"OS X Yosemite":/Mac OS X 10[_\.]/i.test(t)?"macOS":/Android/i.test(t)?"Android":/iPhone|iPad|iPod/i.test(t)?"iOS":/Linux/i.test(t)?"Linux":"Unknown"}}(lt),ct=new class extends L{constructor(t,i,n){var s;super(t,i),e(this,T),this.kt=n,(s=this.kt).flags??(s.flags=[]),this.mi=structuredClone(this.kt),this.di="bmUserSettings",this.ui=5e3,this.bi=0,setInterval(this.pi.bind(this),this.ui)}async pi(){const t=JSON.stringify(this.kt);t!=JSON.stringify(this.mi)&&Date.now()-this.bi>this.ui&&(await GM.setValue(this.di,t),this.mi=structuredClone(this.kt),this.bi=Date.now())}fi(t,e=void 0){const i=this.kt?.flags?.indexOf(t)??-1;-1!=i&&!0!==e?this.kt?.flags?.splice(i,1):-1==i&&!1!==e&&this.kt?.flags?.push(t)}$t(){const t=' ',e=' ',n=this.kt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];this.window=this.H({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().V().D().H({class:"bm-L",style:"margin-left: 1.5ch;"}).R({textContent:"Highlight transparent pixels"},(t,e,i)=>{i.checked=!this.kt?.flags?.includes("hl-noTrans"),i.onchange=t=>this.fi("hl-noTrans",!t.target.checked)}).D().N({id:"bm-4",textContent:"Choose a preset:",style:"font-weight: 700;"}).D().H({class:"bm-D",role:"group","aria-labelledby":"bm-4"}).H({class:"bm-3"}).B({textContent:"None"}).D().lt({innerHTML:t,"aria-label":'Preset "None"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"None")}).D().D().H({class:"bm-3"}).B({textContent:"Cross"}).D().lt({innerHTML:e,"aria-label":'Preset "Cross Shape"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"Cross")}).D().D().H({class:"bm-3"}).B({textContent:"X"}).D().lt({innerHTML:e.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"','d="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'),"aria-label":'Preset "X Shape"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"X")}).D().D().H({class:"bm-3"}).B({textContent:"Full"}).D().lt({innerHTML:t.replace("#fff","#2f4f4f"),"aria-label":'Preset "Full Template"'},(t,e)=>{e.onclick=()=>i(this,T,S).call(this,"Full")}).D().D().D().N({id:"bm-b",textContent:"Create a custom pattern:",style:"font-weight: 700;"}).D().H({class:"bm-n",role:"group","aria-labelledby":"bm-b"});for(let t=-1;t<=1;t++)for(let e=-1;e<=1;e++){const s=n[n.findIndex(([,i,n])=>i==e&&n==t)]?.[0]??0;let o="Disabled";1==s?o="Incorrect":2==s&&(o="Template"),this.window=this.lt({"data-status":o,"aria-label":`Sub-pixel ${o.toLowerCase()}`},(n,s)=>{s.onclick=()=>i(this,T,$).call(this,s,[e,t])}).D()}this.window=this.D().D().D()}St(){this.window=this.H({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().V().D().H({class:"bm-L",style:"margin-left: 1.5ch;"}).R({textContent:"Template creation should skip transparent tiles"},(t,e,i)=>{i.checked=!this.kt?.flags?.includes("hl-noSkip"),i.onchange=t=>this.fi("hl-noSkip",!t.target.checked)}).D().R({innerHTML:"Experimental: Template creation should aggressively skip transparent tiles"},(t,e,i)=>{i.checked=this.kt?.flags?.includes("hl-agSkip"),i.onchange=t=>this.fi("hl-agSkip",t.target.checked)}).D().D().D()}}(et,it,at);rt.k(ct),rt.S(ht),lt.Ye(rt),lt.k(ct);var mt=JSON.parse(GM_getValue("bmTemplates","{}"));if(lt.si(mt),0==Object.keys(at).length){const t=crypto.randomUUID();GM.setValue("bmUserSettings",JSON.stringify({uuid:t}))}setInterval(()=>ht.li(it),18e5);var dt=at?.telemetry;if(null==dt||dt>1){const t=new class extends M{constructor(t,i,n,s){super(t,i),e(this,Q),this.window=null,this.Ct="bm-k",this.Mt=document.body,this.gi=n,this.uuid=s}async Tt(){if(document.querySelector(`#${this.Ct}`))return void this.yt("Telemetry window already exists!");const t=await this.p.hi(navigator.userAgent),e=this.p.ci(navigator.userAgent);this.window=this.H({id:this.Ct,class:"bm-W",style:"height: 80vh; z-index: 9998;"}).H({class:"bm-m"}).H({class:"bm-L bm-h"}).W(1,{textContent:`${this.name} Telemetry`}).D().D().V().D().H({class:"bm-L bm-D",style:"gap: 1.5ch; flex-wrap: wrap;"}).lt({textContent:"Enable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,Q,K).call(this,this.gi);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"Disable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,Q,K).call(this,0);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"More Information"},(t,e)=>{e.onclick=()=>{window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data","_blank","noopener noreferrer")}}).D().D().H({class:"bm-L bm-H"}).H({class:"bm-L"}).W(2,{textContent:"Legal"}).D().N({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}!`}).D().D().V().D().H({class:"bm-L"}).W(2,{textContent:"Non-Legal Summary"}).D().N({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. This is the data stored on our servers:'}).D().J().Z({innerHTML:`A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID. Your UUID is: ${r(this.uuid)} `}).D().Z({innerHTML:`The version of Blue Marble you are using. Your version is: ${r(this.version)} `}).D().Z({innerHTML:`Your browser type, which is used to determine Blue Marble outages and browser popularity. Your browser type is: ${r(t)} `}).D().Z({innerHTML:`Your OS type, which is used to determine Blue Marble outages and OS popularity. Your OS type is: ${r(e)} `}).D().Z({innerHTML:"The date and time that Blue Marble sent the telemetry information."}).D().D().N({innerHTML:'All of the data mentioned above is aggregated every hour . 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.'}).D().D().D().D().D().L(this.Mt)}}(et,it,1,at?.uuid);t.S(ht),t.Tt()}rt.Tt(),ht.ri(rt),new MutationObserver((t,e)=>{const i=document.querySelector("#color-1");if(!i)return;let n=document.querySelector("#bm-G");if(!n){n=document.createElement("button"),n.id="bm-G",n.textContent="Move ↑",n.className="btn btn-soft",n.onclick=function(){const t=this.parentNode.parentNode.parentNode.parentNode,e="Move ↑"==this.textContent;t.parentNode.className=t.parentNode.className.replace(e?"bottom":"top",e?"top":"bottom"),t.style.borderTopLeftRadius=e?"0px":"var(--radius-box)",t.style.borderTopRightRadius=e?"0px":"var(--radius-box)",t.style.borderBottomLeftRadius=e?"var(--radius-box)":"0px",t.style.borderBottomRightRadius=e?"var(--radius-box)":"0px",this.textContent=e?"Move ↓":"Move ↑"};const t=i.parentNode.parentNode.parentNode.parentNode.querySelector("h2");t.parentNode?.appendChild(n)}}).observe(document.body,{childList:!0,subtree:!0}),l(`%c${et}%c (${it}) userscript has loaded!`,"color: cornflowerblue;","")})();
\ No newline at end of file
+(()=>{var t=t=>{throw TypeError(t)},e=(e,i,s)=>i.has(e)?t("Cannot add the same private member more than once"):i instanceof WeakSet?i.add(e):i.set(e,s),i=(e,i,s)=>(((e,i)=>{i.has(e)||t("Cannot access private method")})(e,i),s);function s(t){return new Promise(e=>setTimeout(e,t))}function n(t){return(new Intl.NumberFormat).format(t)}function o(t){return new Intl.NumberFormat(void 0,{style:"percent",t:2,i:2}).format(t)}function a(t){return t.toLocaleString(void 0,{o:"long",l:"numeric",h:"2-digit",m:"2-digit",u:"2-digit"})}function r(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}function l(...t){(0,console.log)(...t)}function c(...t){(0,console.error)(...t)}function h(...t){(0,console.warn)(...t)}function m(t,e){if(0===t)return e[0];let i="";const s=e.length;for(;t>0;)i=e[t%s]+i,t=Math.floor(t/s);return i}function d(t,e){let i=0;const s=e.length;for(const n of t){const t=e.indexOf(n);-1==t&&c(`Invalid character '${n}' encountered whilst decoding! Is the decode alphabet/base incorrect?`),i=i*s+t}return i}function u(t){let e="";for(let i=0;i(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*e[0]+.7152*e[1]+.0722*e[2]}function f(t,e,i){return Array.isArray(t)&&([t,e,i]=t),(1<<24|t<<16|e<<8|i).toString(16).slice(1)}var g,w,x,y=[{id:0,premium:!1,name:"Transparent",rgb:[0,0,0]},{id:1,premium:!1,name:"Black",rgb:[0,0,0]},{id:2,premium:!1,name:"Dark Gray",rgb:[60,60,60]},{id:3,premium:!1,name:"Gray",rgb:[120,120,120]},{id:4,premium:!1,name:"Light Gray",rgb:[210,210,210]},{id:5,premium:!1,name:"White",rgb:[255,255,255]},{id:6,premium:!1,name:"Deep Red",rgb:[96,0,24]},{id:7,premium:!1,name:"Red",rgb:[237,28,36]},{id:8,premium:!1,name:"Orange",rgb:[255,127,39]},{id:9,premium:!1,name:"Gold",rgb:[246,170,9]},{id:10,premium:!1,name:"Yellow",rgb:[249,221,59]},{id:11,premium:!1,name:"Light Yellow",rgb:[255,250,188]},{id:12,premium:!1,name:"Dark Green",rgb:[14,185,104]},{id:13,premium:!1,name:"Green",rgb:[19,230,123]},{id:14,premium:!1,name:"Light Green",rgb:[135,255,94]},{id:15,premium:!1,name:"Dark Teal",rgb:[12,129,110]},{id:16,premium:!1,name:"Teal",rgb:[16,174,166]},{id:17,premium:!1,name:"Light Teal",rgb:[19,225,190]},{id:18,premium:!1,name:"Dark Blue",rgb:[40,80,158]},{id:19,premium:!1,name:"Blue",rgb:[64,147,228]},{id:20,premium:!1,name:"Cyan",rgb:[96,247,242]},{id:21,premium:!1,name:"Indigo",rgb:[107,80,246]},{id:22,premium:!1,name:"Light Indigo",rgb:[153,177,251]},{id:23,premium:!1,name:"Dark Purple",rgb:[120,12,153]},{id:24,premium:!1,name:"Purple",rgb:[170,56,185]},{id:25,premium:!1,name:"Light Purple",rgb:[224,159,249]},{id:26,premium:!1,name:"Dark Pink",rgb:[203,0,122]},{id:27,premium:!1,name:"Pink",rgb:[236,31,128]},{id:28,premium:!1,name:"Light Pink",rgb:[243,141,169]},{id:29,premium:!1,name:"Dark Brown",rgb:[104,70,52]},{id:30,premium:!1,name:"Brown",rgb:[149,104,42]},{id:31,premium:!1,name:"Beige",rgb:[248,178,119]},{id:32,premium:!0,name:"Medium Gray",rgb:[170,170,170]},{id:33,premium:!0,name:"Dark Red",rgb:[165,14,30]},{id:34,premium:!0,name:"Light Red",rgb:[250,128,114]},{id:35,premium:!0,name:"Dark Orange",rgb:[228,92,26]},{id:36,premium:!0,name:"Light Tan",rgb:[214,181,148]},{id:37,premium:!0,name:"Dark Goldenrod",rgb:[156,132,49]},{id:38,premium:!0,name:"Goldenrod",rgb:[197,173,49]},{id:39,premium:!0,name:"Light Goldenrod",rgb:[232,212,95]},{id:40,premium:!0,name:"Dark Olive",rgb:[74,107,58]},{id:41,premium:!0,name:"Olive",rgb:[90,148,74]},{id:42,premium:!0,name:"Light Olive",rgb:[132,197,115]},{id:43,premium:!0,name:"Dark Cyan",rgb:[15,121,159]},{id:44,premium:!0,name:"Light Cyan",rgb:[187,250,242]},{id:45,premium:!0,name:"Light Blue",rgb:[125,199,255]},{id:46,premium:!0,name:"Dark Indigo",rgb:[77,49,184]},{id:47,premium:!0,name:"Dark Slate Blue",rgb:[74,66,132]},{id:48,premium:!0,name:"Slate Blue",rgb:[122,113,196]},{id:49,premium:!0,name:"Light Slate Blue",rgb:[181,174,241]},{id:50,premium:!0,name:"Light Brown",rgb:[219,164,99]},{id:51,premium:!0,name:"Dark Beige",rgb:[209,128,81]},{id:52,premium:!0,name:"Light Beige",rgb:[255,197,165]},{id:53,premium:!0,name:"Dark Peach",rgb:[155,82,73]},{id:54,premium:!0,name:"Peach",rgb:[209,128,120]},{id:55,premium:!0,name:"Light Peach",rgb:[250,182,164]},{id:56,premium:!0,name:"Dark Tan",rgb:[123,99,82]},{id:57,premium:!0,name:"Tan",rgb:[156,132,107]},{id:58,premium:!0,name:"Dark Slate",rgb:[51,57,65]},{id:59,premium:!0,name:"Slate",rgb:[109,117,141]},{id:60,premium:!0,name:"Light Slate",rgb:[179,185,209]},{id:61,premium:!0,name:"Dark Stone",rgb:[109,100,63]},{id:62,premium:!0,name:"Stone",rgb:[148,140,107]},{id:63,premium:!0,name:"Light Stone",rgb:[205,197,158]}],$=' ',v=class{constructor(t,i){e(this,g),this.name=t,this.version=i,this.p=null,this.$=null,this.v="bm-r",this.M=null,this.C=null,this.T=[]}S(t){this.p=t}k(t){this.$=t}D(){return this.T.length>0&&(this.C=this.T.pop()),this}N(t){t?.appendChild(this.M),this.M=null,this.C=null,this.T=[]}L(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{},t)),this}H(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"p",{},t)),this}O(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"small",{},t)),this}I(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"span",{},t)),this}B(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"details",{},t)),this}P(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"summary",{},t)),this}A(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"img",{},t)),this}W(t,e={},s=()=>{}){return s(this,i(this,g,w).call(this,"h"+t,{},e)),this}F(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"hr",{},t)),this}U(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"br",{},t)),this}V(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"form",{},t)),this}G(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"fieldset",{},t)),this}R(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"legend",{},t)),this}j(t={},e=()=>{}){const s={};t.textContent?(s.textContent=t.textContent,delete t.textContent):t.innerHTML&&(s.innerHTML=t.innerHTML,delete t.textContent);const n=i(this,g,w).call(this,"label",s),o=i(this,g,w).call(this,"input",{type:"checkbox"},t);return n.insertBefore(o,n.firstChild),this.D(),e(this,n,o),this}Y(t={},e=()=>{}){const s=i(this,g,w).call(this,"label",{textContent:t.textContent??"",for:t.id??""});return delete t.textContent,this.D(),e(this,s,i(this,g,w).call(this,"select",{},t)),this}J(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"option",{},t)),this}X(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ol",{},t)),this}_(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"ul",{},t)),this}q(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"menu",{},t)),this}Z(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"li",{},t)),this}K(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"table",{},t)),this}tt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"caption",{},t)),this}et(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"thead",{},t)),this}it(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tbody",{},t)),this}st(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tfoot",{},t)),this}nt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"tr",{},t)),this}ot(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"th",{},t)),this}rt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"td",{},t)),this}lt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"button",{},t)),this}ct(t={},e=()=>{}){const s=t.title??t.textContent??"Help: No info";delete t.textContent,t.title=`Help: ${s}`;const n={textContent:"?",className:"bm-10",onclick:()=>{this.ht(this.v,s)}};return e(this,i(this,g,w).call(this,"button",n,t)),this}dt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"input",{},t)),this}ut(t={},e=()=>{}){const s=t.textContent??"";delete t.textContent;const n=i(this,g,w).call(this,"div"),o=i(this,g,w).call(this,"input",{type:"file",tabindex:"-1","aria-hidden":"true"},t);this.D();const a=i(this,g,w).call(this,"button",{textContent:s});return this.D(),this.D(),a.addEventListener("click",()=>{o.click()}),o.addEventListener("change",()=>{a.style.maxWidth=`${a.offsetWidth}px`,o.files.length>0?a.textContent=o.files[0].name:a.textContent=s}),e(this,n,o,a),this}bt(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"textarea",{},t)),this}ft(t={},e=()=>{}){return e(this,i(this,g,w).call(this,"div",{class:"bm-S"},t)),this}gt(t=Date.now(),e=500,s={},n=()=>{}){const o="bm--",a=s?.id||o+"-"+crypto.randomUUID().slice(0,8),r={class:o},l=i(this,g,w).call(this,"time",r,s);return l.id=a,l.dataset.endDate=t,setInterval(()=>{if(!l.isConnected)return;const t=Math.max(l.dataset.endDate-Date.now(),0),e=Math.floor(t/1e3),i=Math.floor(e/3600),s=Math.floor(e%60),n=Math.floor(e%3600/60);l.setAttribute("datetime",`PT${i}H${n}M${s}S`),l.textContent=String(i).padStart(2,"0")+":"+String(n).padStart(2,"0")+":"+String(s).padStart(2,"0")},e),n(this,l),this}ht(t,e,i=!1){const s=document.getElementById(t.replace(/^#/,""));s&&(s instanceof HTMLInputElement?s.value=e:i?s.textContent=e:s.innerHTML=e)}wt(t){if(t.disabled)return;t.disabled=!0,t.style.textDecoration="none";const e=t.closest(".bm-W"),i=t.closest(".bm-S"),s=e?.querySelector("h1"),n=e?.querySelector(".bm-m");if(!e||!i||!n)return t.disabled=!1,void(t.style.textDecoration="");const o=e=>{let i,s=!1;const o=()=>{s||(s=!0,clearTimeout(i),n.removeEventListener("transitionend",a),e(),t.disabled=!1,t.style.textDecoration="")},a=t=>{t.target==n&&"height"==t.propertyName&&o()};n.addEventListener("transitionend",a),i=setTimeout(o,360)};if(e.parentElement.append(e),"expanded"==t.dataset.buttonStatus){e.dataset.widthBeforeMinimize=e.style.width,e.dataset.heightBeforeMinimize=e.style.height,e.dataset.minHeightBeforeMinimize=e.style.minHeight,n.style.height=n.scrollHeight+"px",n.offsetHeight,e.style.width||(e.style.width=e.scrollWidth+"px"),o(()=>{n.style.display="none"}),n.style.height="0",(e.style.height||e.classList.contains("bm-N"))&&(e.style.minHeight="0px",e.style.height=(()=>{const t=getComputedStyle(e),s=t=>parseFloat(t)||0,n="border-box"==t.boxSizing?s(t.paddingTop)+s(t.paddingBottom)+s(t.borderTopWidth)+s(t.borderBottomWidth):0;return Math.ceil(i.getBoundingClientRect().height+n+2)})()+"px");const a=s?.cloneNode(!0)??document.createElement("h1"),r=a.textContent;t.nextElementSibling.appendChild(a),t.innerHTML=' ',t.dataset.buttonStatus="collapsed",t.ariaLabel=`Unminimize window "${r}"`}else{const s=i.querySelector("h1"),a=s.textContent;s.remove(),n.style.display="",n.style.height="0",e.style.width=e.dataset.widthBeforeMinimize??"",e.style.minHeight=e.dataset.minHeightBeforeMinimize??"",e.style.height=e.dataset.heightBeforeMinimize??"",n.offsetHeight,o(()=>{n.style.height="",delete e.dataset.widthBeforeMinimize,delete e.dataset.heightBeforeMinimize,delete e.dataset.minHeightBeforeMinimize}),n.style.height=n.scrollHeight+"px",t.innerHTML=$,t.dataset.buttonStatus="expanded",t.ariaLabel=`Minimize window "${a}"`}}xt(t,e,i={}){const s=document.querySelector(t),n=document.querySelector(e),o=i?.yt??(()=>{});if(!s||!n)return void this.$t(`Can not drag! ${s?"":"moveMe"} ${s||n?"":"and "}${n?"":"iMoveThings "}was not found!`);let a,r=!1,l=0,c=null,h=0,m=0,d=0,u=0,b=null;const p=()=>{if(r){const t=Math.abs(h-d),e=Math.abs(m-u);(t>.5||e>.5)&&(h=d,m=u,s.style.transform=`translate(${h}px, ${m}px)`,s.style.left="0px",s.style.top="0px",s.style.right=""),c=requestAnimationFrame(p)}},f=(t,e)=>{r=!0,b=s.getBoundingClientRect(),a=t-b.left,l=e-b.top;const i=window.getComputedStyle(s).transform;if(i&&"none"!==i){const t=new DOMMatrix(i);h=t.m41,m=t.m42}else h=b.left,m=b.top;d=h,u=m,document.body.style.userSelect="none",n.classList.add("bm-M"),document.addEventListener("mousemove",w),document.addEventListener("touchmove",x,{passive:!1}),document.addEventListener("mouseup",g),document.addEventListener("touchend",g),document.addEventListener("touchcancel",g),c&&cancelAnimationFrame(c),p()},g=()=>{r=!1,c&&(cancelAnimationFrame(c),c=null),document.body.style.userSelect="",n.classList.remove("bm-M"),document.removeEventListener("mousemove",w),document.removeEventListener("touchmove",x),document.removeEventListener("mouseup",g),document.removeEventListener("touchend",g),document.removeEventListener("touchcancel",g),o({element:s,x:h,y:m}),b=null},w=t=>{r&&b&&(d=t.clientX-a,u=t.clientY-l)},x=t=>{if(r&&b){const e=t.touches[0];if(!e)return;d=e.clientX-a,u=e.clientY-l,t.preventDefault()}};n.addEventListener("mousedown",function(t){t.preventDefault(),f(t.clientX,t.clientY)}),n.addEventListener("touchstart",function(t){const e=t?.touches?.[0];e&&(f(e.clientX,e.clientY),t.preventDefault())},{passive:!1})}vt(t,e,i={}){const s=document.querySelector(t),n=document.querySelector(e),o=i?.yt??(()=>{});if(!s||!n)return void this.$t(`Can not resize! ${s?"":"resizeMe"} ${s||n?"":"and "}${n?"":"iResizeThings "}was not found!`);let a=!1,r=0,l=0,c=0,h=0,m=0,d=0,u=0,b=0,p=null;const f=()=>Number.isFinite(i?.maxWidth)?i.maxWidth:window.innerWidth-16,g=()=>Number.isFinite(i?.maxHeight)?i.maxHeight:window.innerHeight-16,w=Number.isFinite(i?.minWidth)?i.minWidth:200,x=Number.isFinite(i?.minHeight)?i.minHeight:160,y=(t,e,i)=>Math.min(Math.max(t,e),Math.max(e,i)),$=()=>{if(a){const t=Math.abs(m-u),e=Math.abs(d-b);(t>.5||e>.5)&&(m=u,d=b,s.style.width=`${m}px`,s.style.height=`${d}px`),p=requestAnimationFrame($)}},v=(t,e)=>{a=!0,r=t,l=e,c=s.offsetWidth,h=s.offsetHeight,m=c,d=h,u=c,b=h,document.body.style.userSelect="none",n.classList.add("bm-2g"),document.addEventListener("mousemove",C),document.addEventListener("touchmove",T,{passive:!1}),document.addEventListener("mouseup",M),document.addEventListener("touchend",M),document.addEventListener("touchcancel",M),p&&cancelAnimationFrame(p),$()},M=()=>{a=!1,p&&(cancelAnimationFrame(p),p=null),document.body.style.userSelect="",n.classList.remove("bm-2g"),document.removeEventListener("mousemove",C),document.removeEventListener("touchmove",T),document.removeEventListener("mouseup",M),document.removeEventListener("touchend",M),document.removeEventListener("touchcancel",M),o({element:s,width:m,height:d})},C=t=>{a&&(u=y(c+(t.clientX-r),w,f()),b=y(h+(t.clientY-l),x,g()))},T=t=>{if(!a)return;const e=t?.touches?.[0];e&&(u=y(c+(e.clientX-r),w,f()),b=y(h+(e.clientY-l),x,g()),t.preventDefault())};n.addEventListener("mousedown",t=>{t.preventDefault(),t.stopPropagation(),v(t.clientX,t.clientY)}),n.addEventListener("touchstart",t=>{const e=t?.touches?.[0];e&&(t.preventDefault(),t.stopPropagation(),v(e.clientX,e.clientY))},{passive:!1})}Mt(t){(0,console.info)(`${this.name}: ${t}`),this.ht(this.v,"Status: "+t,!0)}$t(t){(0,console.error)(`${this.name}: ${t}`),this.ht(this.v,"Error: "+t,!0)}};g=new WeakSet,w=function(t,e={},s={}){const n=document.createElement(t);this.M?(this.C?.appendChild(n),this.T.push(this.C),this.C=n):(this.M=n,this.C=n);for(const[t,s]of Object.entries(e))i(this,g,x).call(this,n,t,s);for(const[t,e]of Object.entries(s))i(this,g,x).call(this,n,t,e);return n},x=function(t,e,i){"class"==e?t.classList.add(...i.split(/\s+/)):"for"==e?t.htmlFor=i:"tabindex"==e?t.tabIndex=Number(i):"readonly"==e?t.readOnly="true"==i||"1"==i:"maxlength"==e?t.maxLength=Number(i):e.startsWith("data")?t.dataset[e.slice(5).split("-").map((t,e)=>0==e?t:t[0].toUpperCase()+t.slice(1)).join("")]=i:e.startsWith("aria")?t.setAttribute(e,i):t[e]=i};var M,C,T,S,k,D,N,L=class extends v{constructor(t,i){super(t,i),e(this,M),this.window=null,this.Ct="bm-l",this.Tt=document.body}St(){document.querySelector(`#${this.Ct}`)?document.querySelector(`#${this.Ct}`).remove():(this.window=this.L({id:this.Ct,class:"bm-W"}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Settings"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',"aria-label":'Close window "Settings"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Settings"}).D().D().F().D().H({textContent:"Settings take 5 seconds to save."}).D().L({class:"bm-L bm-H"},(t,e)=>{this.kt(),this.Dt()}).D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}kt(){i(this,M,C).call(this,"Pixel Highlight")}Dt(){i(this,M,C).call(this,"Template")}};M=new WeakSet,C=function(t){this.window=this.L({class:"bm-L"}).W(2,{textContent:t}).D().F().D().H({innerHTML:`An error occured loading the ${t} category. SettingsManager failed to override the ${t} function inside WindowSettings.`}).D().D()},T=new WeakSet,S=function(t,e){t.disabled=!0;const i=t.dataset.status,s=this.Nt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];let n=[2,0,0];const o=s;switch(i){case"Disabled":t.dataset.status="Incorrect",t.ariaLabel="Sub-pixel incorrect",n=[1,...e];break;case"Incorrect":t.dataset.status="Template",t.ariaLabel="Sub-pixel template",n=[2,...e];break;case"Template":t.dataset.status="Disabled",t.ariaLabel="Sub-pixel disabled",n=[0,...e];break}const a=s.findIndex(([,t,e])=>t==n[1]&&e==n[2]);0!=n[0]?-1!=a?o[a]=n:o.push(n):-1!=a&&o.splice(a,1),this.Nt.highlight=o,t.disabled=!1},k=async function(t){const e=document.querySelectorAll(".bm-3 button");for(const t of e)t.disabled=!0;let i=[0,0,0,0,2,0,0,0,0];switch(t){case"Cross":i=[0,1,0,1,2,1,0,1,0];break;case"X":i=[1,0,1,0,2,0,1,0,1];break;case"Full":i=[2,2,2,2,2,2,2,2,2];break}const n=document.querySelector(".bm-n")?.childNodes??[];for(let t=0;t{const[s,n,o,a]=e.split(",").map(Number);(n>>24==0?0:n.get(e)??-2;const a=o.get(s);o.set(s,a?a+1:1)}return console.log(o),o};var O=class{constructor(){this.Xt=Math.ceil(80/1300*window.innerWidth),this._t=y.slice(1)}qt(t){const e=document.createElement("div");for(let t=0;t{t.parentNode.childElementCount<=1?t.parentNode.remove():t.remove()},e.appendChild(t)}t.appendChild(e)}},I=class extends HTMLElement{};customElements.define("confetti-piece",I);var B,P,A,z,W,F,U,V,G,R,j,E,Y,J,X,_,q,Z,K,Q,tt,et,it,st,nt,ot=' ',at=' ',rt=class extends v{constructor(t){super(t.name,t.version),e(this,B),this.window=null,this.Ct="bm-t",this.Zt="bm-E",this.Tt=document.body,this.$=t.$??null,this.Kt="ftr-oWin",this.Qt="windowFilter",this.te=null,this.ee=null,this.ie=null,this.se=null,this.ne=1e4,this.oe=360,this.ae=220,this.re=1e3,this.le=1400,this.ce=t.p?.ce,this.he=' ',this.me=' ';const{palette:i,Jt:s}=this.ce.de;this.palette=i,this.ue=0,this.be=0,this.pe=new Map,this.fe=new Map,this.ge=0,this.we=0,this.timeRemaining=0,this.xe="",this.sortPrimary="total",this.sortSecondary="descending",this.showUnused=!1}ye(){i(this,B,A).call(this)?this.$e():this.St()}St(){if(document.querySelector(`#${this.Ct}`))return void i(this,B,F).call(this);this.window=this.L({id:this.Ct,class:"bm-W"},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:at,"aria-label":'Switch to windowed mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{i(this,B,z).call(this,!0),i(this,B,F).call(this),this.$e()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",innerHTML:ot,"aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>i(this,B,F).call(this),e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h bm-1N"}).W(1,{textContent:"Color Filter"}).D().D().F().D().L({class:"bm-L bm-x bm-h bm-1F",style:"gap: 1.5ch;"}).lt({class:"bm-1u",textContent:"Hide All Colors"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!1)}).D().lt({class:"bm-1u",textContent:"Show All Colors"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!0)}).D().D().F().D().L({class:"bm-L bm-H bm-1r"}).L({class:"bm-L bm-1A",style:"margin-left: 2.5ch; margin-right: 2.5ch;"}).L({class:"bm-L bm-1s"}).I({id:"bm-i",innerHTML:"Tiles Loaded: 0 / ???"}).D().U().D().I({id:"bm-d",innerHTML:"Correct Pixels: ???"}).D().U().D().I({id:"bm-j",innerHTML:"Total Pixels: ???"}).D().U().D().I({id:"bm-7",innerHTML:"Complete: ??? (???)"}).D().U().D().I({id:"bm-8",innerHTML:"??? ???"}).D().D().L({class:"bm-L bm-20"}).H({innerHTML:`Press the ${at.replace("{e.onclick=t=>{t.preventDefault();const e=new FormData(document.querySelector(`#${this.Ct} form`)),s={};for(const[t,i]of e)s[t]=i;console.log(`Primary: ${s.sortPrimary}; Secondary: ${s.sortSecondary}; Unused: ${"on"==s.showUnused}`),i(this,B,q).call(this,s.sortPrimary,s.sortSecondary,"on"==s.showUnused)}}).D().D().D().D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,_).call(this,t),i(this,B,W).call(this),i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),this.ht("#bm-i",`Tiles Loaded: ${n(this.ue)} / ${n(this.be)}`),this.ht("#bm-d",`Correct Pixels: ${n(this.ge)}`),this.ht("#bm-j",`Total Pixels: ${n(this.we)}`),this.ht("#bm-7",`Remaining: ${n((this.we||0)-(this.ge||0))} (${o(((this.we||0)-(this.ge||0))/(this.we||1))})`),this.ht("#bm-8",`Completed at: ${this.xe} `),i(this,B,U).call(this)}$e(){if(document.querySelector(`#${this.Ct}`))return void i(this,B,F).call(this);this.window=this.L({id:this.Ct,class:"bm-W bm-N",style:`width: 360px; height: min(70vh, 32rem); min-width: ${this.oe}px; min-height: ${this.ae}px; max-width: min(${this.re}px, calc(100vw - 16px)); max-height: min(${this.le}px, calc(100vh - 16px));`}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Color Filter"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>{const i=document.querySelector("#bm-2");i&&(i.style.display="expanded"==e.dataset.buttonStatus?"none":""),t.wt(e)},e.ontouchend=()=>{e.click()}}).D().L().I({id:"bm-2",class:"bm-y",style:"font-weight: 700;"}).D().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',"aria-label":'Switch to fullscreen mode for "Color Filter"'},(t,e)=>{e.onclick=()=>{i(this,B,z).call(this,!1),i(this,B,F).call(this),this.St()},e.ontouchend=()=>{e.click()}}).D().lt({class:"bm-s",innerHTML:ot,"aria-label":'Close window "Color Filter"'},(t,e)=>{e.onclick=()=>i(this,B,F).call(this),e.ontouchend=()=>{e.click()}}).D().D().D().L({class:"bm-m"}).L({class:"bm-L bm-h bm-1N"}).W(1,{textContent:"Color Filter"}).D().D().F().D().L({class:"bm-L bm-x bm-h bm-1F",style:"gap: 1.5ch;"}).lt({class:"bm-1u",textContent:"None"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!1)}).D().lt({class:"bm-1u",textContent:"All"},(t,e)=>{e.onclick=()=>i(this,B,Z).call(this,!0)}).D().D().F().D().L({class:"bm-L bm-H bm-1r"}).D().D().L({class:"bm-1O",title:"Resize Color Filter window","aria-label":"Resize Color Filter window",role:"presentation",textContent:"◢",style:"position: absolute; right: 0; bottom: 0; width: 28px; height: 28px; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 4px; padding-bottom: 4px; box-sizing: border-box; z-index: 5; cursor: nwse-resize; pointer-events: auto; touch-action: none; user-select: none; font-size: 8px; line-height: 1; color: rgba(255,255,255,0.95); background: transparent; border: none; box-shadow: none;"}).D().D().N(this.Tt),i(this,B,X).call(this);const t=document.querySelector(`#${this.Ct} .bm-L.bm-H`);i(this,B,_).call(this,t),i(this,B,W).call(this),i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused),i(this,B,U).call(this)}ve(){i(this,B,K).call(this);const t=document.querySelector(`#${this.Zt}`),e={};for(const t of this.palette){const i=this.pe.get(t.id)??0,s=n(i);let a=0,r="0",l=o(1);0!=i&&(a=this.fe.get(t.id)??"???","number"!=typeof a&&this.ue==this.be&&t.id&&(a=0),r="string"==typeof a?a:n(a),l=isNaN(a/i)?"???":o(a/i));const c=parseInt(i)-parseInt(a);e[t.id]={Me:i,Ce:s,Te:a,Se:r,ke:l,De:c}}if(document.querySelector("#bm-2")){const t=this.ge.toString().length>7?this.ge.toString().slice(0,2)+"…"+this.ge.toString().slice(-3):this.ge.toString(),e=this.we.toString().length>7?this.we.toString().slice(0,2)+"…"+this.we.toString().slice(-3):this.we.toString();this.ht("#bm-2",`${t}/${e}`,!0)}if(this.ht("#bm-i",`Tiles Loaded: ${n(this.ue)} / ${n(this.be)}`),this.ht("#bm-d",`Correct Pixels: ${n(this.ge)}`),this.ht("#bm-j",`Total Pixels: ${n(this.we)}`),this.ht("#bm-7",`Remaining: ${n((this.we||0)-(this.ge||0))} (${o(((this.we||0)-(this.ge||0))/(this.we||1))})`),this.ht("#bm-8",`Completed at: ${this.xe} `),!t)return e;const s=Array.from(t.children);for(const t of s){const i=parseInt(t.dataset.id),{Te:s,Se:n,ke:o,Me:a,Ce:r,De:l}=e[i];t.dataset.correct=Number.isNaN(parseInt(s))?"0":s,t.dataset.total=a,t.dataset.percent="%"==o.slice(-1)?o.slice(0,-1):"0",t.dataset.incorrect=l||0;const c=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-9`);c&&(c.textContent=`${n} / ${r}`);const h=document.querySelector(`#${this.Ct} .bm-z[data-id="${i}"] .bm-6`);h&&(h.textContent=`${"number"!=typeof l||isNaN(l)?"???":l} incorrect pixel${1==l?"":"s"}. Completed: ${o}`)}i(this,B,q).call(this,this.sortPrimary,this.sortSecondary,this.showUnused)}};B=new WeakSet,P=function(){var t,e;return this.$?((t=this.$.Nt)[e=this.Qt]??(t[e]={}),this.$.Nt[this.Qt]):null},A=function(){const t=i(this,B,P).call(this);return"windowed"==t?.mode||"fullscreen"!=t?.mode},z=function(t){const e=i(this,B,P).call(this);e&&(e.mode=t?"windowed":"fullscreen"),this.$&&(this.$.Ne(this.Kt,t),this.$.Le())},W=function(){const t=document.querySelector(`#${this.Ct} #bm-c`),e=document.querySelector(`#${this.Ct} #bm-5`),i=document.querySelector(`#${this.Ct} #bm-e`);t instanceof HTMLSelectElement&&(t.value=this.sortPrimary),e instanceof HTMLSelectElement&&(e.value=this.sortSecondary),i instanceof HTMLInputElement&&(i.checked=this.showUnused)},F=function(){const t=document.querySelector(`#${this.Ct}`);t?.classList.contains("bm-N")&&i(this,B,Y).call(this,t),i(this,B,V).call(this),i(this,B,G).call(this),t?.remove()},U=function(){i(this,B,V).call(this),this.se=setInterval(()=>{document.querySelector(`#${this.Ct}`)?this.ve():i(this,B,V).call(this)},this.ne)},V=function(){this.se&&(clearInterval(this.se),this.se=null)},G=function(){this.te&&(this.te.disconnect(),this.te=null),this.ee&&(window.removeEventListener("resize",this.ee),this.ee=null),this.ie&&(clearTimeout(this.ie),this.ie=null)},R=function(t,e,i){const s=Math.max(e,i);return Math.min(Math.max(Math.round(Number(t)||e),e),s)},j=function(t,e,i){const s=Math.max(8,window.innerWidth-t.offsetWidth-8),n=Math.max(8,window.innerHeight-t.offsetHeight-8);return{x:Math.min(Math.max(Math.round(Number(e)||8),8),s),y:Math.min(Math.max(Math.round(Number(i)||8),8),n)}},E=function(t){const e=i(this,B,P).call(this);if(!e||!t)return;const s=Number(e.width),n=Number(e.height),o=Number.isFinite(s),a=Number.isFinite(n);o&&(e.width=i(this,B,R).call(this,s,this.oe,Math.min(this.re,window.innerWidth-16)),t.style.width=`${e.width}px`),a&&(e.height=i(this,B,R).call(this,n,this.ae,Math.min(this.le,window.innerHeight-16)),t.style.height=`${e.height}px`),requestAnimationFrame(()=>{if(!t.isConnected)return;const s=Number(e.x),n=Number(e.y);if(!Number.isFinite(s)||!Number.isFinite(n))return;const o=i(this,B,j).call(this,t,s,n);t.style.left="0px",t.style.top="0px",t.style.right="",t.style.transform=`translate(${o.x}px, ${o.y}px)`,o.x==s&&o.y==n||(e.x=o.x,e.y=o.y,this.$?.Le())})},Y=function(t){const e=i(this,B,P).call(this);if(!e||!t?.isConnected||!t.classList.contains("bm-N"))return;if(t.querySelector('.bm-S button[data-button-status="collapsed"]'))return;const s=t.getBoundingClientRect(),n=i(this,B,R).call(this,s.width,this.oe,Math.min(this.re,window.innerWidth-16)),o=i(this,B,R).call(this,s.height,this.ae,Math.min(this.le,window.innerHeight-16));Math.round(s.width)!=n&&(t.style.width=`${n}px`),Math.round(s.height)!=o&&(t.style.height=`${o}px`);const a=i(this,B,j).call(this,t,s.left,s.top);t.style.left="0px",t.style.top="0px",t.style.right="",t.style.transform=`translate(${a.x}px, ${a.y}px)`,e.x=a.x,e.y=a.y,e.width=n,e.height=o,this.$?.Le()},J=function(t,e=150){this.ie&&clearTimeout(this.ie),this.ie=setTimeout(()=>{this.ie=null,i(this,B,Y).call(this,t)},e)},X=function(){const t=document.querySelector(`#${this.Ct}.bm-W`);t&&(i(this,B,G).call(this),i(this,B,E).call(this,t),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`,{yt:({element:t})=>i(this,B,Y).call(this,t)}),this.vt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-1O`,{minWidth:this.oe,minHeight:this.ae,maxWidth:Math.min(this.re,window.innerWidth-16),maxHeight:Math.min(this.le,window.innerHeight-16),yt:({element:t})=>i(this,B,Y).call(this,t)}),"function"==typeof ResizeObserver&&(this.te=new ResizeObserver(()=>i(this,B,J).call(this,t)),this.te.observe(t)),this.ee=()=>i(this,B,J).call(this,t,0),window.addEventListener("resize",this.ee))},_=function(t){const e=t.closest(`#${this.Ct}`)?.classList.contains("bm-N");console.log(`Is Windowed Mode: ${e}`);const i=new v(this.name,this.version);i.L({id:this.Zt});const s=this.ve();for(const t of this.palette){const n="#"+f(t.rgb).toUpperCase(),o=p(t.rgb);let a=1.05/(o+.05)>(o+.05)/.05?"white":"black";t.id||(a="transparent");const r="white"==a?"bm-f":"bm-g",{Te:l,Se:c,ke:h,Me:m,Ce:d,De:u}=s[t.id],b=!!this.ce.He.get(t.id);if(e){const e=`background-size: auto 100%; background-repeat: repeat-x; background-image: url("data:image/svg+xml;utf8, ");`;i.L({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==h.slice(-1)?h.slice(0,-1):"0","data-incorrect":u||0}).L({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});${t.premium?e:""}`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.me:this.he,style:`color: ${a};`},(e,i)=>{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.me,i.dataset.state="hidden",i.ariaLabel=`Show the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!0)):(i.innerHTML=this.he,i.dataset.state="shown",i.ariaLabel=`Hide the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!1)),i.disabled=!1,i.style.textDecoration=""},t.id||(i.disabled=!0)}).D().O({textContent:`#${t.id.toString().padStart(2,0)}`,style:`color: ${-1==t.id||0==t.id?"white":a}`}).D().W(2,{textContent:t.name,style:`color: ${-1==t.id||0==t.id?"white":a}`}).D().O({class:"bm-9",textContent:`${c} / ${d}`,style:`color: ${-1==t.id||0==t.id?"white":a}; flex: 1 1 auto; text-align: right;`}).D().D().D()}else i.L({class:"bm-L bm-z bm-x","data-id":t.id,"data-name":t.name,"data-premium":+t.premium,"data-correct":Number.isNaN(parseInt(l))?"0":l,"data-total":m,"data-percent":"%"==h.slice(-1)?h.slice(0,-1):"0","data-incorrect":u||0}).L({class:"bm-D",style:"flex-direction: column;"}).L({class:"bm-a",style:`background-color: rgb(${t.rgb?.map(t=>Number(t)||0).join(",")});`}).lt({class:"bm-A "+r,"data-state":b?"hidden":"shown","aria-label":b?`Show the color ${t.name||""} on templates.`:`Hide the color ${t.name||""} on templates.`,innerHTML:b?this.me:this.he,style:`color: ${a};`},(e,i)=>{i.onclick=()=>{i.style.textDecoration="none",i.disabled=!0,"shown"==i.dataset.state?(i.innerHTML=this.me,i.dataset.state="hidden",i.ariaLabel=`Show the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!0)):(i.innerHTML=this.he,i.dataset.state="shown",i.ariaLabel=`Hide the color ${t.name||""} on templates.`,this.ce.Oe(t.id,!1)),i.disabled=!1,i.style.textDecoration=""},t.id||(i.disabled=!0)}).D().D().O({textContent:-2==t.id?"???????":n}).D().D().L({class:"bm-x"}).W(2,{textContent:(t.premium?"★ ":"")+t.name}).D().L({class:"bm-x",style:"gap: 1.5ch;"}).O({textContent:`#${t.id.toString().padStart(2,0)}`}).D().O({class:"bm-9",textContent:`${c} / ${d}`}).D().D().H({class:"bm-6",textContent:`${"number"!=typeof u||isNaN(u)?"???":u} incorrect pixel${1==u?"":"s"}. Completed: ${h}`}).D().D().D()}i.N(t)},q=function(t,e,i){this.sortPrimary=t,this.sortSecondary=e,this.showUnused=i;const s=document.querySelector(`#${this.Zt}`),n=Array.from(s.children);n.sort((s,n)=>{const o=s.getAttribute("data-"+t),a=n.getAttribute("data-"+t),r=parseFloat(o),l=parseFloat(a),c=!isNaN(r),h=!isNaN(l);if(i?s.classList.remove("bm-I"):Number(s.getAttribute("data-total"))||s.classList.add("bm-I"),c&&h)return"ascending"===e?r-l:l-r;{const t=o.toLowerCase(),i=a.toLowerCase();return ti?"ascending"===e?1:-1:0}}),n.forEach(t=>s.appendChild(t))},Z=function(t){const e=document.querySelector(`#${this.Zt}`),i=Array.from(e.children);for(const e of i){if(e.classList?.contains("bm-I"))continue;const i=e.querySelector(".bm-a button");("hidden"!=i.dataset.state||t)&&("shown"==i.dataset.state&&t||i.click())}},K=function(){this.ue=0,this.be=0,this.we=0,this.ge=0,this.fe=new Map,this.pe=new Map;for(const t of this.ce.Ie){const e=t.Pt?.total??0;this.we+=e??0;const i=t.Pt?.colors??new Map;for(const[t,e]of i){const i=Number(e)||0,s=this.pe.get(t)??0;this.pe.set(t,s+i)}const s=t.Pt?.correct??{};this.ue+=Object.keys(s).length,this.be+=Object.keys(t.Ot).length;for(const t of Object.values(s))for(const[e,i]of t){const t=Number(i)||0;this.ge+=t;const s=this.fe.get(e)??0;this.fe.set(e,s+t)}}console.log(`Tiles loaded: ${this.ue} / ${this.be}`),this.ge>=this.we&&this.we&&this.ue==this.be&&(new O).qt(document.querySelector(`#${this.Ct}`)),this.timeRemaining=new Date(30*(this.we-this.ge)*1e3+Date.now()),this.xe=a(this.timeRemaining)},Q=new WeakSet,tt=async function(t,e,i){i.preventDefault();const s=await async function(t){let e="";return t&&(e=t.clipboardData.getData("text/plain")),0!=e.length||(await navigator.clipboard.readText().then(t=>{e=t}).catch(t=>{l("Failed to retrieve clipboard data using navigator! Using fallback methods...")}),0!=e.length||(e=window.clipboardData?.getData("Text"))),e}(i),n=s.split(/[^a-zA-Z0-9]+/).filter(t=>t).map(Number).filter(t=>!isNaN(t));2==n.length&&"bm-O"==e.id?(t.ht("bm-O",n?.[0]||""),t.ht("bm-P",n?.[1]||"")):1==n.length?t.ht(e.id,n?.[0]||""):(t.ht("bm-Q",n?.[0]||""),t.ht("bm-R",n?.[1]||""),t.ht("bm-O",n?.[2]||""),t.ht("bm-P",n?.[3]||""))};var lt=class extends v{constructor(t,i,s,n=void 0){super(t,i),e(this,et),this.window=null,this.Ct="bm-u",this.Tt=document.body,this.Be=JSON.parse(GM_getValue("bmTemplates","{}")),this.scriptVersion=this.Be?.scriptVersion,this.schemaVersion=this.Be?.schemaVersion,this.Pe=void 0,this.Ae=s,this.ce=n}St(){if(document.querySelector(`#${this.Ct}`))return void document.querySelector(`#${this.Ct}`).remove();let t="";document.querySelector("#bm-F")||(t=t.concat("z-index: 9001;").trim()),this.window=this.L({id:this.Ct,class:"bm-W",style:t},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Template Wizard"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().lt({class:"bm-s",textContent:"✖","aria-label":'Close window "Template Wizard"'},(t,e)=>{e.onclick=()=>{document.querySelector(`#${this.Ct}`)?.remove()},e.ontouchend=()=>{e.click()}}).D().D().L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Status"}).D().H({id:"bm-v",textContent:"Loading template storage status..."}).D().D().L({class:"bm-L bm-H"}).W(2,{textContent:"Detected templates:"}).D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`),i(this,et,it).call(this),i(this,et,st).call(this)}};et=new WeakSet,it=function(){const t=this.schemaVersion.split(/[-\.\+]/),e=this.Ae.split(/[-\.\+]/);let s="";t[0]==e[0]?t[1]==e[1]?(s='Template storage health: Healthy! No futher action required. (Reason: Semantic version matches)',this.Pe="Good"):(s='Template storage health: Poor! You can still use your template, but some features may not work. It is recommended that you update Blue Marble\'s template storage. (Reason: MINOR version mismatch)',this.Pe="Poor"):t[0]Bad! It is guaranteed that some features are broken. You might still be able to use the template. It is HIGHLY recommended that you download all templates and update Blue Marble\'s template storage before continuing. (Reason: MAJOR version mismatch)',this.Pe="Bad"):(s='Template storage health: Dead! Blue Marble can not load the template storage. (Reason: MAJOR version unknown)',this.Pe="Dead");const n=` If you want to continue using your current templates, then make sure the template storage (schema) is up-to-date. If you don't want to update the template storage, then downgrade Blue Marble to version ${r(this.scriptVersion)} to continue using your templates. Alternatively, if you don't care about corrupting the templates listed below, you can fix any issues with the template storage by uploading a new template.`,o=function(){const t=[...document.querySelectorAll("body > div > .hidden")].filter(t=>/version:/i.test(t.textContent));if(t[0]){const e=t[0].textContent?.match(/\d+/);return e?new Date(Number(e[0])):void 0}}();let l=o?a(o):"???";this.ht("#bm-v",`${s} Your templates were created during Blue Marble version ${r(this.scriptVersion)} with schema version ${r(this.schemaVersion)} . The current Blue Marble version is ${r(this.version)} and requires schema version ${r(this.Ae)} . Wplace was last updated on ${l} .${"Good"!=this.Pe?n:""}`);const c=new v(this.name,this.version);"Dead"!=this.Pe&&(c.L({class:"bm-L bm-D bm-h",style:"gap: 1.5ch;"}),c.lt({textContent:"Download all templates"},(t,e)=>{e.onclick=()=>{e.disabled=!0,this.ce.ze().then(()=>{e.disabled=!1})}}).D()),"Poor"!=this.Pe&&"Bad"!=this.Pe||c.lt({textContent:`Update template storage to ${this.Ae}`},(t,e)=>{e.onclick=()=>{e.disabled=!0,i(this,et,nt).call(this,!0)}}).D(),c.D().N(document.querySelector("#bm-v").parentNode)},st=function(){const t=this.Be?.templates;if(Object.keys(t).length>0){const e=document.querySelector(`#${this.Ct} .bm-H`),i=new v(this.name,this.version);i.L({id:"bm-B",class:"bm-L"});for(const e in t){const s=e,o=t[e];if(t.hasOwnProperty(e)){const t=s.split(" "),e=Number(t?.[0]),a=d(t?.[1]||"0",this.ce.We),r=o.name||`Template ${e||""}`,l=o?.coords?.split(",").map(Number),c=o.pixels?.total??void 0,h=void 0,m="number"==typeof e?n(e):"???",u="number"==typeof a?n(a):"???",b="number"==typeof c?n(c):"???";i.L({class:"bm-L bm-D"}).L({class:"bm-D",style:"flex-direction: column; gap: 0;"}).L({class:"bm-1",textContent:h||"🖼️"}).D().O({textContent:`#${m}`}).D().D().L({class:"bm-D bm-0"}).W(3,{textContent:r}).D().I({textContent:`Uploaded by user #${u}`}).D().I({textContent:`Coordinates: ${l.join(", ")}`}).D().I({textContent:`Total Pixels: ${b}`}).D().D().D()}}i.D().N(e)}},nt=async function(t){if(t){const t=document.querySelector(`#${this.Ct} .bm-m`);t.innerHTML="",new v(this.name,this.version).L({class:"bm-L"}).L({class:"bm-L bm-h"}).W(1,{textContent:"Template Wizard"}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Status"}).D().H({textContent:"Updating template storage. Please wait..."}).D().D().D().N(t)}GM_deleteValue("bmCoords");const e=this.Be?.templates;if(Object.keys(e).length>0)for(const[t,i]of Object.entries(e))if(e.hasOwnProperty(t)){const t=new H({displayName:i.name,Ot:i.tiles});t.Yt();const e=await this.ce.Fe(t);await this.ce.Ue(e,t.displayName,t.coords)}t&&(console.log("Restarting Template Wizard..."),document.querySelector(`#${this.Ct}`).remove(),new lt(this.name,this.version,this.Ae,this.ce).St())};var ct,ht,mt,dt,ut,bt,pt,ft,gt=lt;ct=new WeakSet,ht=function(){const t=this.$?.Nt?.filter,e=Array.isArray(t)?t:[];this.He.clear();for(const t of e){const e=Number(t);Number.isFinite(e)&&this.He.set(e,!0)}},mt=function(){this.$&&(this.$.Nt.filter=Array.from(this.He.keys()).map(t=>Number(t)).filter(t=>Number.isFinite(t)).sort((t,e)=>t-e),this.$.Le())},dt=async function(){GM.setValue("bmTemplates",JSON.stringify(this.Ve))},ut=async function(t){console.log("Parsing BlueMarble...");const e=t.templates;console.log(`BlueMarble length: ${Object.keys(e).length}`);const i=t?.schemaVersion,s=i.split(/[-\.\+]/),n=this.schemaVersion.split(/[-\.\+]/),o=t?.scriptVersion;console.log(`BlueMarble Template Schema: ${i}; Script Version: ${o}`),s[0]==n[0]?(s[1]!=n[1]&&new gt(this.name,this.version,this.schemaVersion,this).St(),this.Ie=await async function({Bt:t,Ge:i,Ie:s}){if(Object.keys(e).length>0)for(const n in e){const o=n,a=e[n];if(console.log(`Template Key: ${o}`),e.hasOwnProperty(n)){const e=o.split(" "),n=Number(e?.[0]),r=e?.[1]||"0",l=a.name||`Template ${n||""}`,c={total:a.pixels?.total,colors:new Map(Object.entries(a.pixels?.colors||{}).map(([t,e])=>[Number(t),e]))},h=a.tiles,m={},d={},u=t*i;for(const t in h)if(console.log(t),h.hasOwnProperty(t)){const e=b(h[t]),i=new Blob([e],{type:"image/png"}),s=await createImageBitmap(i);m[t]=s;const n=new OffscreenCanvas(u,u).getContext("2d");n.drawImage(s,0,0);const o=n.getImageData(0,0,s.width,s.height);d[t]=new Uint32Array(o.data.buffer)}const p=new H({displayName:l,Lt:n||this.Ie?.length||0,Ht:r||""});p.Pt=c,p.Ot=m,p.It=d,s.push(p),console.log(this.Ie),console.log("^^^ This ^^^")}}return s}({Bt:this.Bt,Ge:this.Ge,Ie:this.Ie})):s[0]>>24&255,y=g>>>24&255,$=b.get(w)??-2,v=b.get(g)??-2;if(this.He.get($)&&(e[i*c+h]=g),-1==$){const t=536870912;this.He.get($)?e[i*c+h]=0:(u/o&1)==(f/o&1)?(e[i*c+h]=t,e[(i-1)*c+(h-1)]=t,e[(i-1)*c+(h+1)]=t,e[(i+1)*c+(h-1)]=t,e[(i+1)*c+(h+1)]=t):(e[i*c+h]=0,e[(i-1)*c+h]=t,e[(i+1)*c+h]=t,e[i*c+(h-1)]=t,e[i*c+(h+1)]=t)}if(!n&&x>m&&v!=$&&(d||y>m)){const t=e[i*c+h];for(const n of s){const[s,o,a]=n,r=0!=s?1!=s?t:4278190335:0;e[(i+a)*c+(h+o)]=r}}if(-1==$&&g<=m){const t=p.get($);p.set($,t?t+1:1);continue}if(x<=m||y<=m)continue;if(v!=$)continue;const M=p.get($);p.set($,M?M+1:1)}return console.log("List of template pixels that match the tile:"),console.log(p),{qe:p,Ze:e}},pt=new WeakSet,ft=function(t){const e=JSON.parse(GM_getValue("bmUserSettings","{}"));e.telemetry=t,GM.setValue("bmUserSettings",JSON.stringify(e))};var wt=GM_info.script.name.toString(),xt=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-11",wt),e.setAttribute("bm-X","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement?.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=t?.getAttribute("bm-11")||"Blue Marble",i=t?.getAttribute("bm-X")||"",s=new Map;window.addEventListener("message",t=>{const{source:n,endpoint:o,blobID:a,blobData:r,blink:l}=t.data,c=Date.now()-l;if(console.groupCollapsed(`%c${e}%c: ${s.size} Recieved IMAGE message about blob "${a}"`,i,""),console.log(`Blob fetch took %c${String(Math.floor(c/6e4)).padStart(2,"0")}:${String(Math.floor(c/1e3)%60).padStart(2,"0")}.${String(c%1e3).padStart(3,"0")}%c MM:SS.mmm`,i,""),console.log(s),console.groupEnd(),"blue-marble"==n&&a&&r&&!o){const t=s.get(a);"function"==typeof t?t(r):h(`%c${e}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`,i,"",a),s.delete(a)}});const n=window.fetch;window.fetch=async function(...t){const o=await n.apply(this,t),a=o.clone(),r=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",l=a.headers.get("content-type")||"";if(l.includes("application/json"))console.log(`%c${e}%c: Sending JSON message about endpoint "${r}"`,i,""),a.json().then(t=>{const s=r?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();if("me"==s)try{sessionStorage.setItem("bm-2m",JSON.stringify(t))}catch(t){console.warn(`%c${e}%c: Failed to cache "/me" payload`,i,"",t)}window.postMessage({source:"blue-marble",endpoint:r,jsonData:t},"*")}).catch(t=>{console.error(`%c${e}%c: Failed to parse JSON: `,i,"",t)});else if(l.includes("image/")&&!r.includes("openfreemap")&&!r.includes("maps")){const t=Date.now(),n=await a.blob();return console.log(`%c${e}%c: ${s.size} Sending IMAGE message about endpoint "${r}"`,i,""),new Promise(o=>{const l=crypto.randomUUID();s.set(l,t=>{o(new Response(t,{headers:a.headers,status:a.status,statusText:a.statusText})),console.log(`%c${e}%c: ${s.size} Processed blob "${l}"`,i,"")}),window.postMessage({source:"blue-marble",endpoint:r,blobID:l,blobData:n,blink:t})}).catch(n=>{const o=Date.now();console.error(`%c${e}%c: Failed to Promise blob!`,i,""),console.groupCollapsed(`%c${e}%c: Details of failed blob Promise:`,i,""),console.log(`Endpoint: ${r}\nThere are ${s.size} blobs processing...\nBlink: ${t.toLocaleString()}\nTime Since Blink: ${String(Math.floor(o/6e4)).padStart(2,"0")}:${String(Math.floor(o/1e3)%60).padStart(2,"0")}.${String(o%1e3).padStart(3,"0")} MM:SS.mmm`),console.error("Exception stack:",n),console.groupEnd()})}return o}});var yt=GM_getResourceText("CSS-BM-File");function $t(t){const e=document.createElement("link");e.href=t,e.rel="preload",e.as="style",e.onload=function(){this.onload=null,this.rel="stylesheet"},document.head?.appendChild(e)}GM_addStyle(yt);var vt="robotoMonoInjectionPoint";$t("https://fonts.googleapis.com/css2?family=Michroma&family=Rajdhani:wght@400;500;600;700&display=swap"),vt.indexOf("@font-face")+1?(console.log("Loading Roboto Mono as a file..."),GM_addStyle(vt)):$t("https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap");var Mt=JSON.parse(GM_getValue("bmUserSettings","{}")),Ct=(new class{constructor(){this.Ke=null,this.Qe=null,this.ti="#bm-p"}ei(t){return this.Qe=t,this.Ke=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.ti)}),this}ii(){return this.Ke}observe(t,e=!1,i=!1){t.observe(this.Qe,{childList:e,subtree:i})}},new class extends v{constructor(t,i){super(t,i),e(this,Q),this.window=null,this.Ct="bm-F",this.Tt=document.body}St(){document.querySelector(`#${this.Ct}`)?this.$t("Main window already exists!"):(this.window=this.L({id:this.Ct,class:"bm-W bm-N",style:"top: 10px; left: unset; right: 75px;"},(t,e)=>{}).ft().lt({class:"bm-s",innerHTML:$,"aria-label":'Minimize window "Blue Marble"',"data-button-status":"expanded"},(t,e)=>{e.onclick=()=>t.wt(e),e.ontouchend=()=>{e.click()}}).D().L().D().L({class:"bm-D"}).lt({class:"bm-s",innerHTML:' ',title:"Settings","aria-label":"Open settings"},(t,e)=>{e.onclick=()=>{t.$.St()}}).D().D().D().L({class:"bm-m"}).F().D().L({class:"bm-L bm-2d"}).A({class:"bm-T",src:"https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png"},(t,e)=>{const i=new Date;204==Math.floor((i.getTime()-new Date(i.getFullYear(),0,1))/864e5)+1&&(e.parentNode.style.position="relative",e.parentNode.innerHTML=e.parentNode.innerHTML+' ',e.onload=()=>{(new O).qt(document.querySelector(`#${this.Ct}`))})}).D().W(1,{textContent:this.name}).D().D().F().D().L({class:"bm-L bm-28"}).L({class:"bm-1I bm-1h"}).I({class:"bm-1B",textContent:"Droplets"}).D().I({id:"bm-w",class:"bm-1C",textContent:"0"}).D().D().L({class:"bm-1I bm-1h"}).I({class:"bm-1B",textContent:"Next Level"}).D().I({id:"bm-q",class:"bm-1C",textContent:"0 px"}).D().D().L({class:"bm-1I bm-1i"}).I({class:"bm-1B",textContent:"Charges"}).D().gt(Date.now(),1e3,{class:"bm-1C",style:"font-weight: 700;"},(t,e)=>{t.p.si=e.id}).D().D().D().F().D().L({class:"bm-L bm-29"}).L({class:"bm-L bm-22"}).lt({class:"bm-s bm-J",style:"margin-top: 0;",innerHTML:' '},(t,e)=>{e.onclick=()=>{const e=t.p?.ni;e?.[0]?(t.ht("bm-Q",e?.[0]||""),t.ht("bm-R",e?.[1]||""),t.ht("bm-O",e?.[2]||""),t.ht("bm-P",e?.[3]||"")):t.$t("Coordinates are malformed! Did you try clicking on the canvas first?")}}).D().dt({type:"number",id:"bm-Q",class:"bm-C",placeholder:"Tl X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-R",class:"bm-C",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-O",class:"bm-C",placeholder:"Px X",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().dt({type:"number",id:"bm-P",class:"bm-C",placeholder:"Px Y",min:0,max:2047,step:1,required:!0},(t,e)=>{e.addEventListener("paste",s=>i(this,Q,tt).call(this,t,e,s))}).D().D().L({class:"bm-L bm-23"}).ut({class:"bm-K",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).D().D().L({class:"bm-L bm-x bm-1Y"}).lt({class:"bm-1u",textContent:"Disable","data-button-status":"shown"},(t,e)=>{e.onclick=()=>{e.disabled=!0,"shown"==e.dataset.buttonStatus?(t.p?.ce?.oi(!1),e.dataset.buttonStatus="hidden",e.textContent="Enable",t.Mt("Disabled templates!")):(t.p?.ce?.oi(!0),e.dataset.buttonStatus="shown",e.textContent="Disable",t.Mt("Enabled templates!")),e.disabled=!1}}).D().lt({class:"bm-1G",textContent:"Create"},(t,e)=>{e.onclick=()=>{const e=document.querySelector(`#${this.Ct} .bm-K`),i=document.querySelector("#bm-Q");if(!i.checkValidity())return i.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-R");if(!s.checkValidity())return s.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const n=document.querySelector("#bm-O");if(!n.checkValidity())return n.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-P");if(!o.checkValidity())return o.reportValidity(),void t.$t("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(t?.p?.ce.Ue(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(i.value),Number(s.value),Number(n.value),Number(o.value)]),t.Mt("Drew to canvas!")):t.$t("No file selected!")}}).D().lt({class:"bm-1u",textContent:"Filter"},(t,e)=>{e.onclick=()=>this.ai()}).D().D().L({class:"bm-L bm-24"}).bt({id:this.v,placeholder:`Status: Sleeping...\nVersion: ${this.version}`,readOnly:!0}).D().D().D().D().D().N(this.Tt),this.xt(`#${this.Ct}.bm-W`,`#${this.Ct} .bm-S`))}ai(){new rt(this).ye()}}(wt,xt)),Tt=new class{constructor(t,i){e(this,ct),this.name=t,this.version=i,this.Re=null,this.$=null,this.schemaVersion="2.0.0",this.ri=null,this.We="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.Bt=1e3,this.Ge=3,this._e=3,this.de=function(t){const e=y;e.unshift({id:-1,premium:!1,name:"Erased",rgb:[222,250,206]}),e.unshift({id:-2,premium:!1,name:"Other",rgb:[0,0,0]});const i=new Map;for(const s of e){if(0==s.id||-2==s.id)continue;const e=s.rgb[0],n=s.rgb[1],o=s.rgb[2];for(let a=-t;a<=t;a++)for(let r=-t;r<=t;r++)for(let l=-t;l<=t;l++){const t=e+a,c=n+r,h=o+l;if(t<0||t>255||c<0||c>255||h<0||h>255)continue;const m=(255<<24|h<<16|c<<8|t)>>>0;i.has(m)||i.set(m,s.id)}}return{palette:e,Jt:i}}(this._e),this.Ee=null,this.li="",this.Ie=[],this.Ve=null,this.ci=!0,this.hi=null,this.He=new Map}mi(t){this.Re=t}k(t){this.$=t,i(this,ct,ht).call(this)}Oe(t,e){const s=Number(t);Number.isFinite(s)&&(e?this.He.set(s,!0):this.He.delete(s),i(this,ct,mt).call(this))}async di(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.schemaVersion,templates:{}}}async Ue(t,e,s){this.Ve||(this.Ve=await this.di(),console.log("Creating JSON...")),this.Re.Mt(`Creating template at ${s.join(", ")}...`);const n=new H({displayName:e,Lt:0,Ht:m(this.ri||0,this.We),file:t,coords:s}),o=!this.$?.Nt?.flags?.includes("hl-noSkip"),a=this.$?.Nt?.flags?.includes("hl-agSkip");console.log(`Should Skip: ${o}; Should Agg Skip: ${a}`);const{jt:r,Et:l}=await n.Wt(this.Bt,this.de,o,a);n.Ot=r;const c={total:n.Pt.total,colors:Object.fromEntries(n.Pt.colors)};this.Ve.templates[`${n.Lt} ${n.Ht}`]={name:n.displayName,coords:s.join(", "),enabled:!0,pixels:c,tiles:l},this.Ie=[],this.Ie.push(n),this.Re.Mt(`Template created at ${s.join(", ")}!`),console.log(Object.keys(this.Ve.templates).length),console.log(this.Ve),console.log(this.Ie),console.log(JSON.stringify(this.Ve)),await i(this,ct,dt).call(this)}ui(){}async bi(){this.Ve||(this.Ve=await this.di(),console.log("Creating JSON..."))}async pi(){l("Downloading all templates..."),console.log(this.Ie);for(const t of this.Ie)await this.fi(t),await s(500)}async ze(){const t=JSON.parse(GM_getValue("bmTemplates","{}"))?.templates;if(console.log(t),Object.keys(t).length>0)for(const[e,i]of Object.entries(t))t.hasOwnProperty(e)&&(await this.fi(new H({displayName:i.name,Lt:e.split(" ")?.[0],Ht:e.split(" ")?.[1],Ot:i.tiles})),await s(500))}async fi(t){t.Yt();const e=`${t.coords.join("-")}_${t.displayName.replaceAll(" ","-")}`,i=await this.Fe(t);await GM.download({url:URL.createObjectURL(i),name:e+".png",gi:"uniquify",onload:()=>{l(`Download of template '${e}' complete!`)},onerror:(t,i)=>{c(`Download of template '${e}' failed because ${t}! Details: ${i}`)},ontimeout:()=>{h(`Download of template '${e}' has timed out!`)}})}async Fe(t){console.log(t);const e=t.Ot,i=Object.keys(e).sort(),s=await Promise.all(i.map(t=>{return i=e[t],new Promise((t,e)=>{const s=new Image;s.onload=()=>t(s),s.onerror=e,s.src="data:image/png;base64,"+i});var i}));let n=1/0,o=1/0,a=0,r=0;i.forEach((t,e)=>{const[i,l,c,h]=t.split(",").map(Number),m=s[e],d=i*this.Bt+c,u=l*this.Bt+h;n=Math.min(n,d),o=Math.min(o,u),a=Math.max(a,d+m.width/this.Ge),r=Math.max(r,u+m.height/this.Ge)}),console.log(`Absolute coordinates: (${n}, ${o}) and (${a}, ${r})`);const l=a-n,c=r-o,h=l*this.Ge,m=c*this.Ge;console.log(`Template Width: ${l}\nTemplate Height: ${c}\nCanvas Width: ${h}\nCanvas Height: ${m}`);const d=new OffscreenCanvas(h,m),u=d.getContext("2d");i.forEach((t,e)=>{const[i,a,r,l]=t.split(",").map(Number),c=s[e],h=i*this.Bt+r,m=a*this.Bt+l;console.log(`Drawing tile (${i}, ${a}, ${r}, ${l}) (${h}, ${m}) at (${h-n}, ${m-o}) on the canvas...`),u.drawImage(c,(h-n)*this.Ge,(m-o)*this.Ge,c.width,c.height)}),u.globalCompositeOperation="destination-over",u.drawImage(d,0,-1),u.drawImage(d,0,1),u.drawImage(d,-1,0),u.drawImage(d,1,0);const b=new OffscreenCanvas(l,c),p=b.getContext("2d");return p.imageSmoothingEnabled=!1,p.drawImage(d,0,0,l*this.Ge,c*this.Ge,0,0,l,c),b.convertToBlob({type:"image/png"})}async wi(t,e){if(!this.ci)return t;const s=this.Bt*this.Ge;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0"),console.log(`Searching for templates in tile: "${e}"`);const o=this.Ie;console.log(o),o.sort((t,e)=>t.Lt-e.Lt),console.log(o);const a=o.map(t=>{const i=Object.keys(t.Ot).filter(t=>t.startsWith(e));if(0===i.length)return null;const s=i.map(e=>{const i=e.split(",");return{xi:t,Ut:t.Ot[e],It:t.It?.[e],yi:[i[0],i[1]],$i:[i[2],i[3]]}});return s?.[0]}).filter(Boolean);console.log(a);const r=a?.length||0;if(console.log(`templateCount = ${r}`),!(r>0))return this.Re.Mt(`Sleeping\nVersion: ${this.version}`),t;{const t=n(o.filter(t=>Object.keys(t.Ot).filter(t=>t.startsWith(e)).length>0).reduce((t,e)=>t+(e.Pt.total||0),0));this.Re.Mt(`Displaying ${r} template${1==r?"":"s"}.\nTotal pixels: ${t}`)}const l=await createImageBitmap(t),c=new OffscreenCanvas(s,s),h=c.getContext("2d");h.imageSmoothingEnabled=!1,h.beginPath(),h.rect(0,0,s,s),h.clip(),h.clearRect(0,0,s,s),h.drawImage(l,0,0,s,s);const m=h.getImageData(0,0,s,s),d=new Uint32Array(m.data.buffer),u=this.$?.Nt?.highlight||[[2,0,0]],b=u?.[0],p=1==u?.length&&2==b?.[0]&&0==b?.[1]&&0==b?.[2];for(const t of a){console.log("Template:"),console.log(t);const s=!!t.xi.Pt?.colors?.get(-1);let n=t.It.slice();const o=Number(t.$i[0])*this.Ge,a=Number(t.$i[1])*this.Ge;if(0!=this.He.size||s||h.drawImage(t.Ut,o,a),!n){const e=h.getImageData(o,a,t.Ut.width,t.Ut.height);n=new Uint32Array(e.data.buffer)}const r=Date.now(),{qe:l,Ze:c}=i(this,ct,bt).call(this,{je:d,Ee:n,Ye:[o,a,t.Ut.width,t.Ut.height],Je:u,Xe:p});let m=0;const b=0;for(const[t,e]of l)t!=b&&(m+=e);0==this.He.size&&!s&&p||(console.log("Colors to filter: ",this.He),h.drawImage(await createImageBitmap(new ImageData(new Uint8ClampedArray(c.buffer),t.Ut.width,t.Ut.height)),o,a)),console.log(`Finished calculating correct pixels & filtering colors for the tile ${e} in ${(Date.now()-r)/1e3} seconds!\nThere are ${m} correct pixels.`),void 0===t.xi.Pt.correct&&(t.xi.Pt.correct={}),t.xi.Pt.correct[e]=l}return await c.convertToBlob({type:"image/png"})}async Mi(t){console.log("Importing JSON..."),console.log(t),"BlueMarble"==t?.whoami&&await i(this,ct,ut).call(this,t)}oi(t){this.ci=t}}(wt,xt),St=new class{constructor(t){this.ce=t,this.Ci=!1,this.si="",this.ni=[],this.Ti=[]}Si(t){window.addEventListener("message",async e=>{const i=e.data,s=i.jsonData;if(!i||"blue-marble"!==i.source)return;if(!i.endpoint)return;const n=i.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(console.log('%cBlue Marble%c: Recieved message about "%s"',"color: cornflowerblue;","",n),n){case"me":this.ki(t,s);break;case"pixel":const e=i.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),n=new URLSearchParams(i.endpoint.split("?")[1]),r=[n.get("x"),n.get("y")];if(this.ni.length&&(!e.length||!r.length))return void t.$t("Coordinates are malformed!\nDid you try clicking the canvas first?");this.ni=[...e,...r];const l=(o=e,a=r,[parseInt(o[0])%4*1e3+parseInt(a[0]),parseInt(o[1])%4*1e3+parseInt(a[1])]),c=document.querySelectorAll("span");for(const t of c){const i=t.textContent.trim();if(i.includes(l[0])&&i.includes(l[1])){let i=document.querySelector("#bm-p");e[0],e[1],r[0],r[1];const s=["Tl X:","Tl Y:","Px X:","Px Y:"],n=["bm-Y","bm-Z","bm-U","bm-V"],o=[...e,...r];if(i)for(const[t,e]of n.entries())document.getElementById(e).textContent=`${s[t]??"??:"} ${o[t]}`;else{i=document.createElement("span"),i.id="bm-p",i.style="display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;";for(const[t,e]of o.entries()){const a=document.createElement("span");a.id=n[o.indexOf(e)??""],a.textContent=`${s[t]??"??:"} ${e}`,i.appendChild(a)}t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",i)}}}break;case"tile":case"tiles":let h=i.endpoint.split("/");h=[parseInt(h[h.length-2]),parseInt(h[h.length-1].replace(".png",""))];const m=i.blobID,d=i.blobData,u=Date.now(),b=await this.ce.wi(d,h);console.log(`Finished loading the tile in ${(Date.now()-u)/1e3} seconds!`),window.postMessage({source:"blue-marble",blobID:m,blobData:b,blink:i.blink});break;case"robots":this.Ci="false"==s.userscript?.toString().toLowerCase();break}var o,a})}ki(t,e){if(e.status&&"2"!=e.status?.toString()[0])return void t.$t("You are not logged in or Wplace is offline!\nCould not fetch userdata.");const i=Math.ceil(Math.pow(Math.floor(e.level)*Math.pow(30,.65),1/.65)-e.pixelsPainted);if(console.log(e.id),(e.id||0===e.id)&&console.log(m(e.id,"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")),this.ce.ri=e.id,0!=this.si.length){const t=document.querySelector("#"+this.si);if(t){const i=e.charges;t.dataset.endDate=Date.now()+(i.max-i.count)*i.cooldownMs}}t.ht("bm-w",`${n(e.droplets)} `),t.ht("bm-q",`${n(i)} px`)}async Di(t){try{const e=await fetch(`${window.location.origin}/api/me`,{credentials:"include"});if(!e.ok)return void t.$t(`Could not fetch userdata.\nHTTP ${e.status}`);const i=await e.json();this.ki(t,i)}catch(t){c("Failed to fetch current user data:",t)}}Ni(t){try{const e=sessionStorage.getItem("bm-2m");if(!e)return!1;const i=JSON.parse(e);return this.ki(t,i),!0}catch(t){return c("Failed to apply cached user data:",t),!1}}async Li(t){console.log("Sending heartbeat to telemetry server...");let e=GM_getValue("bmUserSettings","{}");if(e=JSON.parse(e),!e||!e.telemetry||!e.uuid)return void console.log("Telemetry is disabled, not sending heartbeat.");const i=navigator.userAgent;let s=await this.Hi(i),n=this.Oi(i);GM_xmlhttpRequest({method:"POST",url:"https://telemetry.thebluecorner.net/heartbeat",headers:{"Content-Type":"application/json"},data:JSON.stringify({uuid:e.uuid,version:t,browser:s,os:n}),onload:t=>{200!==t.status&&c("Failed to send heartbeat:",t.statusText)},onerror:t=>{c("Error sending heartbeat:",t)}})}async Hi(t=navigator.userAgent){return(t=t||"").includes("OPR/")||t.includes("Opera")?"Opera":t.includes("Edg/")?"Edge":t.includes("Vivaldi")?"Vivaldi":t.includes("YaBrowser")?"Yandex":t.includes("Kiwi")?"Kiwi":t.includes("Brave")?"Brave":t.includes("Firefox/")?"Firefox":t.includes("Chrome/")?"Chrome":t.includes("Safari/")?"Safari":navigator.brave&&"function"==typeof navigator.brave.isBrave&&await navigator.brave.isBrave()?"Brave":"Unknown"}Oi(t=navigator.userAgent){return/Windows NT 11/i.test(t=t||"")?"Windows 11":/Windows NT 10/i.test(t)?"Windows 10":/Windows NT 6\.3/i.test(t)?"Windows 8.1":/Windows NT 6\.2/i.test(t)?"Windows 8":/Windows NT 6\.1/i.test(t)?"Windows 7":/Windows NT 6\.0/i.test(t)?"Windows Vista":/Windows NT 5\.1|Windows XP/i.test(t)?"Windows XP":/Mac OS X 10[_\.]15/i.test(t)?"macOS Catalina":/Mac OS X 10[_\.]14/i.test(t)?"macOS Mojave":/Mac OS X 10[_\.]13/i.test(t)?"macOS High Sierra":/Mac OS X 10[_\.]12/i.test(t)?"macOS Sierra":/Mac OS X 10[_\.]11/i.test(t)?"OS X El Capitan":/Mac OS X 10[_\.]10/i.test(t)?"OS X Yosemite":/Mac OS X 10[_\.]/i.test(t)?"macOS":/Android/i.test(t)?"Android":/iPhone|iPad|iPod/i.test(t)?"iOS":/Linux/i.test(t)?"Linux":"Unknown"}}(Tt),kt=new class extends L{constructor(t,i,s){var n;super(t,i),e(this,T),this.Nt=s,(n=this.Nt).flags??(n.flags=[]),this.Ii=structuredClone(this.Nt),this.Bi="bmUserSettings",this.Pi=5e3,this.Ai=0,setInterval(this.zi.bind(this),this.Pi)}async zi(){await this.Wi()}async Wi(t=!1){const e=JSON.stringify(this.Nt);e!=JSON.stringify(this.Ii)&&(t||Date.now()-this.Ai>this.Pi)&&(await GM.setValue(this.Bi,e),this.Ii=structuredClone(this.Nt),this.Ai=Date.now(),console.log(e))}async Le(){await this.Wi(!0)}Ne(t,e=void 0){const i=this.Nt?.flags?.indexOf(t)??-1;-1!=i&&!0!==e?this.Nt?.flags?.splice(i,1):-1==i&&!1!==e&&this.Nt?.flags?.push(t)}kt(){const t=' ',e=' ',s=this.Nt?.highlight??[[1,0,1],[2,0,0],[1,-1,0],[1,1,0],[1,0,-1]];this.window=this.L({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().F().D().L({class:"bm-L",style:"margin-left: 1.5ch;"}).j({textContent:"Highlight transparent pixels"},(t,e,i)=>{i.checked=!this.Nt?.flags?.includes("hl-noTrans"),i.onchange=t=>this.Ne("hl-noTrans",!t.target.checked)}).D().H({id:"bm-4",textContent:"Choose a preset:",style:"font-weight: 700;"}).D().L({class:"bm-D",role:"group","aria-labelledby":"bm-4"}).L({class:"bm-3"}).I({textContent:"None"}).D().lt({innerHTML:t,"aria-label":'Preset "None"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"None")}).D().D().L({class:"bm-3"}).I({textContent:"Cross"}).D().lt({innerHTML:e,"aria-label":'Preset "Cross Shape"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"Cross")}).D().D().L({class:"bm-3"}).I({textContent:"X"}).D().lt({innerHTML:e.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"','d="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'),"aria-label":'Preset "X Shape"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"X")}).D().D().L({class:"bm-3"}).I({textContent:"Full"}).D().lt({innerHTML:t.replace("#fff","#2f4f4f"),"aria-label":'Preset "Full Template"'},(t,e)=>{e.onclick=()=>i(this,T,k).call(this,"Full")}).D().D().D().H({id:"bm-b",textContent:"Create a custom pattern:",style:"font-weight: 700;"}).D().L({class:"bm-n",role:"group","aria-labelledby":"bm-b"});for(let t=-1;t<=1;t++)for(let e=-1;e<=1;e++){const n=s[s.findIndex(([,i,s])=>i==e&&s==t)]?.[0]??0;let o="Disabled";1==n?o="Incorrect":2==n&&(o="Template"),this.window=this.lt({"data-status":o,"aria-label":`Sub-pixel ${o.toLowerCase()}`},(s,n)=>{n.onclick=()=>i(this,T,S).call(this,n,[e,t])}).D()}this.window=this.D().D().D()}Dt(){this.window=this.L({class:"bm-L"}).W(2,{textContent:"Pixel Highlight"}).D().F().D().L({class:"bm-L",style:"margin-left: 1.5ch;"}).j({textContent:"Template creation should skip transparent tiles"},(t,e,i)=>{i.checked=!this.Nt?.flags?.includes("hl-noSkip"),i.onchange=t=>this.Ne("hl-noSkip",!t.target.checked)}).D().j({innerHTML:"Experimental: Template creation should aggressively skip transparent tiles"},(t,e,i)=>{i.checked=this.Nt?.flags?.includes("hl-agSkip"),i.onchange=t=>this.Ne("hl-agSkip",t.target.checked)}).D().D().D()}}(wt,xt,Mt);Ct.k(kt),Ct.S(St),Tt.mi(Ct),Tt.k(kt);var Dt=JSON.parse(GM_getValue("bmTemplates","{}"));if(console.log(Dt),console.log(Mt),console.log(Object.keys(Mt).length),0==Object.keys(Mt).length){const t=crypto.randomUUID();console.log(t),GM.setValue("bmUserSettings",JSON.stringify({uuid:t}))}setInterval(()=>St.Li(xt),18e5);var Nt=Mt?.telemetry;if(console.log(`Telemetry is ${!(null==Nt)}`),null==Nt||Nt>1){const t=new class extends v{constructor(t,i,s,n){super(t,i),e(this,pt),this.window=null,this.Ct="bm-k",this.Tt=document.body,this.Fi=s,this.uuid=n}async St(){if(document.querySelector(`#${this.Ct}`))return void this.$t("Telemetry window already exists!");const t=await this.p.Hi(navigator.userAgent),e=this.p.Oi(navigator.userAgent);this.window=this.L({id:this.Ct,class:"bm-W",style:"height: 80vh; z-index: 9998;"}).L({class:"bm-m"}).L({class:"bm-L bm-h"}).W(1,{textContent:`${this.name} Telemetry`}).D().D().F().D().L({class:"bm-L bm-D",style:"gap: 1.5ch; flex-wrap: wrap;"}).lt({textContent:"Enable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,pt,ft).call(this,this.Fi);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"Disable Telemetry"},(t,e)=>{e.onclick=()=>{i(this,pt,ft).call(this,0);const t=document.getElementById(this.Ct);t?.remove()}}).D().lt({textContent:"More Information"},(t,e)=>{e.onclick=()=>{window.open("https://github.com/SwingTheVine/Wplace-TelemetryServer#telemetry-data","_blank","noopener noreferrer")}}).D().D().L({class:"bm-L bm-H"}).L({class:"bm-L"}).W(2,{textContent:"Legal"}).D().H({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}!`}).D().D().F().D().L({class:"bm-L"}).W(2,{textContent:"Non-Legal Summary"}).D().H({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. This is the data stored on our servers:'}).D()._().Z({innerHTML:`A unique identifier (UUIDv4) generated by Blue Marble. This enables our telemetry to function without tracking your actual user ID. Your UUID is: ${r(this.uuid)} `}).D().Z({innerHTML:`The version of Blue Marble you are using. Your version is: ${r(this.version)} `}).D().Z({innerHTML:`Your browser type, which is used to determine Blue Marble outages and browser popularity. Your browser type is: ${r(t)} `}).D().Z({innerHTML:`Your OS type, which is used to determine Blue Marble outages and OS popularity. Your OS type is: ${r(e)} `}).D().Z({innerHTML:"The date and time that Blue Marble sent the telemetry information."}).D().D().H({innerHTML:'All of the data mentioned above is aggregated every hour . 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.'}).D().D().D().D().D().N(this.Tt)}}(wt,xt,1,Mt?.uuid);t.S(St),t.St()}!async function(){await Tt.Mi(Dt),St.Si(Ct),Ct.St(),Ct.ai(),St.Ni(Ct),St.Di(Ct),new MutationObserver((t,e)=>{const i=document.querySelector("#color-1");if(!i)return;let s=document.querySelector("#bm-G");if(!s){s=document.createElement("button"),s.id="bm-G",s.textContent="Move ↑",s.className="btn btn-soft",s.onclick=function(){const t=this.parentNode.parentNode.parentNode.parentNode,e="Move ↑"==this.textContent;t.parentNode.className=t.parentNode.className.replace(e?"bottom":"top",e?"top":"bottom"),t.style.borderTopLeftRadius=e?"0px":"var(--radius-box)",t.style.borderTopRightRadius=e?"0px":"var(--radius-box)",t.style.borderBottomLeftRadius=e?"var(--radius-box)":"0px",t.style.borderBottomRightRadius=e?"var(--radius-box)":"0px",this.textContent=e?"Move ↓":"Move ↑"};const t=i.parentNode.parentNode.parentNode.parentNode.querySelector("h2");t.parentNode?.appendChild(s)}}).observe(document.body,{childList:!0,subtree:!0}),l(`%c${wt}%c (${xt}) userscript has loaded!`,"color: cornflowerblue;","")}()})();
+
+// Build Hash: 835bfa613e8c
diff --git a/docs/README.md b/docs/README.md
index 6fd577b..c22e4b0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,292 +1,45 @@
-
+# Blue Marble Enhanced
-Blue Marble
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-Quick Guide
-
- Press the arrows to reveal the option you want.
-
-
- I want to download Blue Marble. (Click to Expand)
-
- Click here to view the installation instructions.
-
-
-
- I want to ask questions about Blue Marble. (Click to Expand)
-
- Click here for the Discord server invite to the Blue Marble support server.
-
- Click here for the GitHub help & question page for Blue Marble.
-
-
-
- I want to report a bug. (Click to Expand)
-
- Click here to report a bug, then choose the "Bug Report" option.
-
-
-
- I want to suggest a feature. (Click to Expand)
-
- Click here to suggest a feature, then choose the Feature Request" option.
-
-
-
- I want to contribute. (Click to Expand)
-
- Click here to read the contributing guidelines.
-
-
-
- I want to report a vulnerability. (Click to Expand)
-
- Click here to submit a vulnerability report.
-
-
-
- I want to visit the website. (Click to Expand)
-
- Click here to visit the official Blue Marble website.
-
-
+This fork is based on [SwingTheVine/Wplace-BlueMarble](https://github.com/SwingTheVine/Wplace-BlueMarble) and focuses on practical improvements for everyday template work on [wplace.live](https://wplace.live/).
-Overview
-
- Welcome to Blue Marble! Blue Marble is a userscript for the website wplace.live . The purpose of Blue Marble is to allow you to take an image, and layer it onto the canvas! That way, you can easily trace the image of your art, without having to look back and forth between multiple tabs/monitors. In addition, Blue Marble supports some neat extra features such as:
-
- Displaying the number of pixels you need to level up
- Displaying a simple coordinate system (tile coordinats & pixel coordinates)
- Allowing you to move the color palette to the top of the screen when placing pixels
- Allowing you to use the eyedropper on the template image, provided the colors are correct
- Minimizing or maximizing the menu to switch between compact and full views
- Filtering overlay colors by toggling individual template colors or using global enable/disable buttons
- ...and more!
-
- If you like this userscript, please ⭐ the repository! For more information and updates, visit the Blue Marble website . If you wish to contribute to Blue Marble, check out the CONTRIBUTING.md file in docs/.
+The goal is not to replace the upstream project. This fork keeps the original Blue Marble workflow, then adds usability, workflow, and Color Filter improvements on top.
-
+## What Is Different
- Installation Instructions
-
-
-
- Blue Marble has been verified to work on mobile devices. Blue Marble was designed on Chrome, but Blue Marble might work on "unsupported" browsers not listed above. Some versions/forks of Firefox work. Some versions/forks of Firefox do not work.
-
- Installation instructions for Blue Marble are below. Click the arrows to expand the instructions you want to see. Blue text is a link.
-
-
- Install Chrome (Click to expand)
-
-
-
- Install the TamperMonkey extension for Chrome.
-
-
- Right-click the extension.
-
-
- Left-click "Manage Extension."
- Enable "Developer Mode."
-
-
- Enable "Allow user scripts."
- One-click install: Click this link to Install Blue Marble directly: Install Blue Marble
-
- TamperMonkey will automatically detect the userscript and prompt you to Install it.
- Refresh the wplace.live webpage.
-
-
-
-
- Install on Microsoft Edge (Click to expand)
-
-
- Install the TamperMonkey plugin for Microsoft Edge.
-
-
- Right-click the extension.
-
-
- Left-click "Manage Extension."
- Enable "Developer Mode."
-
-
- Download the BlueMarble.user.js file in the "Assets" of the latest release.
- Open the TamperMonkey Dashboard.
-
-
- Drag the BlueMarble.user.js file inside the dashboard of TamperMonkey.
-
-
- Click the "Install" button to Install Blue Marble.
-
-
- Enable Blue Marble inside the TamperMonkey dashboard.
-
-
- Refresh the wplace.live webpage.
-
-
-
-
- Install on Firefox (Click to expand)
-
-
- Install the TamperMonkey plugin for Firefox.
-
-
- One-click install: Click this link to Install Blue Marble directly: Install Blue Marble
-
- TamperMonkey will automatically detect the userscript and prompt you to install it.
- Refresh the wplace.live webpage.
-
-
-
+Version `0.94.0` introduces the first enhanced release:
- Template Instructions
-
- Blue Marble will display your template as the same size. If your image is 500 pixels tall and 300 pixels wide, the template will be 500 pixels tall and 300 pixels wide. Here is the instructions to display a template image on the canvas:
-
- Find the pixel of the top left corner. Fill in Tl X, Tl Y, Px X, and Px Y with the coordinates. You can use the "Pin" icon to auto-fill the coordinates after clicking the pixel.
-
-
- Upload a PNG or WEBP image.
- Click the "Create" button.
- If your template still does not show, try clicking the "Enable" button.
-
-
+- Redesigned Blue Marble windows with a minimal liquid-glass visual style.
+- Redesigned window controls, buttons, typography, spacing, and transitions.
+- Added a resizable windowed mode for Color Filter.
+- Added Color Filter position and size persistence.
+- Added persistence for shown and hidden colors in Color Filter.
+- Added automatic Color Filter refresh every 10 seconds.
+- Updated Color Filter visibility icons to match the new interface style.
- Script Settings
-
- There are many settings available for the Blue Marble userscript! Through these settings, you can control how the script behaves.
-
+## Installation
- Template Settings
-
-
Transparent Pixels
-
- Templates for Blue Marble work slightly different from normal. Since there is a "Transparent" color, and transparent pixels in templates are typically ignored, your template should have a custom color to signify "Transparent" colored pixels.
-
- If you want a specific pixel to be any color, it should be transparent in your template.
- If you want a specific pixel to be the "Transparent" color on the Wplace palette, it should have the #deface hex color.
-
-
- Coordinates
-
-
Tile Coordinates
-
- The coordinate system for wplace.live is unique. Instead of all pixels having a global coordinate number (x, y), the coordinate number is relative to the tile. This means you need to know the tile number and the coordinate number to do anything. In Blue Marble, the tile coordinates and the pixel coordinates are displayed when you click on a pixel. These are the coordinates you should use for aligning a template.
-
-
-
- Template Coordinates
-
- The template is aligned from the top left corner of the template. You can auto-fill this position using the "pin" (also called "waypoint") icon next to the coordinate input boxes.
-
-
-
-
+Install the latest userscript from the release page:
-How Versioning Works
-
- The versioning system for this userscript follows the Semantic Versioning rules . As such, it is formatted in an X.Y.Z format where:
-
- X is the major version. This is incremented when a non-backward compatible update is pushed. This is for new features that break previous versions of the userscript. Additionally, if wplace.live breaks the userscript, this will be incremented.
- Y is the minor version. This is incremented whenever I push to GitHub. This is for stable bug-fixes and new (non-breaking) features.
- Z is the patch version. This is incremented whenever I launch a development version of the userscript to test a patch. This is for unstable bug-fixes/features.
-
-
+[Download the latest release](https://github.com/alexeygasenko/Wplace-BlueMarble/releases/latest)
-Licenses
-
- (Below, all mentions of the "userscript" refer to the "Blue Marble" userscript made by SwingTheVine)
- Most of this userscript is licensed under the Mozilla Public License Version 2.0 (MPL-2.0). All software, code, and libraries in this repository are licensed under the MPL-2.0 license. However, the "Blue Marble" image in this userscript is owned by NASA and is licensed under the Creative Commons 0 1.0 Universal (CC0 1.0) license.
-
+Use `BlueMarble.user.js` with a userscript manager such as Tampermonkey, then refresh [wplace.live](https://wplace.live/).
-FAQ
-
-
Is Blue Marble malware?
- A: Blue Marble does not contain malicious code. The Blue Marble code can be found in the src/ folder. If you worry about Blue Marble being malware, you can read the code, then bundle it yourself using the tools in build/.
+## Color Filter
-
Why are some pixels not showing on the overlay?
- A: This usually happens if the template image is not converted to the Wplace color palette. You should convert your template using a color converter for Wplace, or manually adjust the template image to match the Wplace color palette. Also check that no pixels are disabled in the filter settings of the Blue Marble menu.
+Color Filter is one of the main areas improved here. It can be opened as a compact window, resized, moved around the canvas, and restored with the same size and position the next time you use it.
- How can Blue Marble place pixels for me?
- A: Unfortunately, Blue Marble will not support the automatic placement of pixels without user interaction because it is not allowed by Wplace.
+Hidden and visible colors are remembered, so you can isolate the colors you are actively painting without rebuilding the filter state every session. The list also refreshes automatically every 10 seconds, keeping pixel counts current without a manual refresh button.
-
How do I hide the overlay?
- A: You can temporarily hide the overlay by clicking the "Disable" button in the Blue Marble menu.
-
- If you want to completely remove both the overlay and the Blue Marble menu, turn off the userscript in Tampermonkey and refresh the page.
+## Upstream
- How do I tell colors apart?
- A: Find the color in the color filter list. Click the checkbox to turn the color on or off. If you want to work on only one color at a time (recommended), then click "Disable All" in the color filter. Finally, enable the checkbox next to the color you want to place. This way, only one color on your template will appear at a time.
+Original project:
- How do get the color of a pixel?
- A: Use the eyedropper in the palette menu of wplace. If your template colors match the wplace palette, you can select the template pixel dot to get the template's color for that pixel.
+[SwingTheVine/Wplace-BlueMarble](https://github.com/SwingTheVine/Wplace-BlueMarble)
- Why do game notifications appear on top of the overlay?
- A: Game notifications only appear when they need immediate attention. Therefore, they have priority over the overlay (which typically needs no attention).
-
+This fork keeps the original license and credits. For upstream documentation, contribution rules, and project background, refer to the original repository.
+
+## License
+
+Blue Marble is licensed under the Mozilla Public License 2.0. See [LICENSE.txt](../LICENSE.txt).
diff --git a/docs/assets/ComputerChromeInstall1.png b/docs/assets/ComputerChromeInstall1.png
deleted file mode 100644
index ca40cad..0000000
Binary files a/docs/assets/ComputerChromeInstall1.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall2.png b/docs/assets/ComputerChromeInstall2.png
deleted file mode 100644
index ac1f732..0000000
Binary files a/docs/assets/ComputerChromeInstall2.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall3.png b/docs/assets/ComputerChromeInstall3.png
deleted file mode 100644
index 80f7c4d..0000000
Binary files a/docs/assets/ComputerChromeInstall3.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall4.png b/docs/assets/ComputerChromeInstall4.png
deleted file mode 100644
index 6595bac..0000000
Binary files a/docs/assets/ComputerChromeInstall4.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall5.png b/docs/assets/ComputerChromeInstall5.png
deleted file mode 100644
index 7b17ddd..0000000
Binary files a/docs/assets/ComputerChromeInstall5.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall6.png b/docs/assets/ComputerChromeInstall6.png
deleted file mode 100644
index 4d16961..0000000
Binary files a/docs/assets/ComputerChromeInstall6.png and /dev/null differ
diff --git a/docs/assets/ComputerChromeInstall7.png b/docs/assets/ComputerChromeInstall7.png
deleted file mode 100644
index f7ea77d..0000000
Binary files a/docs/assets/ComputerChromeInstall7.png and /dev/null differ
diff --git a/docs/assets/ComputerEdgeInstall1.png b/docs/assets/ComputerEdgeInstall1.png
deleted file mode 100644
index 8c0a4fe..0000000
Binary files a/docs/assets/ComputerEdgeInstall1.png and /dev/null differ
diff --git a/docs/assets/ComputerEdgeInstall2.png b/docs/assets/ComputerEdgeInstall2.png
deleted file mode 100644
index caf5908..0000000
Binary files a/docs/assets/ComputerEdgeInstall2.png and /dev/null differ
diff --git a/docs/assets/ComputerEdgeInstall3.png b/docs/assets/ComputerEdgeInstall3.png
deleted file mode 100644
index 2c1bdf9..0000000
Binary files a/docs/assets/ComputerEdgeInstall3.png and /dev/null differ
diff --git a/docs/assets/ComputerEdgeInstall4.png b/docs/assets/ComputerEdgeInstall4.png
deleted file mode 100644
index e0a4743..0000000
Binary files a/docs/assets/ComputerEdgeInstall4.png and /dev/null differ
diff --git a/docs/assets/ComputerEdgeInstall5.png b/docs/assets/ComputerEdgeInstall5.png
deleted file mode 100644
index 8b8d00e..0000000
Binary files a/docs/assets/ComputerEdgeInstall5.png and /dev/null differ
diff --git a/docs/assets/ComputerFirefoxInstall1.png b/docs/assets/ComputerFirefoxInstall1.png
deleted file mode 100644
index f02fcef..0000000
Binary files a/docs/assets/ComputerFirefoxInstall1.png and /dev/null differ
diff --git a/docs/assets/ComputerFirefoxInstall2.png b/docs/assets/ComputerFirefoxInstall2.png
deleted file mode 100644
index 633fe8b..0000000
Binary files a/docs/assets/ComputerFirefoxInstall2.png and /dev/null differ
diff --git a/docs/assets/ComputerFirefoxInstall3.png b/docs/assets/ComputerFirefoxInstall3.png
deleted file mode 100644
index a2ea838..0000000
Binary files a/docs/assets/ComputerFirefoxInstall3.png and /dev/null differ
diff --git a/docs/assets/Showcase1.png b/docs/assets/Showcase1.png
deleted file mode 100644
index ef79e3f..0000000
Binary files a/docs/assets/Showcase1.png and /dev/null differ
diff --git a/docs/assets/TemplateCoordinates1.png b/docs/assets/TemplateCoordinates1.png
deleted file mode 100644
index c042228..0000000
Binary files a/docs/assets/TemplateCoordinates1.png and /dev/null differ
diff --git a/docs/assets/TemplateCoordinatesDisplay.png b/docs/assets/TemplateCoordinatesDisplay.png
deleted file mode 100644
index ffcca77..0000000
Binary files a/docs/assets/TemplateCoordinatesDisplay.png and /dev/null differ
diff --git a/docs/assets/blue-marble.png b/docs/assets/blue-marble.png
new file mode 100644
index 0000000..8648b1f
Binary files /dev/null and b/docs/assets/blue-marble.png differ
diff --git a/package-lock.json b/package-lock.json
index f078be1..703c6d3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
- "version": "0.91.116",
+ "version": "0.94.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
- "version": "0.91.116",
+ "version": "0.94.0",
"devDependencies": {
"esbuild": "^0.25.0",
"jsdoc": "^4.0.5",
@@ -37,6 +37,7 @@
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"dev": true,
+ "peer": true,
"dependencies": {
"@babel/types": "^7.28.0"
},
@@ -52,6 +53,7 @@
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
"dev": true,
+ "peer": true,
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@@ -544,7 +546,6 @@
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"dev": true,
- "peer": true,
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
@@ -741,7 +742,6 @@
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"dev": true,
- "peer": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
diff --git a/package.json b/package.json
index 95f85fa..9249617 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
- "version": "0.92.0",
+ "version": "0.94.0",
"type": "module",
"homepage": "https://bluemarble.lol/",
"repository": {
diff --git a/src/BlueMarble.meta.js b/src/BlueMarble.meta.js
index d08e900..ac5baa1 100644
--- a/src/BlueMarble.meta.js
+++ b/src/BlueMarble.meta.js
@@ -2,7 +2,7 @@
// @name Blue Marble
// @name:en Blue Marble
// @namespace https://github.com/SwingTheVine/
-// @version 0.92.0
+// @version 0.94.0
// @description A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @description:en A userscript to enhance the user experience on Wplace.live. This includes, but is not limited to: uploading images to display locally on a canvas, adding a button to move the Wplace color palette menu, and other QoL features.
// @author SwingTheVine
diff --git a/src/Overlay.js b/src/Overlay.js
index 96ceb2a..c0a06d0 100644
--- a/src/Overlay.js
+++ b/src/Overlay.js
@@ -1,3 +1,6 @@
+export const minimizeIconExpanded = ' ';
+export const minimizeIconCollapsed = ' ';
+
/** The overlay builder for the Blue Marble script.
* @description This class handles the overlay UI for the Blue Marble script.
* @class Overlay
@@ -1217,8 +1220,50 @@ export default class Overlay {
const window = button.closest('.bm-window'); // Get the window
const dragbar = button.closest('.bm-dragbar'); // Get the dragbar
- const header = window.querySelector('h1'); // Get the header
- const windowContent = window.querySelector('.bm-window-content'); // Get the window content container
+ const header = window?.querySelector('h1'); // Get the header
+ const windowContent = window?.querySelector('.bm-window-content'); // Get the window content container
+
+ if (!window || !dragbar || !windowContent) {
+ button.disabled = false;
+ button.style.textDecoration = '';
+ return;
+ }
+
+ const finishMinimizeTransition = (callback) => {
+ let isFinished = false;
+ let fallbackTimer;
+
+ const finish = () => {
+ if (isFinished) {return;}
+ isFinished = true;
+ clearTimeout(fallbackTimer);
+ windowContent.removeEventListener('transitionend', handler);
+ callback();
+ button.disabled = false;
+ button.style.textDecoration = '';
+ };
+
+ const handler = event => {
+ if (event.target != windowContent || event.propertyName != 'height') {return;}
+ finish();
+ };
+
+ windowContent.addEventListener('transitionend', handler);
+ fallbackTimer = setTimeout(finish, 360);
+ };
+
+ const getCollapsedHeight = () => {
+ const windowStyle = getComputedStyle(window);
+ const toPixels = value => parseFloat(value) || 0;
+ const extraHeight = windowStyle.boxSizing == 'border-box'
+ ? toPixels(windowStyle.paddingTop) +
+ toPixels(windowStyle.paddingBottom) +
+ toPixels(windowStyle.borderTopWidth) +
+ toPixels(windowStyle.borderBottomWidth)
+ : 0;
+
+ return Math.ceil(dragbar.getBoundingClientRect().height + extraHeight + 2);
+ };
window.parentElement.append(window); // Moves the window to the top
@@ -1227,22 +1272,29 @@ export default class Overlay {
// ...we want to close it
// Logic for the transition animation to collapse the window
+ window.dataset['widthBeforeMinimize'] = window.style.width;
+ window.dataset['heightBeforeMinimize'] = window.style.height;
+ window.dataset['minHeightBeforeMinimize'] = window.style.minHeight;
windowContent.style.height = windowContent.scrollHeight + 'px';
- window.style.width = window.scrollWidth + 'px'; // So the width of the window does not change due to the lack of content
- windowContent.style.height = '0'; // Set the height to 0px
- windowContent.addEventListener('transitionend', function handler() { // Add an event listener to cleanup once the minimize transition is complete
+ void windowContent.offsetHeight; // Force layout so the height transition always has a real start value
+ if (!window.style.width) {
+ window.style.width = window.scrollWidth + 'px'; // So the width of the window does not change due to the lack of content
+ }
+ finishMinimizeTransition(() => {
windowContent.style.display = 'none'; // Changes "display" to "none" for screen readers
- button.disabled = false; // Enables the button
- button.style.textDecoration = ''; // Resets the text decoration to default
- windowContent.removeEventListener('transitionend', handler); // Removes the event listener
});
+ windowContent.style.height = '0'; // Set the height to 0px
+ if (window.style.height || window.classList.contains('bm-windowed')) {
+ window.style.minHeight = '0px';
+ window.style.height = getCollapsedHeight() + 'px';
+ }
// Makes a clone of the h1 element inside the window, and adds it to the dragbar
- const dragbarHeader1 = header.cloneNode(true);
+ const dragbarHeader1 = header?.cloneNode(true) ?? document.createElement('h1');
const dragbarHeader1Text = dragbarHeader1.textContent;
button.nextElementSibling.appendChild(dragbarHeader1);
- button.textContent = '▶'; // Swap button icon
+ button.innerHTML = minimizeIconCollapsed; // Swap button icon
button.dataset['buttonStatus'] = 'collapsed'; // Swap button status tracker
button.ariaLabel = `Unminimize window "${dragbarHeader1Text}"`; // Screen reader label
} else {
@@ -1256,16 +1308,19 @@ export default class Overlay {
// Logic for the transition animation to expand the window
windowContent.style.display = ''; // Resets display to default
windowContent.style.height = '0'; // Sets the height to 0
- window.style.width = ''; // Resets the window width to default
- windowContent.style.height = windowContent.scrollHeight + 'px'; // Change the height back to normal
- windowContent.addEventListener('transitionend', function handler() { // Add an event listener to cleanup once the minimize transition is complete
+ window.style.width = window.dataset['widthBeforeMinimize'] ?? ''; // Restores width to the pre-minimized value
+ window.style.minHeight = window.dataset['minHeightBeforeMinimize'] ?? ''; // Restores resizable windows
+ window.style.height = window.dataset['heightBeforeMinimize'] ?? ''; // Restores height to the pre-minimized value
+ void windowContent.offsetHeight; // Force layout before expanding from 0px
+ finishMinimizeTransition(() => {
windowContent.style.height = ''; // Changes the height back to default
- button.disabled = false; // Enables the button
- button.style.textDecoration = ''; // Resets the text decoration to default
- windowContent.removeEventListener('transitionend', handler); // Removes the event listener
+ delete window.dataset['widthBeforeMinimize'];
+ delete window.dataset['heightBeforeMinimize'];
+ delete window.dataset['minHeightBeforeMinimize'];
});
+ windowContent.style.height = windowContent.scrollHeight + 'px'; // Change the height back to normal
- button.textContent = '▼'; // Swap button icon
+ button.innerHTML = minimizeIconExpanded; // Swap button icon
button.dataset['buttonStatus'] = 'expanded'; // Swap button status tracker
button.ariaLabel = `Minimize window "${dragbarHeader1Text}"`; // Screen reader label
}
@@ -1278,11 +1333,12 @@ export default class Overlay {
* @param {string} iMoveThingsSelector - The drag handle element
* @since 0.8.2
*/
- handleDrag(moveMeSelector, iMoveThingsSelector) {
+ handleDrag(moveMeSelector, iMoveThingsSelector, options = {}) {
// Retrieves the elements
const moveMe = document.querySelector(moveMeSelector);
const iMoveThings = document.querySelector(iMoveThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {});
// What to do when one of the two elements are not found
if (!moveMe || !iMoveThings) {
@@ -1375,6 +1431,14 @@ export default class Overlay {
document.removeEventListener('mouseup', endDrag);
document.removeEventListener('touchend', endDrag);
document.removeEventListener('touchcancel', endDrag);
+
+ onEnd({
+ element: moveMe,
+ x: currentX,
+ y: currentY
+ });
+
+ initialRect = null;
};
// Mouse move
@@ -1411,6 +1475,135 @@ export default class Overlay {
}, { passive: false });
}
+ /** Handles resizing of an overlay window from a resize handle.
+ * @param {string} resizeMeSelector - The element to resize
+ * @param {string} iResizeThingsSelector - The resize handle element
+ * @param {{onEnd?: function({element: HTMLElement, width: number, height: number}): void, minWidth?: number, minHeight?: number, maxWidth?: number, maxHeight?: number}} [options={}]
+ * @since 0.92.0
+ */
+ handleResize(resizeMeSelector, iResizeThingsSelector, options = {}) {
+
+ const resizeMe = document.querySelector(resizeMeSelector);
+ const iResizeThings = document.querySelector(iResizeThingsSelector);
+ const onEnd = options?.onEnd ?? (() => {});
+
+ if (!resizeMe || !iResizeThings) {
+ this.handleDisplayError(`Can not resize! ${!resizeMe ? 'resizeMe' : ''} ${!resizeMe && !iResizeThings ? 'and ' : ''}${!iResizeThings ? 'iResizeThings ' : ''}was not found!`);
+ return;
+ }
+
+ let isResizing = false;
+ let startX = 0;
+ let startY = 0;
+ let startWidth = 0;
+ let startHeight = 0;
+ let currentWidth = 0;
+ let currentHeight = 0;
+ let targetWidth = 0;
+ let targetHeight = 0;
+ let animationFrame = null;
+
+ const getMaximumWidth = () => Number.isFinite(options?.maxWidth) ? options.maxWidth : window.innerWidth - 16;
+ const getMaximumHeight = () => Number.isFinite(options?.maxHeight) ? options.maxHeight : window.innerHeight - 16;
+ const minimumWidth = Number.isFinite(options?.minWidth) ? options.minWidth : 200;
+ const minimumHeight = Number.isFinite(options?.minHeight) ? options.minHeight : 160;
+
+ const clamp = (value, minimum, maximum) => Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
+
+ const updateSize = () => {
+ if (isResizing) {
+ const deltaWidth = Math.abs(currentWidth - targetWidth);
+ const deltaHeight = Math.abs(currentHeight - targetHeight);
+
+ if (deltaWidth > 0.5 || deltaHeight > 0.5) {
+ currentWidth = targetWidth;
+ currentHeight = targetHeight;
+ resizeMe.style.width = `${currentWidth}px`;
+ resizeMe.style.height = `${currentHeight}px`;
+ }
+
+ animationFrame = requestAnimationFrame(updateSize);
+ }
+ };
+
+ const startResize = (clientX, clientY) => {
+ isResizing = true;
+ startX = clientX;
+ startY = clientY;
+ startWidth = resizeMe.offsetWidth;
+ startHeight = resizeMe.offsetHeight;
+ currentWidth = startWidth;
+ currentHeight = startHeight;
+ targetWidth = startWidth;
+ targetHeight = startHeight;
+
+ document.body.style.userSelect = 'none';
+ iResizeThings.classList.add('bm-resizing');
+
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('touchmove', onTouchMove, { passive: false });
+ document.addEventListener('mouseup', endResize);
+ document.addEventListener('touchend', endResize);
+ document.addEventListener('touchcancel', endResize);
+
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ }
+ updateSize();
+ };
+
+ const endResize = () => {
+ isResizing = false;
+ if (animationFrame) {
+ cancelAnimationFrame(animationFrame);
+ animationFrame = null;
+ }
+ document.body.style.userSelect = '';
+ iResizeThings.classList.remove('bm-resizing');
+
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('touchmove', onTouchMove);
+ document.removeEventListener('mouseup', endResize);
+ document.removeEventListener('touchend', endResize);
+ document.removeEventListener('touchcancel', endResize);
+
+ onEnd({
+ element: resizeMe,
+ width: currentWidth,
+ height: currentHeight
+ });
+ };
+
+ const onMouseMove = event => {
+ if (!isResizing) {return;}
+ targetWidth = clamp(startWidth + (event.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (event.clientY - startY), minimumHeight, getMaximumHeight());
+ };
+
+ const onTouchMove = event => {
+ if (!isResizing) {return;}
+ const touch = event?.touches?.[0];
+ if (!touch) {return;}
+ targetWidth = clamp(startWidth + (touch.clientX - startX), minimumWidth, getMaximumWidth());
+ targetHeight = clamp(startHeight + (touch.clientY - startY), minimumHeight, getMaximumHeight());
+ event.preventDefault();
+ };
+
+ iResizeThings.addEventListener('mousedown', event => {
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(event.clientX, event.clientY);
+ });
+
+ iResizeThings.addEventListener('touchstart', event => {
+ const touch = event?.touches?.[0];
+ if (!touch) {return;}
+ event.preventDefault();
+ event.stopPropagation();
+ startResize(touch.clientX, touch.clientY);
+ }, { passive: false });
+ }
+
/** Handles status display.
* This will output plain text into the output Status box.
* Additionally, this will output an info message to the console.
@@ -1434,4 +1627,4 @@ export default class Overlay {
consoleError(`${this.name}: ${text}`); // Outputs something like "ScriptName: text" as an error message to the console
this.updateInnerHTML(this.outputStatusId, 'Error: ' + text, true); // Update output Status box
}
-}
\ No newline at end of file
+}
diff --git a/src/WindowCredits.js b/src/WindowCredits.js
index 6cef669..13b12cc 100644
--- a/src/WindowCredits.js
+++ b/src/WindowCredits.js
@@ -1,4 +1,4 @@
-import Overlay from "./Overlay";
+import Overlay, { minimizeIconExpanded } from "./Overlay";
import { localizeDate } from "./utils";
/** Manages the credits window for Blue Marble.
@@ -54,7 +54,7 @@ export default class WindowCredts extends Overlay {
// Creates a new credits window
this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window'}, (instance, div) => {})
.addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Credits"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Credits"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {button.click()}; // Needed only to negate weird interaction with dragbar
}).buildElement()
@@ -116,4 +116,4 @@ export default class WindowCredts extends Overlay {
// Creates dragging capability on the drag bar for dragging the window
this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
}
-}
\ No newline at end of file
+}
diff --git a/src/WindowFilter.css b/src/WindowFilter.css
index bc5135f..6b9027f 100644
--- a/src/WindowFilter.css
+++ b/src/WindowFilter.css
@@ -1,10 +1,117 @@
/* @since 0.88.459 */
-/* Filter window eye icon inside paragraphs */
+/* Filter window visibility icon */
+#bm-window-filter .bm-filter-eye-icon {
+ display: block;
+ width: 1.28em;
+ height: 1.28em;
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.9;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ overflow: visible;
+}
+
+/* Filter window inline icons inside paragraphs */
#bm-window-filter p svg {
- display: inline;
+ display: inline-block;
height: 1em;
- fill: white;
+ width: 1em;
+ color: currentColor;
+ vertical-align: -0.16em;
+}
+
+#bm-window-filter:not(.bm-windowed) {
+ width: min(50rem, calc(100vw - 0.55rem));
+ max-width: min(50rem, calc(100vw - 0.55rem)) !important;
+}
+
+#bm-window-filter .bm-filter-header {
+ padding-top: 0.08rem;
+}
+
+#bm-window-filter .bm-filter-toolbar {
+ gap: 0.22rem;
+ flex-wrap: wrap;
+ width: 100%;
+ padding: 0.16rem;
+}
+
+#bm-window-filter .bm-filter-toolbar > button {
+ flex: 1 1 10rem;
+}
+
+#bm-window-filter .bm-filter-scrollable {
+ padding-right: 0.08rem;
+}
+
+#bm-window-filter .bm-filter-insights {
+ display: grid;
+ grid-template-columns: minmax(16rem, 20rem) minmax(0, 1fr);
+ gap: 0.24rem 0.3rem;
+ align-items: stretch;
+}
+
+#bm-window-filter .bm-filter-insights > hr,
+#bm-window-filter .bm-filter-sort-panel {
+ grid-column: 1 / -1;
+}
+
+#bm-window-filter .bm-filter-stats-card,
+#bm-window-filter .bm-filter-note,
+#bm-window-filter .bm-filter-sort-panel {
+ padding: 0.38rem 0.48rem;
+ border-radius: 13px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background: linear-gradient(155deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.07));
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.16),
+ 0 8px 20px rgba(0, 0, 0, 0.08);
+}
+
+#bm-window-filter .bm-filter-stats-card {
+ display: grid;
+ gap: 0.18rem;
+}
+
+#bm-window-filter .bm-filter-stats-card br {
+ display: none;
+}
+
+#bm-window-filter .bm-filter-stats-card span {
+ display: block;
+ padding: 0.28rem 0.38rem;
+ border-radius: 10px;
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.08));
+}
+
+#bm-window-filter .bm-filter-note p {
+ margin: 0;
+}
+
+#bm-window-filter .bm-filter-sort-panel fieldset {
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+
+#bm-window-filter .bm-filter-sort-panel legend {
+ margin-bottom: 0.12rem;
+}
+
+#bm-window-filter .bm-filter-sort-panel .bm-container {
+ margin-top: 0.1rem;
+ margin-bottom: 0.1rem;
+}
+
+#bm-window-filter .bm-filter-sort-actions {
+ display: flex;
+ justify-content: flex-start;
+}
+
+#bm-window-filter .bm-filter-sort-actions button {
+ min-width: 7.8rem;
}
/* Filter flex */
@@ -13,33 +120,63 @@
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
- gap: 1em 3ch;
+ align-items: stretch;
+ gap: 0.3rem;
}
/* Filter color */
#bm-window-filter .bm-filter-color {
+ position: relative;
+ overflow: hidden;
width: fit-content;
max-width: 35ch;
- background-color: rgba(21, 48, 99, 0.9);
- border-radius: 1em;
- padding: 0.5em;
- gap: 1ch;
- transition: background-color 0.3s ease;
+ padding: 0.28rem;
+ gap: 0.28rem;
+ border-radius: 13px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background: linear-gradient(160deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.07));
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.16),
+ 0 8px 20px rgba(0, 0, 0, 0.08);
+ transition:
+ background 0.25s ease,
+ border-color 0.25s ease,
+ box-shadow 0.25s ease,
+ transform 0.25s ease;
+}
+
+#bm-window-filter .bm-filter-color::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(circle at top right, rgba(255, 255, 255, 0.18), transparent 24%),
+ radial-gradient(circle at 18% 0%, rgba(186, 246, 255, 0.12), transparent 22%);
}
/* Filter color on hover */
#bm-window-filter .bm-filter-color:hover,
-#bm-window-filter.bm-filter-color:focus-within {
- background-color: rgba(17, 40, 85, 0.9);
+#bm-window-filter .bm-filter-color:focus-within {
+ transform: translateY(-2px);
+ border-color: rgba(146, 221, 255, 0.26);
+ background: linear-gradient(160deg, rgba(255, 255, 255, 0.22), rgba(186, 246, 255, 0.1));
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.2),
+ 0 10px 24px rgba(0, 0, 0, 0.1);
}
/* Filter window container for RGB color display */
#bm-window-filter .bm-filter-container-rgb {
display: block;
- border: thick double darkslategray;
width: fit-content;
height: fit-content;
- padding: 1ch;
+ padding: 0.26rem;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 10px;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.14),
+ 0 6px 16px rgba(0, 0, 0, 0.08);
}
/* Filter window container for RGB color display for Other color */
@@ -60,8 +197,9 @@
background: url('data:image/svg+xml;utf8, ') repeat;
background-color: transparent !important;
}
+
#bm-window-filter .bm-filter-color[data-id="-1"] .bm-filter-container-rgb svg {
- fill: white !important;
+ color: white !important;
}
/* Filter window container for RGB color display for Transparent color */
@@ -71,19 +209,36 @@
/* Filter window hide color button */
#bm-window-filter .bm-filter-container-rgb button {
- padding: 0.75em 0.5ch;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.24em;
+ min-width: 2.05rem;
+ min-height: 2.05rem;
+ border-radius: 999px;
+ line-height: 1;
}
/* Filter window hide color button SVG */
-#bm-window-filter .bm-filter-container-rgb svg {
- width: 4ch;
+#bm-window-filter .bm-filter-container-rgb .bm-filter-eye-icon {
+ width: 1.55rem;
+ height: 1.55rem;
+ filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.18));
}
/* Filter window container for color information */
#bm-window-filter .bm-filter-color > .bm-flex-between {
flex-direction: column;
align-items: flex-start;
- gap: 0;
+ gap: 0.02rem;
+}
+
+#bm-window-filter .bm-filter-color h2 {
+ margin: 0.04rem 0 0;
+}
+
+#bm-window-filter .bm-filter-color .bm-filter-color-pxl-desc {
+ margin: 0.1rem 0 0;
}
/* Filter window color flavor text */
@@ -98,46 +253,186 @@
/* WINDOWED MODE */
+/* Resizable filter window in windowed mode */
+#bm-window-filter.bm-windowed {
+ --bm-scrollable-max-height: 100%;
+ display: grid;
+ grid-template-rows: auto minmax(0, 1fr);
+ width: 360px;
+ height: min(60vh, 22rem);
+ min-width: 360px;
+ min-height: 180px;
+ max-width: min(1000px, calc(100vw - 16px)) !important;
+ max-height: min(1400px, calc(100vh - 16px)) !important;
+ overflow: hidden;
+ box-sizing: border-box;
+ position: fixed;
+ transition:
+ background 320ms ease,
+ border-color 220ms ease,
+ box-shadow 220ms ease,
+ transform 0s;
+}
+
+/* Keep the content area flexible inside the resizable window */
+#bm-window-filter.bm-windowed .bm-window-content {
+ display: grid;
+ grid-template-rows: auto auto auto auto minmax(0, 1fr);
+ grid-row: 2;
+ min-height: 0;
+ min-width: 0;
+ overflow: hidden;
+}
+
+#bm-window-filter.bm-windowed .bm-filter-toolbar {
+ gap: 0.16rem;
+ flex-wrap: nowrap;
+ width: 100%;
+ padding: 0.12rem;
+}
+
+#bm-window-filter.bm-windowed .bm-filter-toolbar > button {
+ flex: 1 1 0;
+ min-width: 0;
+}
+
/* Filter flex in windowed mode */
#bm-window-filter.bm-windowed #bm-filter-flex {
flex-direction: column;
- gap: 0.25em;
+ align-items: stretch;
+ gap: 0.16rem;
+ width: 100%;
+ align-self: stretch;
+ min-width: 0;
+ box-sizing: border-box;
}
/* Filter color in windowed mode */
#bm-window-filter.bm-windowed .bm-filter-color {
- width: auto;
+ width: 100%;
+ max-width: none;
+ align-self: stretch;
+ flex: 1 1 auto;
+ min-width: 0;
margin: 0;
- padding: 0;
+ padding: 0.12rem;
+ border-radius: 11px;
+ box-sizing: border-box;
+}
+
+#bm-window-filter.bm-windowed .bm-filter-color > .bm-flex-between {
+ width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
+}
+
+/* Let the scroll area grow and shrink with the resizable window */
+#bm-window-filter.bm-windowed .bm-container.bm-scrollable {
+ display: block;
+ grid-row: 5;
+ min-height: 0;
+ min-width: 0;
+ height: 100%;
+ width: 100%;
+ max-height: 100% !important;
+ overflow: auto;
+ box-sizing: border-box;
+}
+
+/* Visible resize handle in the bottom-right corner */
+#bm-window-filter.bm-windowed .bm-resize-corner {
+ position: absolute;
+ right: 4px;
+ bottom: 4px;
+ display: flex;
+ width: 20px;
+ height: 20px;
+ align-items: flex-end;
+ justify-content: flex-end;
+ padding-right: 4px;
+ padding-bottom: 4px;
+ box-sizing: border-box;
+ z-index: 5;
+ cursor: nwse-resize;
+ pointer-events: auto;
+ opacity: 0.78;
+ touch-action: none;
+ user-select: none;
+ color: rgba(255, 255, 255, 0.86);
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.14), rgba(83, 141, 255, 0.14));
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ border-radius: 8px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
+}
+
+#bm-window-filter.bm-windowed .bm-resize-corner:hover,
+#bm-window-filter.bm-windowed .bm-resize-corner.bm-resizing {
+ opacity: 1;
+ border-color: rgba(146, 221, 255, 0.36);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
+ 0 8px 16px rgba(0, 0, 0, 0.16);
}
/* Filter window container for RGB color display in windowed mode */
#bm-window-filter.bm-windowed .bm-filter-container-rgb {
display: flex;
width: 100%;
+ min-width: 0;
+ flex: 1 1 auto;
gap: 0.5ch;
align-items: center;
- padding: 0.1em 0.5ch;
+ padding: 0.1rem 0.2rem;
border: none;
- border-radius: 1em;
+ border-radius: 8px;
+ box-sizing: border-box;
}
/* Filter window hide color button */
#bm-window-filter.bm-windowed .bm-filter-container-rgb button {
- padding: 0.5em 0.25ch;
+ padding: 0.2em;
+ flex: 0 0 auto;
}
/* Filter window hide color button SVG in windowed mode */
-#bm-window-filter.bm-windowed .bm-filter-container-rgb svg {
- width: 3ch;
+#bm-window-filter.bm-windowed .bm-filter-container-rgb .bm-filter-eye-icon {
+ width: 1.36rem;
+ height: 1.36rem;
}
/* Filter window header 2 in windowed mode */
#bm-window-filter.bm-windowed .bm-filter-color h2 {
- font-size: 0.75em;
+ font-size: 0.78rem;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
/* Filter window dragbar text area in windowed mode */
#bm-window-filter #bm-filter-windowed-color-totals {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.08rem 0.24rem;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.12);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
font-size: 1em;
-}
\ No newline at end of file
+}
+
+@media (max-width: 980px) {
+ #bm-window-filter:not(.bm-windowed) {
+ width: min(100vw - 0.35rem, 50rem);
+ max-width: min(100vw - 0.35rem, 50rem) !important;
+ }
+
+ #bm-window-filter .bm-filter-insights {
+ grid-template-columns: 1fr;
+ }
+
+ #bm-window-filter .bm-filter-note,
+ #bm-window-filter .bm-filter-sort-panel,
+ #bm-window-filter .bm-filter-insights > hr {
+ grid-column: auto;
+ }
+}
diff --git a/src/WindowFilter.js b/src/WindowFilter.js
index 9cedaa8..4c0ed15 100644
--- a/src/WindowFilter.js
+++ b/src/WindowFilter.js
@@ -1,7 +1,11 @@
import ConfettiManager from "./confetttiManager";
-import Overlay from "./Overlay";
+import Overlay, { minimizeIconExpanded } from "./Overlay";
import { calculateRelativeLuminance, localizeDate, localizeNumber, localizePercent, rgbToHex } from "./utils";
+const closeIcon = ' ';
+const fullscreenIcon = ' ';
+const windowedIcon = ' ';
+
/** The overlay builder for the color filter Blue Marble window.
* @description This class handles the overlay UI for the color filter window of the Blue Marble userscript.
* @class WindowFilter
@@ -21,13 +25,25 @@ export default class WindowFilter extends Overlay {
this.windowID = 'bm-window-filter'; // The ID attribute for this window
this.colorListID = 'bm-filter-flex'; // The ID attribute for the color list
this.windowParent = document.body; // The parent of the window DOM tree
+ this.settingsManager = executor.settingsManager ?? null; // Settings manager from the executor
+ this.windowModeFlag = 'ftr-oWin'; // User setting flag for opening the filter in windowed mode
+ this.windowStateKey = 'windowFilter'; // User setting key for the persisted window state
+ this.windowResizeObserver = null; // Resize observer for the windowed mode
+ this.windowViewportResizeHandler = null; // Resize handler for viewport changes
+ this.windowSaveTimeout = null; // Debounce timer for resize persistence
+ this.colorRefreshInterval = null; // Auto-refresh timer for live color statistics
+ this.colorRefreshIntervalMS = 10000; // Refresh Color Filter statistics every 10 seconds
+ this.windowMinWidth = 360; // Minimum width for the windowed filter
+ this.windowMinHeight = 220; // Minimum height for the windowed filter
+ this.windowMaxWidth = 1000; // Maximum width for the windowed filter
+ this.windowMaxHeight = 1400; // Maximum height for the windowed filter
/** The templateManager instance currently being used. @type {TemplateManager} */
this.templateManager = executor.apiManager?.templateManager;
// Eye icons
- this.eyeOpen = ' ';
- this.eyeClosed = ' ';
+ this.eyeOpen = ' ';
+ this.eyeClosed = ' ';
// Obtains the color palette Blue Marble currently uses
const { palette: palette, LUT: _ } = this.templateManager.paletteBM;
@@ -46,11 +62,22 @@ export default class WindowFilter extends Overlay {
this.timeRemainingLocalized = ''; // The date & time the user will complete the templates in the date-time format of the user's device, as a string
// Color list display settings
- this.sortPrimary = 'id'; // The last used primary sort option
- this.sortSecondary = 'ascending'; // The last used secondary sort option
+ this.sortPrimary = 'total'; // The last used primary sort option
+ this.sortSecondary = 'descending'; // The last used secondary sort option
this.showUnused = false; // Were unused colors shown the last time the user sorted the color list?
}
+ /** Builds the preferred filter window mode for the user.
+ * @since 0.92.0
+ */
+ buildPreferredWindow() {
+ if (this.#prefersWindowedMode()) {
+ this.buildWindowed();
+ return;
+ }
+ this.buildWindow();
+ }
+
/** Spawns a Color Filter window.
* If another color filter window already exists, we DON'T spawn another!
* Parent/child relationships in the DOM structure below are indicated by indentation.
@@ -60,7 +87,7 @@ export default class WindowFilter extends Overlay {
// If a color filter wizard window already exists, close it
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ this.#closeWindow();
return;
}
@@ -71,48 +98,43 @@ export default class WindowFilter extends Overlay {
// div.parentElement.appendChild(div); // When the window is clicked on, bring to top
// }
}).addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Color Filter"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Color Filter"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {button.click()}; // Needed only to negate weird interaction with dragbar
}).buildElement()
.addDiv().buildElement() // Contains the minimized h1 element
.addDiv({'class': 'bm-flex-center'})
- .addButton({'class': 'bm-button-circle', 'textContent': '🗗', 'aria-label': 'Switch to windowed mode for "Color Filter"'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': windowedIcon, 'aria-label': 'Switch to windowed mode for "Color Filter"'}, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ this.#setWindowModePreference(true);
+ this.#closeWindow();
this.buildWindowed();
};
button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
}).buildElement()
- .addButton({'class': 'bm-button-circle', 'textContent': '✖', 'aria-label': 'Close window "Color Filter"'}, (instance, button) => {
- button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
+ .addButton({'class': 'bm-button-circle', 'innerHTML': closeIcon, 'aria-label': 'Close window "Color Filter"'}, (instance, button) => {
+ button.onclick = () => this.#closeWindow();
button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
}).buildElement()
.buildElement()
.buildElement()
.addDiv({'class': 'bm-window-content'})
- .addDiv({'class': 'bm-container bm-center-vertically'})
+ .addDiv({'class': 'bm-container bm-center-vertically bm-filter-header'})
.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': 'Hide All Colors'}, (instance, button) => {
+ .addDiv({'class': 'bm-container bm-flex-between bm-center-vertically bm-filter-toolbar', 'style': 'gap: 1.5ch;'})
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'Hide All Colors'}, (instance, button) => {
button.onclick = () => this.#selectColorList(false);
}).buildElement()
- .addButton({'textContent': 'Refresh Data'}, (instance, button) => {
- button.onclick = () => {
- button.disabled = true;
- this.updateColorList();
- button.disabled = false;
- };
- }).buildElement()
- .addButton({'textContent': 'Show All Colors'}, (instance, button) => {
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'Show All Colors'}, (instance, button) => {
button.onclick = () => this.#selectColorList(true);
}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container bm-scrollable'})
- .addDiv({'class': 'bm-container', 'style': 'margin-left: 2.5ch; margin-right: 2.5ch;'})
- .addDiv({'class': 'bm-container'})
+ .addHr().buildElement()
+ .addDiv({'class': 'bm-container bm-scrollable bm-filter-scrollable'})
+ .addDiv({'class': 'bm-container bm-filter-insights', 'style': 'margin-left: 2.5ch; margin-right: 2.5ch;'})
+ .addDiv({'class': 'bm-container bm-filter-stats-card'})
.addSpan({'id': 'bm-filter-tile-load', 'innerHTML': 'Tiles Loaded: 0 / ???'}).buildElement()
.addBr().buildElement()
.addSpan({'id': 'bm-filter-tot-correct', 'innerHTML': 'Correct Pixels: ???'}).buildElement()
@@ -123,11 +145,11 @@ export default class WindowFilter extends Overlay {
.addBr().buildElement()
.addSpan({'id': 'bm-filter-tot-completed', 'innerHTML': '??? ???'}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container'})
- .addP({'innerHTML': `Press the 🗗 button to make this window smaller. Colors with the icon ${this.eyeOpen.replace(' {
+ .addDiv({'class': 'bm-container bm-filter-sort-actions'})
+ .addButton({'class': 'bm-button-primary', 'textContent': 'Sort Colors', 'type': 'submit'}, (instance, button) => {
button.onclick = (event) => {
event.preventDefault(); // Stop default form submission
@@ -183,6 +205,7 @@ export default class WindowFilter extends Overlay {
// These run when the user opens the Color Filter window
this.#buildColorList(scrollableContainer);
+ this.#syncSortFormControls();
this.#sortColorList(this.sortPrimary, this.sortSecondary, this.showUnused);
// Displays some template statistics to the user
@@ -191,6 +214,7 @@ export default class WindowFilter extends Overlay {
this.updateInnerHTML('#bm-filter-tot-total', `Total Pixels: ${localizeNumber(this.allPixelsTotal)}`);
this.updateInnerHTML('#bm-filter-tot-remaining', `Remaining: ${localizeNumber((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0))} (${localizePercent(((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0)) / (this.allPixelsTotal || 1))})`);
this.updateInnerHTML('#bm-filter-tot-completed', `Completed at: ${this.timeRemainingLocalized} `);
+ this.#startAutoRefresh();
}
/** Spawns a windowed Color Filter window.
@@ -202,14 +226,18 @@ export default class WindowFilter extends Overlay {
// If a color filter wizard window already exists, close it
if (document.querySelector(`#${this.windowID}`)) {
- document.querySelector(`#${this.windowID}`).remove();
+ this.#closeWindow();
return;
}
// Creates a new windowed color filter window
- this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window bm-windowed'})
+ this.window = this.addDiv({
+ 'id': this.windowID,
+ 'class': 'bm-window bm-windowed',
+ 'style': `width: 360px; height: min(70vh, 32rem); min-width: ${this.windowMinWidth}px; min-height: ${this.windowMinHeight}px; max-width: min(${this.windowMaxWidth}px, calc(100vw - 16px)); max-height: min(${this.windowMaxHeight}px, calc(100vh - 16px));`
+ })
.addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Color Filter"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Color Filter"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => {
const windowedColorTotals = document.querySelector('#bm-filter-windowed-color-totals');
if (windowedColorTotals) {
@@ -224,54 +252,318 @@ export default class WindowFilter extends Overlay {
// Minimized h1 element will appear here
.buildElement()
.addDiv({'class': 'bm-flex-center'})
- .addButton({'class': 'bm-button-circle', 'textContent': '🗖', 'aria-label': 'Switch to fullscreen mode for "Color Filter"'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': fullscreenIcon, 'aria-label': 'Switch to fullscreen mode for "Color Filter"'}, (instance, button) => {
button.onclick = () => {
- document.querySelector(`#${this.windowID}`)?.remove();
+ this.#setWindowModePreference(false);
+ this.#closeWindow();
this.buildWindow();
};
button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
}).buildElement()
- .addButton({'class': 'bm-button-circle', 'textContent': '✖', 'aria-label': 'Close window "Color Filter"'}, (instance, button) => {
- button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
+ .addButton({'class': 'bm-button-circle', 'innerHTML': closeIcon, 'aria-label': 'Close window "Color Filter"'}, (instance, button) => {
+ button.onclick = () => this.#closeWindow();
button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
}).buildElement()
.buildElement()
.buildElement()
.addDiv({'class': 'bm-window-content'})
- .addDiv({'class': 'bm-container bm-center-vertically'})
+ .addDiv({'class': 'bm-container bm-center-vertically bm-filter-header'})
.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': 'None'}, (instance, button) => {
+ .addDiv({'class': 'bm-container bm-flex-between bm-center-vertically bm-filter-toolbar', 'style': 'gap: 1.5ch;'})
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'None'}, (instance, button) => {
button.onclick = () => this.#selectColorList(false);
}).buildElement()
- .addButton({'textContent': 'Refresh'}, (instance, button) => {
- button.onclick = () => {
- button.disabled = true;
- this.updateColorList();
- button.disabled = false;
- };
- }).buildElement()
- .addButton({'textContent': 'All'}, (instance, button) => {
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'All'}, (instance, button) => {
button.onclick = () => this.#selectColorList(true);
}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container bm-scrollable'})
+ .addHr().buildElement()
+ .addDiv({'class': 'bm-container bm-scrollable bm-filter-scrollable'})
// Color list will appear here
.buildElement()
.buildElement()
+ .addDiv({
+ 'class': 'bm-resize-corner',
+ 'title': 'Resize Color Filter window',
+ 'aria-label': 'Resize Color Filter window',
+ 'role': 'presentation',
+ 'textContent': '◢',
+ 'style': 'position: absolute; right: 0; bottom: 0; width: 28px; height: 28px; display: flex; align-items: flex-end; justify-content: flex-end; padding-right: 4px; padding-bottom: 4px; box-sizing: border-box; z-index: 5; cursor: nwse-resize; pointer-events: auto; touch-action: none; user-select: none; font-size: 8px; line-height: 1; color: rgba(255,255,255,0.95); background: transparent; border: none; box-shadow: none;'
+ }).buildElement()
.buildElement().buildOverlay(this.windowParent);
- // Creates dragging capability on the drag bar for dragging the window
- this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
+ this.#initializeWindowedPersistence();
// Obtains the scrollable container to put the color filter in
const scrollableContainer = document.querySelector(`#${this.windowID} .bm-container.bm-scrollable`);
// These run when the user opens the Color Filter window
this.#buildColorList(scrollableContainer);
+ this.#syncSortFormControls();
this.#sortColorList(this.sortPrimary, this.sortSecondary, this.showUnused);
+ this.#startAutoRefresh();
+ }
+
+ /** Retrieves the persisted window state object.
+ * @returns {Object | null}
+ * @since 0.92.0
+ */
+ #getWindowState() {
+ if (!this.settingsManager) {return null;}
+ this.settingsManager.userSettings[this.windowStateKey] ??= {};
+ return this.settingsManager.userSettings[this.windowStateKey];
+ }
+
+ /** Returns whether the filter should open in windowed mode.
+ * Defaults to windowed mode when no explicit preference was stored.
+ * @returns {boolean}
+ * @since 0.92.1
+ */
+ #prefersWindowedMode() {
+ const windowState = this.#getWindowState();
+ if (windowState?.mode == 'windowed') {return true;}
+ if (windowState?.mode == 'fullscreen') {return false;}
+ return true;
+ }
+
+ /** Updates the preferred window mode setting.
+ * @param {boolean} shouldBeWindowed
+ * @since 0.92.0
+ */
+ #setWindowModePreference(shouldBeWindowed) {
+ const windowState = this.#getWindowState();
+ if (windowState) {
+ windowState.mode = shouldBeWindowed ? 'windowed' : 'fullscreen';
+ }
+ if (!this.settingsManager) {return;}
+ this.settingsManager.toggleFlag(this.windowModeFlag, shouldBeWindowed);
+ void this.settingsManager.saveUserStorageNow();
+ }
+
+ /** Updates the visible sort controls to reflect the active sort state.
+ * @since 0.92.1
+ */
+ #syncSortFormControls() {
+ const sortPrimaryInput = document.querySelector(`#${this.windowID} #bm-filter-sort-primary`);
+ const sortSecondaryInput = document.querySelector(`#${this.windowID} #bm-filter-sort-secondary`);
+ const showUnusedInput = document.querySelector(`#${this.windowID} #bm-filter-show-unused`);
+
+ if (sortPrimaryInput instanceof HTMLSelectElement) {
+ sortPrimaryInput.value = this.sortPrimary;
+ }
+ if (sortSecondaryInput instanceof HTMLSelectElement) {
+ sortSecondaryInput.value = this.sortSecondary;
+ }
+ if (showUnusedInput instanceof HTMLInputElement) {
+ showUnusedInput.checked = this.showUnused;
+ }
+ }
+
+ /** Immediately closes the filter window and cleans up persistence observers.
+ * @since 0.92.0
+ */
+ #closeWindow() {
+ const windowElement = document.querySelector(`#${this.windowID}`);
+ if (windowElement?.classList.contains('bm-windowed')) {
+ this.#saveWindowState(windowElement);
+ }
+ this.#stopAutoRefresh();
+ this.#cleanupWindowPersistence();
+ windowElement?.remove();
+ }
+
+ /** Starts the automatic Color Filter statistics refresh loop.
+ * @since 0.92.1
+ */
+ #startAutoRefresh() {
+ this.#stopAutoRefresh();
+ this.colorRefreshInterval = setInterval(() => {
+ if (!document.querySelector(`#${this.windowID}`)) {
+ this.#stopAutoRefresh();
+ return;
+ }
+ this.updateColorList();
+ }, this.colorRefreshIntervalMS);
+ }
+
+ /** Stops the automatic Color Filter statistics refresh loop.
+ * @since 0.92.1
+ */
+ #stopAutoRefresh() {
+ if (!this.colorRefreshInterval) {return;}
+ clearInterval(this.colorRefreshInterval);
+ this.colorRefreshInterval = null;
+ }
+
+ /** Disconnects live observers used for window persistence.
+ * @since 0.92.0
+ */
+ #cleanupWindowPersistence() {
+ if (this.windowResizeObserver) {
+ this.windowResizeObserver.disconnect();
+ this.windowResizeObserver = null;
+ }
+ if (this.windowViewportResizeHandler) {
+ window.removeEventListener('resize', this.windowViewportResizeHandler);
+ this.windowViewportResizeHandler = null;
+ }
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ this.windowSaveTimeout = null;
+ }
+ }
+
+ /** Returns a clamped dimension value for the window.
+ * @param {number} size - The size in pixels
+ * @param {number} minimum - Minimum allowed size
+ * @param {number} maximum - Maximum allowed size
+ * @returns {number}
+ * @since 0.92.0
+ */
+ #clampWindowDimension(size, minimum, maximum) {
+ const resolvedMaximum = Math.max(minimum, maximum);
+ return Math.min(Math.max(Math.round(Number(size) || minimum), minimum), resolvedMaximum);
+ }
+
+ /** Returns a viewport-safe position for the window.
+ * @param {HTMLElement} windowElement
+ * @param {number} x
+ * @param {number} y
+ * @returns {{x: number, y: number}}
+ * @since 0.92.0
+ */
+ #clampWindowPosition(windowElement, x, y) {
+ const margin = 8;
+ const maxX = Math.max(margin, window.innerWidth - windowElement.offsetWidth - margin);
+ const maxY = Math.max(margin, window.innerHeight - windowElement.offsetHeight - margin);
+ return {
+ x: Math.min(Math.max(Math.round(Number(x) || margin), margin), maxX),
+ y: Math.min(Math.max(Math.round(Number(y) || margin), margin), maxY)
+ };
+ }
+
+ /** Applies the persisted size and position to the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ #restoreWindowState(windowElement) {
+ const windowState = this.#getWindowState();
+ if (!windowState || !windowElement) {return;}
+
+ const width = Number(windowState.width);
+ const height = Number(windowState.height);
+ const hasWidth = Number.isFinite(width);
+ const hasHeight = Number.isFinite(height);
+
+ if (hasWidth) {
+ windowState.width = this.#clampWindowDimension(width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ windowElement.style.width = `${windowState.width}px`;
+ }
+ if (hasHeight) {
+ windowState.height = this.#clampWindowDimension(height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+ windowElement.style.height = `${windowState.height}px`;
+ }
+
+ requestAnimationFrame(() => {
+ if (!windowElement.isConnected) {return;}
+
+ const x = Number(windowState.x);
+ const y = Number(windowState.y);
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {return;}
+
+ const clampedPosition = this.#clampWindowPosition(windowElement, x, y);
+ windowElement.style.left = '0px';
+ windowElement.style.top = '0px';
+ windowElement.style.right = '';
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+
+ if ((clampedPosition.x != x) || (clampedPosition.y != y)) {
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ void this.settingsManager?.saveUserStorageNow();
+ }
+ });
+ }
+
+ /** Saves the current size and position of the windowed filter.
+ * @param {HTMLElement} windowElement
+ * @since 0.92.0
+ */
+ #saveWindowState(windowElement) {
+ const windowState = this.#getWindowState();
+ if (!windowState || !windowElement?.isConnected || !windowElement.classList.contains('bm-windowed')) {return;}
+ if (windowElement.querySelector('.bm-dragbar button[data-button-status="collapsed"]')) {return;}
+
+ const rect = windowElement.getBoundingClientRect();
+ const width = this.#clampWindowDimension(rect.width, this.windowMinWidth, Math.min(this.windowMaxWidth, window.innerWidth - 16));
+ const height = this.#clampWindowDimension(rect.height, this.windowMinHeight, Math.min(this.windowMaxHeight, window.innerHeight - 16));
+
+ if (Math.round(rect.width) != width) {
+ windowElement.style.width = `${width}px`;
+ }
+ if (Math.round(rect.height) != height) {
+ windowElement.style.height = `${height}px`;
+ }
+
+ const clampedPosition = this.#clampWindowPosition(windowElement, rect.left, rect.top);
+ windowElement.style.left = '0px';
+ windowElement.style.top = '0px';
+ windowElement.style.right = '';
+ windowElement.style.transform = `translate(${clampedPosition.x}px, ${clampedPosition.y}px)`;
+
+ windowState.x = clampedPosition.x;
+ windowState.y = clampedPosition.y;
+ windowState.width = width;
+ windowState.height = height;
+
+ void this.settingsManager?.saveUserStorageNow();
+ }
+
+ /** Debounces persisting the current window size and position.
+ * @param {HTMLElement} windowElement
+ * @param {number} [delay=150]
+ * @since 0.92.0
+ */
+ #scheduleWindowStateSave(windowElement, delay = 150) {
+ if (this.windowSaveTimeout) {
+ clearTimeout(this.windowSaveTimeout);
+ }
+ this.windowSaveTimeout = setTimeout(() => {
+ this.windowSaveTimeout = null;
+ this.#saveWindowState(windowElement);
+ }, delay);
+ }
+
+ /** Enables persistence and resize handling for the windowed filter.
+ * @since 0.92.0
+ */
+ #initializeWindowedPersistence() {
+ const windowElement = document.querySelector(`#${this.windowID}.bm-window`);
+ if (!windowElement) {return;}
+
+ this.#cleanupWindowPersistence();
+ this.#restoreWindowState(windowElement);
+
+ this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`, {
+ onEnd: ({element}) => this.#saveWindowState(element)
+ });
+ this.handleResize(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-resize-corner`, {
+ minWidth: this.windowMinWidth,
+ minHeight: this.windowMinHeight,
+ maxWidth: Math.min(this.windowMaxWidth, window.innerWidth - 16),
+ maxHeight: Math.min(this.windowMaxHeight, window.innerHeight - 16),
+ onEnd: ({element}) => this.#saveWindowState(element)
+ });
+
+ if (typeof ResizeObserver == 'function') {
+ this.windowResizeObserver = new ResizeObserver(() => this.#scheduleWindowStateSave(windowElement));
+ this.windowResizeObserver.observe(windowElement);
+ }
+
+ this.windowViewportResizeHandler = () => this.#scheduleWindowStateSave(windowElement, 0);
+ window.addEventListener('resize', this.windowViewportResizeHandler);
}
/** Creates the color list container.
@@ -348,7 +640,8 @@ export default class WindowFilter extends Overlay {
'class': 'bm-button-trans ' + bgEffectForButtons,
'data-state': isColorHidden ? 'hidden' : 'shown',
'aria-label': isColorHidden ? `Show the color ${color.name || ''} on templates.` : `Hide the color ${color.name || ''} on templates.`,
- 'innerHTML': isColorHidden ? this.eyeClosed.replace(' {
// When the button is clicked
@@ -356,15 +649,15 @@ export default class WindowFilter extends Overlay {
button.style.textDecoration = 'none';
button.disabled = true;
if (button.dataset['state'] == 'shown') {
- button.innerHTML = this.eyeClosed.replace(' {
// When the button is clicked
@@ -405,15 +699,15 @@ export default class WindowFilter extends Overlay {
button.style.textDecoration = 'none';
button.disabled = true;
if (button.dataset['state'] == 'shown') {
- button.innerHTML = this.eyeClosed.replace('Tiles Loaded: ${localizeNumber(this.tilesLoadedTotal)} / ${localizeNumber(this.tilesTotal)}`);
+ this.updateInnerHTML('#bm-filter-tot-correct', `Correct Pixels: ${localizeNumber(this.allPixelsCorrectTotal)}`);
+ this.updateInnerHTML('#bm-filter-tot-total', `Total Pixels: ${localizeNumber(this.allPixelsTotal)}`);
+ this.updateInnerHTML('#bm-filter-tot-remaining', `Remaining: ${localizeNumber((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0))} (${localizePercent(((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0)) / (this.allPixelsTotal || 1))})`);
+ this.updateInnerHTML('#bm-filter-tot-completed', `Completed at: ${this.timeRemainingLocalized} `);
+
// Return early if the color list does not exist.
// We can't update DOM elements that don't exist, so we exit now.
if (!colorList) {return colorStatistics;}
@@ -652,6 +952,8 @@ export default class WindowFilter extends Overlay {
#calculatePixelStatistics() {
// Resets pixel totals to 0
+ this.tilesLoadedTotal = 0;
+ this.tilesTotal = 0;
this.allPixelsTotal = 0;
this.allPixelsCorrectTotal = 0;
this.allPixelsCorrect = new Map();
@@ -705,4 +1007,4 @@ export default class WindowFilter extends Overlay {
this.timeRemaining = new Date(((this.allPixelsTotal - this.allPixelsCorrectTotal) * 30 * 1000) + Date.now());
this.timeRemainingLocalized = localizeDate(this.timeRemaining);
}
-}
\ No newline at end of file
+}
diff --git a/src/WindowMain.css b/src/WindowMain.css
new file mode 100644
index 0000000..5131a46
--- /dev/null
+++ b/src/WindowMain.css
@@ -0,0 +1,173 @@
+/* @since 0.92.2 */
+
+#bm-window-main {
+ width: min(25.5rem, calc(100vw - 0.65rem));
+ max-width: min(25.5rem, calc(100vw - 0.65rem)) !important;
+}
+
+#bm-window-main .bm-window-content {
+ display: flex;
+ flex-direction: column;
+}
+
+#bm-window-main .bm-main-hero,
+#bm-window-main .bm-main-shell {
+ position: relative;
+ overflow: hidden;
+ border-radius: 14px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background: linear-gradient(150deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08));
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.2),
+ 0 10px 24px rgba(0, 0, 0, 0.1);
+}
+
+#bm-window-main .bm-main-hero::before,
+#bm-window-main .bm-main-shell::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ background:
+ radial-gradient(circle at top right, rgba(255, 255, 255, 0.2), transparent 26%),
+ radial-gradient(circle at 20% 0%, rgba(186, 246, 255, 0.16), transparent 24%);
+}
+
+#bm-window-main .bm-main-hero {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.48rem 0.58rem;
+}
+
+#bm-window-main .bm-main-hero h1 {
+ margin: 0;
+}
+
+#bm-window-main .bm-main-stats {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 0.24rem;
+}
+
+#bm-window-main .bm-main-stat-card {
+ min-width: 0;
+ min-height: 2.55rem;
+ display: flex;
+ align-items: flex-start;
+ padding: 0.34rem 0.45rem;
+ border-radius: 11px;
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ background: linear-gradient(160deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.07));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16);
+ color: rgba(18, 35, 63, 0.96);
+ line-height: 1.22;
+}
+
+#bm-window-main .bm-main-stat-card > span,
+#bm-window-main .bm-main-stat-card > time {
+ min-width: 0;
+}
+
+#bm-window-main .bm-main-stat-card-value,
+#bm-window-main .bm-main-stat-card-timer {
+ flex-direction: column;
+ justify-content: center;
+ gap: 0.08rem;
+}
+
+#bm-window-main .bm-main-stat-value {
+ display: inline-block;
+ font-size: 1.3em;
+ color: rgba(15, 31, 56, 0.96);
+ white-space: normal;
+ overflow-wrap: anywhere;
+}
+
+#bm-window-main .bm-main-stat-value b {
+ font-size: 1em;
+}
+
+#bm-window-main .bm-main-stat-label {
+ color: rgba(49, 71, 105, 0.82);
+ font-size: 0.62rem;
+ letter-spacing: 0.08em;
+ font-family: var(--bm-font-display);
+ text-transform: uppercase;
+}
+
+#bm-window-main .bm-main-stat-card time {
+ white-space: nowrap;
+ font-family: var(--bm-font-mono);
+ letter-spacing: 0.06em;
+}
+
+#bm-window-main .bm-main-shell {
+ padding: 0.48rem;
+}
+
+#bm-window-main .bm-main-coords {
+ display: grid;
+ grid-template-columns: auto repeat(4, minmax(0, 1fr));
+ gap: 0.22rem;
+ align-items: center;
+}
+
+#bm-window-main .bm-main-coords .bm-button-pin {
+ width: 1.8rem;
+ height: 1.8rem;
+}
+
+#bm-window-main .bm-main-coords .bm-input-coords {
+ width: 100%;
+ margin-left: 0;
+ text-align: center;
+}
+
+#bm-window-main .bm-main-upload,
+#bm-window-main .bm-main-status {
+ margin-top: 0.24rem;
+}
+
+#bm-window-main .bm-main-upload > div {
+ width: 100%;
+}
+
+#bm-window-main .bm-main-actions {
+ gap: 0.24rem;
+ margin-top: 0.3rem;
+}
+
+#bm-window-main .bm-main-actions > button {
+ flex: 1 1 0;
+}
+
+#bm-window-main .bm-main-status textarea {
+ min-height: 4.1rem;
+}
+
+@media (max-width: 720px) {
+ #bm-window-main {
+ width: min(100vw - 0.45rem, 25.5rem);
+ max-width: min(100vw - 0.45rem, 25.5rem) !important;
+ }
+
+ #bm-window-main .bm-main-stats {
+ grid-template-columns: 1fr;
+ }
+
+ #bm-window-main .bm-main-coords {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ #bm-window-main .bm-main-coords .bm-button-pin {
+ grid-column: 1 / -1;
+ width: 100%;
+ aspect-ratio: auto;
+ height: 1.8rem;
+ }
+
+ #bm-window-main .bm-main-actions {
+ flex-direction: column;
+ }
+}
diff --git a/src/WindowMain.js b/src/WindowMain.js
index 3474085..6e98b4f 100644
--- a/src/WindowMain.js
+++ b/src/WindowMain.js
@@ -1,9 +1,9 @@
import ConfettiManager from "./confetttiManager";
-import Overlay from "./Overlay";
+import Overlay, { minimizeIconExpanded } from "./Overlay";
import { getClipboardData } from "./utils";
-import WindowCredts from "./WindowCredits";
import WindowFilter from "./WindowFilter";
-import WindowWizard from "./WindowWizard";
+
+const settingsIcon = ' ';
/** The overlay builder for the main Blue Marble window.
* @description This class handles the overlay UI for the main window of the Blue Marble userscript.
@@ -45,14 +45,22 @@ export default class WindowMain extends Overlay {
// div.parentElement.appendChild(div); // When the window is clicked on, bring to top
// }
}).addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Blue Marble"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Blue Marble"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {button.click();}; // Needed ONLY to negate weird interaction with dragbar
}).buildElement()
.addDiv().buildElement() // Contains the minimized h1 element
+ .addDiv({'class': 'bm-flex-center'})
+ .addButton({'class': 'bm-button-circle', 'innerHTML': settingsIcon, 'title': 'Settings', 'aria-label': 'Open settings'}, (instance, button) => {
+ button.onclick = () => {
+ instance.settingsManager.buildWindow();
+ }
+ }).buildElement()
+ .buildElement()
.buildElement()
.addDiv({'class': 'bm-window-content'})
- .addDiv({'class': 'bm-container'})
+ .addHr().buildElement()
+ .addDiv({'class': 'bm-container bm-main-hero'})
.addImg({'class': 'bm-favicon', 'src': 'https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png'}, (instance, img) => {
// Adds a birthday hat & confetti to the window if it is Blue Marble's birthday
const date = new Date();
@@ -69,20 +77,25 @@ export default class WindowMain extends Overlay {
.addHeader(1, {'textContent': this.name}).buildElement()
.buildElement()
.addHr().buildElement()
- .addDiv({'class': 'bm-container'})
- .addSpan({'id': 'bm-user-droplets', 'textContent': 'Droplets:'}).buildElement()
- .addBr().buildElement()
- .addSpan({'id': 'bm-user-nextlevel', 'textContent': 'Next level in...'}).buildElement()
- .addBr().buildElement()
- .addSpan({'textContent': 'Charges: '})
- .addTimer(Date.now(), 1000, {'style': 'font-weight: 700;'}, (instance, timer) => {
+ .addDiv({'class': 'bm-container bm-main-stats'})
+ .addDiv({'class': 'bm-main-stat-card bm-main-stat-card-value'})
+ .addSpan({'class': 'bm-main-stat-label', 'textContent': 'Droplets'}).buildElement()
+ .addSpan({'id': 'bm-user-droplets', 'class': 'bm-main-stat-value', 'textContent': '0'}).buildElement()
+ .buildElement()
+ .addDiv({'class': 'bm-main-stat-card bm-main-stat-card-value'})
+ .addSpan({'class': 'bm-main-stat-label', 'textContent': 'Next Level'}).buildElement()
+ .addSpan({'id': 'bm-user-nextlevel', 'class': 'bm-main-stat-value', 'textContent': '0 px'}).buildElement()
+ .buildElement()
+ .addDiv({'class': 'bm-main-stat-card bm-main-stat-card-timer'})
+ .addSpan({'class': 'bm-main-stat-label', 'textContent': 'Charges'}).buildElement()
+ .addTimer(Date.now(), 1000, {'class': 'bm-main-stat-value', 'style': 'font-weight: 700;'}, (instance, timer) => {
instance.apiManager.chargeRefillTimerID = timer.id; // Store the timer ID in apiManager so we can update the timer automatically
}).buildElement()
.buildElement()
.buildElement()
.addHr().buildElement()
- .addDiv({'class': 'bm-container'})
- .addDiv({'class': 'bm-container'})
+ .addDiv({'class': 'bm-container bm-main-shell'})
+ .addDiv({'class': 'bm-container bm-main-coords'})
.addButton({'class': 'bm-button-circle bm-button-pin', 'style': 'margin-top: 0;', 'innerHTML': ' '},
(instance, button) => {
button.onclick = () => {
@@ -111,11 +124,11 @@ export default class WindowMain extends Overlay {
input.addEventListener("paste", event => this.#coordinateInputPaste(instance, input, event));
}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container'})
+ .addDiv({'class': 'bm-container bm-main-upload'})
.addInputFile({'class': 'bm-input-file', 'textContent': 'Upload Template', 'accept': 'image/png, image/jpeg, image/webp, image/bmp, image/gif'}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container bm-flex-between'})
- .addButton({'textContent': 'Disable', 'data-button-status': 'shown'}, (instance, button) => {
+ .addDiv({'class': 'bm-container bm-flex-between bm-main-actions'})
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'Disable', 'data-button-status': 'shown'}, (instance, button) => {
button.onclick = () => {
button.disabled = true; // Disables the button until the transition ends
if (button.dataset['buttonStatus'] == 'shown') { // If templates are currently being 'shown' then hide them
@@ -132,7 +145,7 @@ export default class WindowMain extends Overlay {
button.disabled = false; // Enables the button
}
}).buildElement()
- .addButton({'textContent': 'Create'}, (instance, button) => {
+ .addButton({'class': 'bm-button-primary', 'textContent': 'Create'}, (instance, button) => {
button.onclick = () => {
const input = document.querySelector(`#${this.windowID} .bm-input-file`);
@@ -153,52 +166,13 @@ export default class WindowMain extends Overlay {
instance.handleDisplayStatus(`Drew to canvas!`);
}
}).buildElement()
- .addButton({'textContent': 'Filter'}, (instance, button) => {
- button.onclick = () => this.#buildWindowFilter();
+ .addButton({'class': 'bm-button-secondary', 'textContent': 'Filter'}, (instance, button) => {
+ button.onclick = () => this.buildWindowFilter();
}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container'})
+ .addDiv({'class': 'bm-container bm-main-status'})
.addTextarea({'id': this.outputStatusId, 'placeholder': `Status: Sleeping...\nVersion: ${this.version}`, 'readOnly': true}).buildElement()
.buildElement()
- .addDiv({'class': 'bm-container bm-flex-between', 'style': 'margin-bottom: 0; flex-direction: column;'})
- .addDiv({'class': 'bm-flex-between'})
- // .addButton({'class': 'bm-button-circle', 'innerHTML': '🖌'}).buildElement()
- .addButton({'class': 'bm-button-circle', 'innerHTML': '⚙️', 'title': 'Settings'}, (instance, button) => {
- button.onclick = () => {
- instance.settingsManager.buildWindow();
- }
- }).buildElement()
- .addButton({'class': 'bm-button-circle', 'innerHTML': '🧙', 'title': 'Template Wizard'}, (instance, button) => {
- button.onclick = () => {
- const templateManager = instance.apiManager?.templateManager;
- const wizard = new WindowWizard(this.name, this.version, templateManager?.schemaVersion, templateManager);
- wizard.buildWindow();
- }
- }).buildElement()
- .addButton({'class': 'bm-button-circle', 'innerHTML': '🎨', 'title': 'Template Color Converter'}, (instance, button) => {
- button.onclick = () => {
- window.open('https://pepoafonso.github.io/color_converter_wplace/', '_blank', 'noopener noreferrer');
- }
- }).buildElement()
- .addButton({'class': 'bm-button-circle', 'innerHTML': '🌐', 'title': 'Official Blue Marble Website'}, (instance, button) => {
- button.onclick = () => {
- window.open('https://bluemarble.lol/', '_blank', 'noopener noreferrer');
- }
- }).buildElement()
- .addButton({'class': 'bm-button-circle', 'title': 'Donate to SwingTheVine', 'innerHTML': ' '}, (instance, button) => {
- button.onclick = () => {
- window.open('https://ko-fi.com/swingthevine', '_blank', 'noopener noreferrer');
- }
- }).buildElement()
- .addButton({'class': 'bm-button-circle', 'innerHTML': '🤝', 'title': 'Credits'}, (instance, button) => {
- button.onclick = () => {
- const credits = new WindowCredts(this.name, this.version);
- credits.buildWindow();
- }
- }).buildElement()
- .buildElement()
- .addSmall({'textContent': 'Made by SwingTheVine', 'style': 'margin-top: auto;'}).buildElement()
- .buildElement()
.buildElement()
.buildElement()
.buildElement().buildOverlay(this.windowParent);
@@ -212,9 +186,9 @@ export default class WindowMain extends Overlay {
* This might cause a memory leak. I pray that this is not the case...
* @since 0.88.330
*/
- #buildWindowFilter() {
+ buildWindowFilter() {
const windowFilter = new WindowFilter(this); // Creates a new color filter window instance
- windowFilter.buildWindow();
+ windowFilter.buildPreferredWindow();
}
/** Handles pasting into the coordinate input boxes in the main Blue Marble window.
@@ -254,4 +228,4 @@ export default class WindowMain extends Overlay {
instance.updateInnerHTML('bm-input-py', coords?.[3] || '');
}
}
-}
\ No newline at end of file
+}
diff --git a/src/WindowSettings.js b/src/WindowSettings.js
index a478e7b..3433892 100644
--- a/src/WindowSettings.js
+++ b/src/WindowSettings.js
@@ -1,4 +1,6 @@
-import Overlay from "./Overlay";
+import Overlay, { minimizeIconExpanded } from "./Overlay";
+
+const closeIcon = ' ';
/** The overlay builder for the settings window in Blue Marble.
* The logic for this window is managed in {@link SettingsManager}
@@ -37,13 +39,13 @@ export default class WindowSettings extends Overlay {
this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window'})
.addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Color Filter"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Settings"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {button.click()}; // Needed only to negate weird interaction with dragbar
}).buildElement()
.addDiv().buildElement() // Contains the minimized h1 element
.addDiv({'class': 'bm-flex-center'})
- .addButton({'class': 'bm-button-circle', 'textContent': '✖', 'aria-label': 'Close window "Color Filter"'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': closeIcon, 'aria-label': 'Close window "Settings"'}, (instance, button) => {
button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
}).buildElement()
@@ -94,4 +96,4 @@ export default class WindowSettings extends Overlay {
buildTemplate() {
this.#errorOverrideFailure('Template');
}
-}
\ No newline at end of file
+}
diff --git a/src/WindowWizard.js b/src/WindowWizard.js
index 92b19d2..a45d787 100644
--- a/src/WindowWizard.js
+++ b/src/WindowWizard.js
@@ -1,4 +1,4 @@
-import Overlay from "./Overlay";
+import Overlay, { minimizeIconExpanded } from "./Overlay";
import Template from "./Template";
import TemplateManager from "./templateManager";
import { encodedToNumber, escapeHTML, getWplaceVersion, localizeDate, localizeNumber, sleep } from "./utils";
@@ -63,7 +63,7 @@ export default class WindowWizard extends Overlay {
// div.parentElement.appendChild(div); // When the window is clicked on, bring to top
// }
}).addDragbar()
- .addButton({'class': 'bm-button-circle', 'textContent': '▼', 'aria-label': 'Minimize window "Template Wizard"', 'data-button-status': 'expanded'}, (instance, button) => {
+ .addButton({'class': 'bm-button-circle', 'innerHTML': minimizeIconExpanded, 'aria-label': 'Minimize window "Template Wizard"', 'data-button-status': 'expanded'}, (instance, button) => {
button.onclick = () => instance.handleMinimization(button);
button.ontouchend = () => {button.click()}; // Needed only to negate weird interaction with dragbar
}).buildElement()
diff --git a/src/apiManager.js b/src/apiManager.js
index a45bda2..522cb0e 100644
--- a/src/apiManager.js
+++ b/src/apiManager.js
@@ -54,44 +54,7 @@ export default class ApiManager {
switch (endpointText) {
case 'me': // Request to retrieve user data
-
- // If the game can not retrieve the userdata...
- if (dataJSON['status'] && 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'],
- '!#$%&\'()*+,-./0123456789:;<=>?@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: ${localizeNumber(dataJSON['droplets'])} `); // Updates the text content of the droplets field
- overlay.updateInnerHTML('bm-user-nextlevel', `Next level in ${localizeNumber(nextLevelPixels)} pixel${nextLevelPixels == 1 ? '' : 's'}`); // Updates the text content of the next level field
+ this.applyUserDataToOverlay(overlay, dataJSON);
break;
case 'pixel': // Request to retrieve pixel data
@@ -195,6 +158,90 @@ export default class ApiManager {
});
}
+ /** Applies user data from the /me endpoint to the current overlay.
+ * @param {Overlay} overlay
+ * @param {Object.} dataJSON
+ * @since 0.92.1
+ */
+ applyUserDataToOverlay(overlay, dataJSON) {
+
+ // If the game can not retrieve the userdata...
+ if (dataJSON['status'] && dataJSON['status']?.toString()[0] != '2') {
+ overlay.handleDisplayError(`You are not logged in or Wplace is offline!\nCould not fetch userdata.`);
+ return;
+ }
+
+ const nextLevelPixels = Math.ceil(Math.pow(Math.floor(dataJSON['level']) * Math.pow(30, 0.65), (1 / 0.65)) - dataJSON['pixelsPainted']);
+
+ console.log(dataJSON['id']);
+ if (!!dataJSON['id'] || dataJSON['id'] === 0) {
+ console.log(numberToEncoded(
+ dataJSON['id'],
+ '!#$%&\'()*+,-./0123456789:;<=>?@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']);
+ }
+ }
+
+ overlay.updateInnerHTML('bm-user-droplets', `${localizeNumber(dataJSON['droplets'])} `);
+ overlay.updateInnerHTML('bm-user-nextlevel', `${localizeNumber(nextLevelPixels)} px`);
+ }
+
+ /** Requests the current /me payload directly so the overlay has initial user data
+ * even if the first network response was missed during startup.
+ * @param {Overlay} overlay
+ * @since 0.92.1
+ */
+ async requestCurrentUserData(overlay) {
+ try {
+ const response = await fetch(`${window.location.origin}/api/me`, {
+ credentials: 'include'
+ });
+
+ if (!response.ok) {
+ overlay.handleDisplayError(`Could not fetch userdata.\nHTTP ${response.status}`);
+ return;
+ }
+
+ const dataJSON = await response.json();
+ this.applyUserDataToOverlay(overlay, dataJSON);
+ } catch (error) {
+ consoleError('Failed to fetch current user data:', error);
+ }
+ }
+
+ /** Applies cached /me data from sessionStorage if it was captured during early startup.
+ * @param {Overlay} overlay
+ * @returns {boolean}
+ * @since 0.92.1
+ */
+ applyCachedUserData(overlay) {
+ try {
+ const cached = sessionStorage.getItem('bm-last-me');
+ if (!cached) {return false;}
+
+ const dataJSON = JSON.parse(cached);
+ this.applyUserDataToOverlay(overlay, dataJSON);
+ return true;
+ } catch (error) {
+ consoleError('Failed to apply cached user data:', error);
+ return false;
+ }
+ }
+
// Sends a heartbeat to the telemetry server
async sendHeartbeat(version) {
diff --git a/src/main.css b/src/main.css
index bfa8ce9..1b19702 100644
--- a/src/main.css
+++ b/src/main.css
@@ -6,5 +6,6 @@
@import './confettiManager.css';
@import './overlay.css';
@import './WindowFilter.css';
+@import './WindowMain.css';
@import './WindowSettings.css';
@import './WindowWizard.css';
diff --git a/src/main.js b/src/main.js
index 322c1b7..a7ab136 100644
--- a/src/main.js
+++ b/src/main.js
@@ -92,6 +92,18 @@ inject(() => {
// Sends a message about the endpoint it spied on
cloned.json()
.then(jsonData => {
+ const endpointText = endpointName?.split('?')[0].split('/').filter(s => s && isNaN(Number(s))).filter(s => s && !s.includes('.')).pop();
+
+ // Cache the latest /me payload so the userscript can hydrate its UI
+ // even if the first response arrives before listeners are attached.
+ if (endpointText == 'me') {
+ try {
+ sessionStorage.setItem('bm-last-me', JSON.stringify(jsonData));
+ } catch (error) {
+ console.warn(`%c${name}%c: Failed to cache "/me" payload`, consoleStyle, '', error);
+ }
+ }
+
window.postMessage({
source: 'blue-marble',
endpoint: endpointName,
@@ -163,9 +175,23 @@ inject(() => {
const cssOverlay = GM_getResourceText("CSS-BM-File");
GM_addStyle(cssOverlay);
+function appendFontStylesheet(href) {
+ const stylesheetLink = document.createElement('link');
+ stylesheetLink.href = href;
+ stylesheetLink.rel = 'preload';
+ stylesheetLink.as = 'style';
+ stylesheetLink.onload = function () {
+ this.onload = null;
+ this.rel = 'stylesheet';
+ };
+ document.head?.appendChild(stylesheetLink);
+}
+
// Injection point for the Roboto Mono font file (only if this is the Standalone version)
const robotoMonoInjectionPoint = 'robotoMonoInjectionPoint';
+appendFontStylesheet('https://fonts.googleapis.com/css2?family=Michroma&family=Rajdhani:wght@400;500;600;700&display=swap');
+
// If the Roboto Mono injection point contains '@font-face'...
if (!!(robotoMonoInjectionPoint.indexOf('@font-face') + 1)) {
// A very hacky way of doing truthy/falsy logic
@@ -176,15 +202,7 @@ if (!!(robotoMonoInjectionPoint.indexOf('@font-face') + 1)) {
// Else, no Roboto Mono was found. We need to use a stylesheet.
// Imports the Roboto Mono font family as a stylesheet
- var stylesheetLink = document.createElement('link');
- stylesheetLink.href = 'https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap';
- stylesheetLink.rel = 'preload';
- stylesheetLink.as = 'style';
- stylesheetLink.onload = function () {
- this.onload = null;
- this.rel = 'stylesheet';
- };
- document.head?.appendChild(stylesheetLink);
+ appendFontStylesheet('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
}
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}')); // Loads the user settings
@@ -204,7 +222,6 @@ templateManager.setSettingsManager(settingsManager); // Sets the settings manage
const storageTemplates = JSON.parse(GM_getValue('bmTemplates', '{}'));
console.log(storageTemplates);
-templateManager.importJSON(storageTemplates); // Loads the templates
console.log(userSettings);
@@ -236,13 +253,23 @@ if ((previousTelemetryVersion == undefined) || (previousTelemetryVersion > curre
windowTelemetry.buildWindow(); // Asks the user if they want to enable telemetry
}
-windowMain.buildWindow(); // Builds the main Blue Marble window
+void initializeBlueMarble();
-apiManager.spontaneousResponseListener(windowMain); // Reads spontaneous fetch responces
+async function initializeBlueMarble() {
+ await templateManager.importJSON(storageTemplates); // Loads the templates
-observeBlack(); // Observes the black palette color
+ apiManager.spontaneousResponseListener(windowMain); // Reads spontaneous fetch responces
-consoleLog(`%c${name}%c (${version}) userscript has loaded!`, 'color: cornflowerblue;', '');
+ windowMain.buildWindow(); // Builds the main Blue Marble window
+ windowMain.buildWindowFilter(); // Opens the Color Filter window automatically on page load
+
+ apiManager.applyCachedUserData(windowMain); // Hydrates the UI from the earliest cached /me response if it exists
+ void apiManager.requestCurrentUserData(windowMain); // Ensures the main window gets current /me data even if startup missed it
+
+ observeBlack(); // Observes the black palette color
+
+ consoleLog(`%c${name}%c (${version}) userscript has loaded!`, 'color: cornflowerblue;', '');
+}
/** Observe the black color, and add the "Move" button.
* @since 0.66.3
diff --git a/src/overlay.css b/src/overlay.css
index 69320ab..f53b88a 100644
--- a/src/overlay.css
+++ b/src/overlay.css
@@ -15,32 +15,81 @@
/* The Blue Marble windows */
.bm-window {
+ --bm-surface-strong: rgba(9, 20, 42, 0.5);
+ --bm-surface-soft: rgba(24, 41, 74, 0.28);
+ --bm-surface-glass: rgba(255, 255, 255, 0.1);
+ --bm-surface-glass-strong: rgba(255, 255, 255, 0.18);
+ --bm-border-soft: rgba(255, 255, 255, 0.18);
+ --bm-border-strong: rgba(163, 228, 255, 0.34);
+ --bm-text-primary: rgba(17, 36, 66, 0.96);
+ --bm-text-secondary: rgba(36, 57, 90, 0.84);
+ --bm-accent-start: #baf6ff;
+ --bm-accent-end: #81b6ff;
+ --bm-accent-shadow: rgba(132, 182, 255, 0.22);
+ --bm-font-body: "Rajdhani", "Segoe UI Variable Text", "Segoe UI", sans-serif;
+ --bm-font-display: "Michroma", "Orbitron", "Segoe UI", sans-serif;
+ --bm-font-mono: "Roboto Mono", "Rajdhani", "Courier New", monospace;
position: fixed;
- background-color: rgba(21, 48, 99, 0.9);
- color: white;
- padding: 10px;
- border-radius: 8px;
+ isolation: isolate;
+ overflow: hidden;
+ background:
+ radial-gradient(circle at 14% 12%, rgba(255, 255, 255, 0.24), transparent 18%),
+ radial-gradient(circle at 86% 8%, rgba(186, 246, 255, 0.22), transparent 24%),
+ radial-gradient(circle at 82% 84%, rgba(129, 182, 255, 0.18), transparent 28%),
+ linear-gradient(145deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.06) 22%, rgba(105, 145, 212, 0.08) 54%, rgba(9, 20, 42, 0.18));
+ color: var(--bm-text-primary);
+ padding: 6px;
+ border-radius: 16px;
+ border: 1px solid var(--bm-border-soft);
+ box-shadow:
+ 0 18px 40px rgba(0, 0, 0, 0.2),
+ inset 0 1px 0 rgba(255, 255, 255, 0.22),
+ inset 0 -1px 0 rgba(255, 255, 255, 0.05);
z-index: 9000;
- transition: all 0.3s ease, transform 0s;
+ transition:
+ background 320ms ease,
+ border-color 220ms ease,
+ box-shadow 220ms ease,
+ opacity 220ms ease,
+ transform 0s,
+ width 220ms ease,
+ max-width 220ms ease,
+ max-height 220ms ease;
top: 75px;
left: 60px;
width: auto;
max-height: fit-content;
max-width: calc(100% - 135px);
- /* Font stack is as follows:
- * Highest Priority (Roboto Mono)
- * Windows fallback (Courier New)
- * macOS fallback (Monaco)
- * Linux fallback (DejaVu Sans Mono)
- * Any possible monospace font (monospace)
- * Last resort (Arial) */
- font-family: 'Roboto Mono', 'Courier New', 'Monaco', 'DejaVu Sans Mono', monospace, 'Arial';
- letter-spacing: 0.05em;
+ backdrop-filter: blur(26px) saturate(1.25);
+ font-family: var(--bm-font-body);
+ letter-spacing: 0.04em;
}
-/* The Blue Marble windowed windows */
-.bm-window.bm-windowed {
- max-width: 300px;
+.bm-window::before,
+.bm-window::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+
+.bm-window::before {
+ border-radius: inherit;
+ padding: 1px;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.45), rgba(255, 255, 255, 0.12) 24%, rgba(186, 246, 255, 0.22) 58%, rgba(129, 182, 255, 0.3));
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ opacity: 0.85;
+}
+
+.bm-window::after {
+ border-radius: inherit;
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.2), transparent 24%),
+ radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.22), transparent 22%),
+ radial-gradient(circle at 88% 16%, rgba(186, 246, 255, 0.18), transparent 18%);
+ opacity: 1;
}
/* The drag bar */
@@ -48,12 +97,19 @@
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
- gap: 0.5ch;
- /* For background circles, width & height should be odd, cx & cy should be half of width & height, and r should be less than or equal to cx & cy */
- background: url('data:image/svg+xml;utf8, ') repeat;
+ gap: 0.28ch;
+ padding: 0.18rem 0.24rem;
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ background:
+ radial-gradient(circle at 0 0, rgba(255, 255, 255, 0.22) 0, transparent 42%),
+ linear-gradient(135deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08));
cursor: grab;
width: 100%;
height: fit-content;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.16),
+ 0 6px 18px rgba(0, 0, 0, 0.1);
}
/* When a window is being dragged */
@@ -78,37 +134,42 @@
/* The Blue Marble Favicon */
.bm-favicon {
display: inline-block;
- height: 2.5em;
- margin-right: 1ch;
+ height: 2.2em;
+ margin-right: 0.45ch;
+ padding: 0.2rem;
+ border-radius: 12px;
vertical-align: middle;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.08));
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.16),
+ 0 8px 18px rgba(0, 0, 0, 0.12);
}
/* Header 1 */
.bm-window h1 {
display: inline-block;
- font-size: x-large;
- font-weight: bold;
+ font-size: 1rem;
+ font-weight: 700;
vertical-align: middle;
+ font-family: var(--bm-font-display);
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ color: rgba(16, 33, 60, 0.96);
}
/* Header 1 when inside dragbar */
/* Or, when the custom class is used */
.bm-dragbar h1,
.bm-dragbar-text {
- font-size: 1.2em;
+ font-size: 0.78rem;
user-select: none;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
- text-shadow:
- 3px 0px rgba(21, 48, 99, 0.5),
- -3px 0px rgba(21, 48, 99, 0.5),
- 0px 3px rgba(21, 48, 99, 0.5),
- 0px -3px rgba(21, 48, 99, 0.5),
- 3px 3px rgba(21, 48, 99, 0.5),
- -3px 3px rgba(21, 48, 99, 0.5),
- 3px -3px rgba(21, 48, 99, 0.5),
- -3px -3px rgba(21, 48, 99, 0.5);
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.14em;
+ color: rgba(18, 37, 66, 0.95);
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.28);
}
/* Container for Header 1 when inside dragbar */
@@ -119,9 +180,12 @@
/* Header 2 */
.bm-window h2 {
display: inline-block;
- font-size: larger;
+ font-size: 0.88rem;
font-weight: 700;
vertical-align: middle;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.1em;
+ color: rgba(18, 35, 64, 0.96);
}
/* Header 3 */
@@ -129,6 +193,24 @@
display: inline-block;
font-size: large;
font-weight: 700;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.08em;
+ color: rgba(18, 35, 64, 0.96);
+}
+
+/* Paragraphs */
+.bm-window p {
+ color: var(--bm-text-secondary);
+ line-height: 1.5;
+ letter-spacing: 0.035em;
+}
+
+/* Horizontal dividers */
+.bm-window hr {
+ border: none;
+ height: 1px;
+ margin: 0.32rem 0;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.28), transparent);
}
/* Container with a vertically centered header 1-6 */
@@ -140,42 +222,162 @@
/* Containers for "sections" of elements */
.bm-container {
- margin: 0.5em 0;
+ margin: 0.24em 0;
+}
+
+/* Shared form controls */
+.bm-window input,
+.bm-window select,
+.bm-window textarea,
+.bm-window button {
+ font: inherit;
}
/* All window buttons */
.bm-window button {
- background-color: #144eb9;
- border-radius: 1em;
- padding: 0 0.75ch;
+ appearance: none;
+ color: var(--bm-text-primary);
+ font-family: var(--bm-font-display);
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(164, 208, 255, 0.14));
+ border: 1px solid rgba(255, 255, 255, 0.22);
+ border-radius: 999px;
+ padding: 0.28em 0.62em;
+ min-height: 1.78em;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.22),
+ 0 8px 20px rgba(0, 0, 0, 0.1);
+ transition:
+ background 180ms ease,
+ border-color 180ms ease,
+ box-shadow 180ms ease,
+ filter 180ms ease,
+ opacity 180ms ease,
+ transform 180ms ease;
}
/* All window buttons when hovered/focused */
-.bm-window button:hover, .bm-window button:focus-visible {
- background-color: #1061e5;
+.bm-window button:hover,
+.bm-window button:focus-visible {
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.28), rgba(186, 246, 255, 0.18));
+ border-color: rgba(230, 245, 255, 0.36);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.26),
+ 0 10px 22px rgba(0, 0, 0, 0.12);
+ transform: translateY(-1px);
}
-/* All window buttons when pressed (plus disabled color) */
-.bm-window button:active,
-.bm-window button:disabled {
- background-color: #2e97ff;
+/* Focus ring */
+.bm-window button:focus-visible,
+.bm-window input:focus-visible,
+.bm-window select:focus-visible,
+.bm-window textarea:focus-visible {
+ outline: none;
+ border-color: rgba(116, 231, 255, 0.62);
+ box-shadow:
+ 0 0 0 3px rgba(116, 231, 255, 0.15),
+ inset 0 1px 0 rgba(255, 255, 255, 0.12);
+}
+
+/* All window buttons when pressed */
+.bm-window button:active {
+ transform: translateY(0) scale(0.98);
+ filter: brightness(0.98);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.12),
+ 0 8px 16px rgba(18, 36, 78, 0.24);
}
/* All window buttons when disabled */
-.bm-window button:disabled, .bm-window button:disabled {
- text-decoration: line-through;
+.bm-window button:disabled {
+ opacity: 0.56;
cursor: not-allowed;
+ text-decoration: none;
+ transform: none;
+ filter: saturate(0.72);
+ box-shadow: none;
+}
+
+/* Button variants */
+.bm-window button.bm-button-primary {
+ color: #07203b;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.42), rgba(186, 246, 255, 0.34));
+ border-color: rgba(239, 248, 255, 0.42);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.44),
+ 0 10px 22px rgba(129, 182, 255, 0.14);
+}
+
+.bm-window button.bm-button-primary:hover,
+.bm-window button.bm-button-primary:focus-visible {
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.52), rgba(204, 250, 255, 0.36));
+}
+
+.bm-window button.bm-button-secondary {
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.08));
+ border-color: rgba(255, 255, 255, 0.18);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.14),
+ 0 8px 20px rgba(0, 0, 0, 0.08);
+}
+
+.bm-window button.bm-button-secondary:hover,
+.bm-window button.bm-button-secondary:focus-visible {
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.24), rgba(186, 246, 255, 0.12));
}
/* Icon buttons (single character text content buttons) */
.bm-button-circle {
- border: white 1px solid;
- height: 1.5em;
- width: 1.5em;
- margin-top: 2px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid rgba(255, 255, 255, 0.16);
+ inline-size: 1.62rem;
+ block-size: 1.62rem;
+ min-height: 1.62rem !important;
+ min-width: 1.62rem;
+ margin-top: 0;
text-align: center;
- line-height: 1em;
- padding: 0 !important; /* Overrides the padding in ".bm-window button" */
+ line-height: 1;
+ padding: 0 !important;
+ aspect-ratio: 1 / 1;
+ border-radius: 50% !important;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0.1)) !important;
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.18),
+ 0 8px 18px rgba(0, 0, 0, 0.1) !important;
+ overflow: hidden;
+ flex: 0 0 auto;
+ font-size: 0.74rem;
+}
+
+.bm-button-circle svg {
+ display: block;
+ width: 70%;
+ height: 70%;
+ flex: 0 0 auto;
+ margin: auto;
+ color: currentColor;
+ pointer-events: none;
+ transform: translateZ(0);
+ overflow: visible;
+}
+
+.bm-button-circle .bm-button-icon-settings {
+ width: 68%;
+ height: 68%;
+ transform: translateY(0.5px) translateZ(0);
+}
+
+.bm-button-circle .bm-button-icon-minimize {
+ width: 62%;
+ height: 62%;
+ transform: translateY(0.75px) translateZ(0);
+}
+
+#bm-window-filter.bm-windowed .bm-button-circle .bm-button-icon-minimize {
+ transform: translateZ(0);
}
/* Pin button */
@@ -192,39 +394,52 @@
/* Transparent buttons */
.bm-window button.bm-button-trans {
- background-color: unset;
+ background: transparent !important;
+ box-shadow: none !important;
+ border-color: transparent !important;
}
/* Transparent buttons on dark backgrounds when hovered */
.bm-button-trans.bm-button-hover-white:hover,
.bm-button-trans.bm-button-hover-white:focus {
- background-color: rgba(255, 255, 255, 0.17);
+ background-color: rgba(255, 255, 255, 0.18) !important;
}
/* Transparent buttons on dark backgrounds when pressed */
.bm-button-trans.bm-button-hover-white:active {
- background-color: rgba(255, 255, 255, 0.22);
+ background-color: rgba(255, 255, 255, 0.24) !important;
}
/* Transparent buttons on light backgrounds when hovered */
.bm-button-trans.bm-button-hover-black:hover,
.bm-button-trans.bm-button-hover-black:focus {
- background-color: rgba(0, 0, 0, 0.17);
+ background-color: rgba(0, 0, 0, 0.14) !important;
}
/* Transparent buttons on light backgrounds when pressed */
.bm-button-trans.bm-button-hover-black:active {
- background-color: rgba(0, 0, 0, 0.22);
+ background-color: rgba(0, 0, 0, 0.2) !important;
+}
+
+/* Shared inputs */
+.bm-window input[type="number"],
+.bm-window select,
+.bm-window textarea {
+ color: var(--bm-text-primary);
+ font-family: var(--bm-font-body);
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.06));
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ border-radius: 12px;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* Tile (x, y) & Pixel (x, y) input fields */
input[type="number"].bm-input-coords {
appearance: auto;
-moz-appearance: textfield;
- width: 5.5ch;
+ width: 5.9ch;
margin-left: 1ch;
- background-color: rgba(0, 0, 0, 0.2);
- padding: 0 0.5ch;
+ padding: 0.2em 0.35ch;
font-size: small;
}
@@ -260,10 +475,7 @@ input[type="file"] {
/* Dropdown selection & dropdown (::picker) menus */
.bm-window select {
- color: white;
- background-color: #144eb9;
- border-radius: 1em;
- padding: 0 0.5ch;
+ padding: 0.22em 0.45ch;
}
/* Checkbox container (the label element) */
@@ -271,37 +483,51 @@ input[type="file"] {
display: flex;
width: fit-content;
gap: 1ch;
+ align-items: center;
+ color: var(--bm-text-secondary);
}
/* Checkbox */
.bm-window input[type="checkbox"] {
width: 1em;
+ accent-color: #74e7ff;
}
/* Window content container */
.bm-window-content {
overflow: hidden;
+ max-height: calc(100% - 5px);
transition: height 300ms cubic-bezier(.4, 0, .2, 1);
}
/* Text areas */
.bm-window textarea {
font-size: small;
- background-color: rgba(0, 0, 0, 0.2);
- padding: 0 0.5ch;
- height: 5.25em;
+ padding: 0.38em 0.52em;
+ height: 4em;
width: 100%;
+ resize: vertical;
+ line-height: 1.45;
+}
+
+.bm-window textarea::placeholder,
+.bm-window input::placeholder {
+ color: rgba(47, 68, 102, 0.6);
}
/* Anchor/Links with no children */
.bm-window a:not(:has(*)) {
+ color: rgba(21, 88, 164, 0.94);
text-decoration: underline;
+ text-decoration-color: rgba(21, 88, 164, 0.35);
}
/* Small elements */
.bm-window small {
font-size: x-small;
- color: lightgray;
+ font-family: var(--bm-font-display);
+ letter-spacing: 0.12em;
+ color: var(--bm-text-secondary);
}
/* List items of unordered lists */
@@ -312,8 +538,27 @@ input[type="file"] {
/* Scrollable container */
.bm-window .bm-container.bm-scrollable {
- max-height: calc(80vh - 150px);
+ max-height: var(--bm-scrollable-max-height, calc(80vh - 150px));
overflow: auto;
+ scrollbar-width: thin;
+ scrollbar-color: rgba(146, 221, 255, 0.42) rgba(255, 255, 255, 0.05);
+}
+
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar-thumb {
+ background: linear-gradient(180deg, rgba(116, 231, 255, 0.48), rgba(83, 141, 255, 0.4));
+ border-radius: 999px;
+ border: 2px solid transparent;
+ background-clip: padding-box;
+}
+
+.bm-window .bm-container.bm-scrollable::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.04);
+ border-radius: 999px;
}
/* Flex children space between */
@@ -322,7 +567,7 @@ input[type="file"] {
align-content: center;
justify-content: space-between;
align-items: center;
- gap: 0.5ch;
+ gap: 0.35ch;
}
/* Flex children space center */
@@ -331,7 +576,7 @@ input[type="file"] {
align-content: center;
justify-content: center;
align-items: center;
- gap: 0.5ch;
+ gap: 0.35ch;
}
/* ASCII Art */
@@ -348,9 +593,8 @@ input[type="file"] {
/* Containers for "sections" of elements in windowed mode */
/* Does not apply to the main window */
.bm-windowed .bm-container:not(#bm-window-main .bm-container) {
- margin-top: 0.25em;
- margin-bottom: 0.25em;
- /* Do not use 'margin' shorthand, as it will override left/right margin */
+ margin-top: 0.18em;
+ margin-bottom: 0.18em;
}
/* Header 1 in windowed mode */
diff --git a/src/settingsManager.js b/src/settingsManager.js
index 849e999..21cb975 100644
--- a/src/settingsManager.js
+++ b/src/settingsManager.js
@@ -16,7 +16,8 @@ import WindowSettings from "./WindowSettings";
* "telemetry": 1,
* "flags": ["hl-noTrans", "ftr-oWin", "te-noSkip"],
* "highlight": [[1,0,-1],[1,-1,0],[2,1,0],[1,0,1]],
- * "filter": [-2,0,4,5,6,29,63]
+ * "filter": [-2,0,4,5,6,29,63],
+ * "windowFilter": {"x": 60, "y": 75, "width": 300, "height": 420}
* }
*/
export default class SettingsManager extends WindowSettings {
@@ -45,13 +46,21 @@ export default class SettingsManager extends WindowSettings {
* @since 0.91.39
*/
async updateUserStorage() {
+ await this.saveUserStorage();
+ }
+
+ /** Saves the user settings in userscript storage.
+ * @param {boolean} [force=false] - Should the throttle be ignored?
+ * @since 0.92.0
+ */
+ async saveUserStorage(force = false) {
// Turns the objects into a string
const userSettingsCurrent = JSON.stringify(this.userSettings);
const userSettingsOld = JSON.stringify(this.userSettingsOld);
// If the user settings have changed, AND the last update to user storage was over 5 seconds ago (5sec throttle)...
- if ((userSettingsCurrent != userSettingsOld) && ((Date.now() - this.lastUpdateTime) > this.updateFrequency)) {
+ if ((userSettingsCurrent != userSettingsOld) && (force || ((Date.now() - this.lastUpdateTime) > this.updateFrequency))) {
await GM.setValue(this.userSettingsSaveLocation, userSettingsCurrent); // Updates user storage
this.userSettingsOld = structuredClone(this.userSettings); // Updates the old user settings with a duplicate of the current user settings
this.lastUpdateTime = Date.now(); // Updates the variable that contains the last time updated
@@ -59,6 +68,13 @@ export default class SettingsManager extends WindowSettings {
}
}
+ /** Immediately saves the user settings in userscript storage.
+ * @since 0.92.0
+ */
+ async saveUserStorageNow() {
+ await this.saveUserStorage(true);
+ }
+
/** Toggles a boolean flag to the state that was passed in.
* If no state was passed in, the flag will flip to the opposite state.
* The existence of the flag determines its state. If it exists, it is `true`.
@@ -328,4 +344,4 @@ export default class SettingsManager extends WindowSettings {
.buildElement()
.buildElement()
}
-}
\ No newline at end of file
+}
diff --git a/src/templateManager.js b/src/templateManager.js
index 1a7ce13..858cebc 100644
--- a/src/templateManager.js
+++ b/src/templateManager.js
@@ -130,6 +130,55 @@ export default class TemplateManager {
*/
setSettingsManager(settingsManager) {
this.settingsManager = settingsManager;
+ this.#restoreFilteredColorsFromSettings();
+ }
+
+ /** Restores hidden colors from persisted user settings.
+ * @since 0.92.1
+ */
+ #restoreFilteredColorsFromSettings() {
+ const storedFilter = this.settingsManager?.userSettings?.filter;
+ const filteredColors = Array.isArray(storedFilter) ? storedFilter : [];
+
+ this.shouldFilterColor.clear();
+
+ for (const colorID of filteredColors) {
+ const parsedColorID = Number(colorID);
+ if (!Number.isFinite(parsedColorID)) {continue;}
+ this.shouldFilterColor.set(parsedColorID, true);
+ }
+ }
+
+ /** Persists hidden colors to user settings storage.
+ * @since 0.92.1
+ */
+ #persistFilteredColors() {
+ if (!this.settingsManager) {return;}
+
+ this.settingsManager.userSettings.filter = Array.from(this.shouldFilterColor.keys())
+ .map(colorID => Number(colorID))
+ .filter(colorID => Number.isFinite(colorID))
+ .sort((a, b) => a - b);
+
+ void this.settingsManager.saveUserStorageNow();
+ }
+
+ /** Updates whether a palette color should be hidden on the canvas.
+ * @param {number} colorID
+ * @param {boolean} shouldHide
+ * @since 0.92.1
+ */
+ setColorFiltered(colorID, shouldHide) {
+ const parsedColorID = Number(colorID);
+ if (!Number.isFinite(parsedColorID)) {return;}
+
+ if (shouldHide) {
+ this.shouldFilterColor.set(parsedColorID, true);
+ } else {
+ this.shouldFilterColor.delete(parsedColorID);
+ }
+
+ this.#persistFilteredColors();
}
/** Creates the JSON object to store templates in
@@ -648,14 +697,14 @@ export default class TemplateManager {
/** Imports the JSON object, and appends it to any JSON object already loaded
* @param {string} json - The JSON string to parse
*/
- importJSON(json) {
+ async importJSON(json) {
console.log(`Importing JSON...`);
console.log(json);
// If the passed in JSON is a Blue Marble template object...
if (json?.whoami == 'BlueMarble') {
- this.#parseBlueMarble(json); // ...parse the template object as Blue Marble
+ await this.#parseBlueMarble(json); // ...parse the template object as Blue Marble
}
}