From 5a452ef4d0ad4b07d600f08568c621da2de0d192 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Mar 2026 01:31:43 +0000 Subject: [PATCH] Update wiki via JSDoc --- docs/ApiManager.html | 178 + docs/ConfettiManager.html | 177 + docs/Observers.html | 178 + docs/Overlay.html | 202 + docs/Overlay.js.html | 1496 ++ docs/SettingsManager.html | 196 + docs/Template.html | 178 + docs/Template.js.html | 458 + docs/TemplateManager.html | 256 + docs/WindowCredits.html | 184 + docs/WindowCredits.js.html | 178 + docs/WindowFilter.html | 184 + docs/WindowFilter.js.html | 767 + docs/WindowMain.html | 184 + docs/WindowMain.js.html | 316 + docs/WindowSettings.html | 184 + docs/WindowSettings.js.html | 156 + docs/WindowTelemetry.html | 184 + docs/WindowTelemetry.js.html | 167 + docs/WindowWizard.html | 184 + docs/WindowWizard.js.html | 363 + docs/apiManager.js.html | 363 + docs/confetttiManager.js.html | 123 + docs/fonts/OpenSans-Bold-webfont.eot | Bin 0 -> 19544 bytes docs/fonts/OpenSans-Bold-webfont.svg | 1830 ++ docs/fonts/OpenSans-Bold-webfont.woff | Bin 0 -> 22432 bytes docs/fonts/OpenSans-BoldItalic-webfont.eot | Bin 0 -> 20133 bytes docs/fonts/OpenSans-BoldItalic-webfont.svg | 1830 ++ docs/fonts/OpenSans-BoldItalic-webfont.woff | Bin 0 -> 23048 bytes docs/fonts/OpenSans-Italic-webfont.eot | Bin 0 -> 20265 bytes docs/fonts/OpenSans-Italic-webfont.svg | 1830 ++ docs/fonts/OpenSans-Italic-webfont.woff | Bin 0 -> 23188 bytes docs/fonts/OpenSans-Light-webfont.eot | Bin 0 -> 19514 bytes docs/fonts/OpenSans-Light-webfont.svg | 1831 ++ docs/fonts/OpenSans-Light-webfont.woff | Bin 0 -> 22248 bytes docs/fonts/OpenSans-LightItalic-webfont.eot | Bin 0 -> 20535 bytes docs/fonts/OpenSans-LightItalic-webfont.svg | 1835 ++ docs/fonts/OpenSans-LightItalic-webfont.woff | Bin 0 -> 23400 bytes docs/fonts/OpenSans-Regular-webfont.eot | Bin 0 -> 19836 bytes docs/fonts/OpenSans-Regular-webfont.svg | 1831 ++ docs/fonts/OpenSans-Regular-webfont.woff | Bin 0 -> 22660 bytes docs/fonts/OpenSans-Semibold-webfont.eot | Bin 0 -> 20028 bytes docs/fonts/OpenSans-Semibold-webfont.svg | 1830 ++ docs/fonts/OpenSans-Semibold-webfont.ttf | Bin 0 -> 39476 bytes docs/fonts/OpenSans-Semibold-webfont.woff | Bin 0 -> 22908 bytes .../fonts/OpenSans-SemiboldItalic-webfont.eot | Bin 0 -> 20962 bytes .../fonts/OpenSans-SemiboldItalic-webfont.svg | 1830 ++ .../fonts/OpenSans-SemiboldItalic-webfont.ttf | Bin 0 -> 40252 bytes .../OpenSans-SemiboldItalic-webfont.woff | Bin 0 -> 23764 bytes docs/global.html | 18797 ++++++++++++++++ docs/index.html | 162 + docs/main.js.html | 344 + docs/module.exports_module.exports.html | 3189 +++ docs/observers.js.html | 121 + docs/scripts/linenumber.js | 25 + docs/scripts/prettify/Apache-License-2.0.txt | 202 + docs/scripts/prettify/lang-css.js | 2 + docs/scripts/prettify/prettify.js | 28 + docs/settingsManager.js.html | 390 + docs/styles/jsdoc-default.css | 692 + docs/styles/prettify-jsdoc.css | 111 + docs/styles/prettify-tomorrow.css | 132 + docs/templateManager.js.html | 1055 + docs/utils.js.html | 546 + 64 files changed, 47299 insertions(+) create mode 100644 docs/ApiManager.html create mode 100644 docs/ConfettiManager.html create mode 100644 docs/Observers.html create mode 100644 docs/Overlay.html create mode 100644 docs/Overlay.js.html create mode 100644 docs/SettingsManager.html create mode 100644 docs/Template.html create mode 100644 docs/Template.js.html create mode 100644 docs/TemplateManager.html create mode 100644 docs/WindowCredits.html create mode 100644 docs/WindowCredits.js.html create mode 100644 docs/WindowFilter.html create mode 100644 docs/WindowFilter.js.html create mode 100644 docs/WindowMain.html create mode 100644 docs/WindowMain.js.html create mode 100644 docs/WindowSettings.html create mode 100644 docs/WindowSettings.js.html create mode 100644 docs/WindowTelemetry.html create mode 100644 docs/WindowTelemetry.js.html create mode 100644 docs/WindowWizard.html create mode 100644 docs/WindowWizard.js.html create mode 100644 docs/apiManager.js.html create mode 100644 docs/confetttiManager.js.html create mode 100644 docs/fonts/OpenSans-Bold-webfont.eot create mode 100644 docs/fonts/OpenSans-Bold-webfont.svg create mode 100644 docs/fonts/OpenSans-Bold-webfont.woff create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-BoldItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Italic-webfont.eot create mode 100644 docs/fonts/OpenSans-Italic-webfont.svg create mode 100644 docs/fonts/OpenSans-Italic-webfont.woff create mode 100644 docs/fonts/OpenSans-Light-webfont.eot create mode 100644 docs/fonts/OpenSans-Light-webfont.svg create mode 100644 docs/fonts/OpenSans-Light-webfont.woff create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.eot create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.svg create mode 100644 docs/fonts/OpenSans-LightItalic-webfont.woff create mode 100644 docs/fonts/OpenSans-Regular-webfont.eot create mode 100644 docs/fonts/OpenSans-Regular-webfont.svg create mode 100644 docs/fonts/OpenSans-Regular-webfont.woff create mode 100755 docs/fonts/OpenSans-Semibold-webfont.eot create mode 100755 docs/fonts/OpenSans-Semibold-webfont.svg create mode 100755 docs/fonts/OpenSans-Semibold-webfont.ttf create mode 100755 docs/fonts/OpenSans-Semibold-webfont.woff create mode 100755 docs/fonts/OpenSans-SemiboldItalic-webfont.eot create mode 100755 docs/fonts/OpenSans-SemiboldItalic-webfont.svg create mode 100755 docs/fonts/OpenSans-SemiboldItalic-webfont.ttf create mode 100755 docs/fonts/OpenSans-SemiboldItalic-webfont.woff create mode 100644 docs/global.html create mode 100644 docs/index.html create mode 100644 docs/main.js.html create mode 100644 docs/module.exports_module.exports.html create mode 100644 docs/observers.js.html create mode 100644 docs/scripts/linenumber.js create mode 100644 docs/scripts/prettify/Apache-License-2.0.txt create mode 100644 docs/scripts/prettify/lang-css.js create mode 100644 docs/scripts/prettify/prettify.js create mode 100644 docs/settingsManager.js.html create mode 100644 docs/styles/jsdoc-default.css create mode 100644 docs/styles/prettify-jsdoc.css create mode 100644 docs/styles/prettify-tomorrow.css create mode 100644 docs/templateManager.js.html create mode 100644 docs/utils.js.html diff --git a/docs/ApiManager.html b/docs/ApiManager.html new file mode 100644 index 0000000..a0da020 --- /dev/null +++ b/docs/ApiManager.html @@ -0,0 +1,178 @@ + + + + + + ApiManager - Documentation + + + + + + + + + + + + + + + + + +
+ +

ApiManager

+ + + + + + + +
+ +
+ +

+ ApiManager +

+ + +
+ +
+
+ + +
+ + + +

new ApiManager()

+ + + + + +
+

ApiManager class for handling API requests, responses, and interactions. +Note: Fetch spying is done in main.js, not here.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.11.1
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/ConfettiManager.html b/docs/ConfettiManager.html new file mode 100644 index 0000000..c3837d9 --- /dev/null +++ b/docs/ConfettiManager.html @@ -0,0 +1,177 @@ + + + + + + ConfettiManager - Documentation + + + + + + + + + + + + + + + + + +
+ +

ConfettiManager

+ + + + + + + +
+ +
+ +

+ ConfettiManager +

+ + +
+ +
+
+ + +
+ + + +

new ConfettiManager()

+ + + + + +
+

Manages any confetti animation used by Blue Marble.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.356
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Observers.html b/docs/Observers.html new file mode 100644 index 0000000..2e01443 --- /dev/null +++ b/docs/Observers.html @@ -0,0 +1,178 @@ + + + + + + Observers - Documentation + + + + + + + + + + + + + + + + + +
+ +

Observers

+ + + + + + + +
+ +
+ +

+ Observers +

+ + +
+ +
+
+ + +
+ + + +

new Observers()

+ + + + + +
+

This class contains all MutationObservers used (which is 1 probably). +This is not an object, but rather a "collection" of functions (in a class).

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Overlay.html b/docs/Overlay.html new file mode 100644 index 0000000..810d3db --- /dev/null +++ b/docs/Overlay.html @@ -0,0 +1,202 @@ + + + + + + Overlay - Documentation + + + + + + + + + + + + + + + + + +
+ +

Overlay

+ + + + + + + +
+ +
+ +

+ Overlay +

+ + +
+ +
+
+ + +
+ + + +

new Overlay()

+ + + + + +
+

This class handles the overlay UI for the Blue Marble script.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.0.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
Example
+ +
const overlay = new Overlay();
+overlay.addDiv({ 'id': 'overlay' })
+  .addDiv({ 'id': 'header' })
+    .addHeader(1, {'textContent': 'Your Overlay'}).buildElement()
+    .addP({'textContent': 'This is your overlay. It is versatile.'}).buildElement()
+  .buildElement() // Marks the end of the header <div>
+  .addHr().buildElement()
+.buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <div id="overlay">
+    <div id="header">
+      <h1>Your Overlay</h1>
+      <p>This is your overlay. It is versatile.</p>
+    </div>
+    <hr>
+  </div>
+</body>
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Overlay.js.html b/docs/Overlay.js.html new file mode 100644 index 0000000..9eb49f4 --- /dev/null +++ b/docs/Overlay.js.html @@ -0,0 +1,1496 @@ + + + + + + Overlay.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

Overlay.js

+ + + + + + + +
+
+
/** The overlay builder for the Blue Marble script.
+ * @description This class handles the overlay UI for the Blue Marble script.
+ * @class Overlay
+ * @since 0.0.2
+ * @example
+ * const overlay = new Overlay();
+ * overlay.addDiv({ 'id': 'overlay' })
+ *   .addDiv({ 'id': 'header' })
+ *     .addHeader(1, {'textContent': 'Your Overlay'}).buildElement()
+ *     .addP({'textContent': 'This is your overlay. It is versatile.'}).buildElement()
+ *   .buildElement() // Marks the end of the header <div>
+ *   .addHr().buildElement()
+ * .buildOverlay(document.body);
+ * // Output:
+ * // (Assume <body> already exists in the webpage)
+ * <body>
+ *   <div id="overlay">
+ *     <div id="header">
+ *       <h1>Your Overlay</h1>
+ *       <p>This is your overlay. It is versatile.</p>
+ *     </div>
+ *     <hr>
+ *   </div>
+ * </body>
+*/
+export default class Overlay {
+
+  /** Constructor for the Overlay class.
+   * @param {string} name - The name of the userscript
+   * @param {string} version - The version of the userscript
+   * @since 0.0.2
+   * @see {@link Overlay}
+   */
+  constructor(name, version) {
+    this.name = name; // Name of userscript
+    this.version = version; // Version of userscript
+
+    /** The API manager instance. Later populated when setApiManager is called @type {ApiManager} */
+    this.apiManager = null;
+
+    /** The Settings Manager instance. Later populated when setSettingsManager is called @type {SettingsManager} */
+    this.settingsManager = null;
+    
+    this.outputStatusId = 'bm-output-status'; // ID for status element
+
+    this.overlay = null; // The overlay root DOM HTMLElement
+    this.currentParent = null; // The current parent HTMLElement in the overlay
+    this.parentStack = []; // Tracks the parent elements BEFORE the currentParent so we can nest elements
+  }
+
+  /** Populates the apiManager variable with the apiManager class.
+   * @param {ApiManager} apiManager - The apiManager class instance
+   * @since 0.41.4
+   */
+  setApiManager(apiManager) {this.apiManager = apiManager;}
+
+  /** Populates the settingsManager variable with the settingsManager class.
+   * @param {SettingsManager} settingsManager - The settingsManager class instance
+   * @since 0.91.11
+   */
+  setSettingsManager(settingsManager) {this.settingsManager = settingsManager;}
+
+  /** Creates an element.
+   * For **internal use** of the {@link Overlay} class.
+   * @param {string} tag - The tag name as a string.
+   * @param {Object.<string, any>} [properties={}] - The DOM properties of the element.
+   * @returns {HTMLElement} HTML Element
+   * @since 0.43.2
+   */
+  #createElement(tag, properties = {}, additionalProperties={}) {
+
+    const element = document.createElement(tag); // Creates the element
+
+    // If this is the first element made...
+    if (!this.overlay) {
+      this.overlay = element; // Declare it the highest overlay element
+      this.currentParent = element;
+    } else {
+      this.currentParent?.appendChild(element); // ...else delcare it the child of the last element
+      this.parentStack.push(this.currentParent);
+      this.currentParent = element;
+    }
+
+    // For every passed in property (shared by all like-elements), apply the it to the element
+    for (const [property, value] of Object.entries(properties)) {
+      this.#applyAttribute(element, property, value);
+    }
+
+    // For every passed in additional property, apply the it to the element
+    for (const [property, value] of Object.entries(additionalProperties)) {
+      this.#applyAttribute(element, property, value);
+    }
+    
+    return element;
+  }
+
+  /** Applies an attribute to an element
+   * @param {HTMLElement} element - The element to apply the attribute to
+   * @param {String} property - The name of the attribute to apply
+   * @param {String} value - The value of the attribute
+   * @since 0.88.136
+   */
+  #applyAttribute(element, property, value) {
+    if (property == 'class') {
+      element.classList.add(...value.split(/\s+/)); // converts `'foo bar'` to `'foo', 'bar'` which is accepted
+    } else if (property == 'for') {
+      element.htmlFor = value;
+    } else if (property == 'tabindex') {
+      element.tabIndex = Number(value);
+    } else if (property == 'readonly') {
+      element.readOnly = ((value == 'true') || (value == '1'));
+    } else if (property == 'maxlength') {
+      element.maxLength = Number(value);
+    } else if (property.startsWith('data')) {
+      element.dataset[
+        property.slice(5).split('-').map(
+          (part, i) => (i == 0) ? part : part[0].toUpperCase() + part.slice(1)
+        ).join('')
+      ] = value;
+    } else if (property.startsWith('aria')) {
+      element.setAttribute(property, value); // We can't do the solution for 'data', as 'aria-labelledby' would fail to apply
+    } else {
+      element[property] = value;
+    }
+  }
+
+  /** Finishes building an element.
+   * Call this after you are finished adding children.
+   * If the element will have no children, call it anyways.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.2
+   * @example
+   * overlay
+   *   .addDiv()
+   *     .addHeader(1).buildElement() // Breaks out of the <h1>
+   *     .addP().buildElement() // Breaks out of the <p>
+   *   .buildElement() // Breaks out of the <div>
+   *   .addHr() // Since there are no more elements, calling buildElement() is optional
+   * .buildOverlay(document.body);
+   */
+  buildElement() {
+    if (this.parentStack.length > 0) {
+      this.currentParent = this.parentStack.pop();
+    }
+    return this;
+  }
+
+  /** Finishes building the overlay and displays it.
+   * Call this when you are done chaining methods.
+   * @param {HTMLElement} parent - The parent HTMLElement this overlay should be appended to as a child.
+   * @since 0.43.2
+   * @example
+   * overlay
+   *   .addDiv()
+   *     .addP().buildElement()
+   *   .buildElement()
+   * .buildOverlay(document.body); // Adds DOM structure to document body
+   * // <div><p></p></div>
+   */
+  buildOverlay(parent) {
+    parent?.appendChild(this.overlay);
+
+    // Resets the class-bound variables of this class instance back to default so overlay can be build again later
+    this.overlay = null;
+    this.currentParent = null;
+    this.parentStack = [];
+  }
+
+  /** Adds a `div` to the overlay.
+   * This `div` element will have properties shared between all `div` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `div` that are NOT shared between all overlay `div` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLDivElement):void} [callback=()=>{}] - Additional JS modification to the `div`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.2
+   * @example
+   * // Assume all <div> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addDiv({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <div id="foo" class="bar"></div>
+   * </body>
+   */
+  addDiv(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <div> DOM properties
+
+    const div = this.#createElement('div', properties, additionalProperties); // Creates the <div> element
+    callback(this, div); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `p` to the overlay.
+   * This `p` element will have properties shared between all `p` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `p` that are NOT shared between all overlay `p` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLParagraphElement):void} [callback=()=>{}] - Additional JS modification to the `p`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.2
+   * @example
+   * // Assume all <p> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addP({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <p id="foo" class="bar">Foobar.</p>
+   * </body>
+   */
+  addP(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <p> DOM properties
+
+    const p = this.#createElement('p', properties, additionalProperties); // Creates the <p> element
+    callback(this, p); // Runs any script passed in through the callback
+    return this;
+  }
+  
+  /** Adds a `small` to the overlay.
+   * This `small` element will have properties shared between all `small` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `small` that are NOT shared between all overlay `small` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLElement):void} [callback=()=>{}] - Additional JS modification to the `small`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.55.8
+   * @example
+   * // Assume all <small> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addSmall({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <small id="foo" class="bar">Foobar.</small>
+   * </body>
+   */
+  addSmall(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <small> DOM properties
+
+    const small = this.#createElement('small', properties, additionalProperties); // Creates the <small> element
+    callback(this, small); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `span` to the overlay.
+   * This `span` element will have properties shared between all `span` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `span` that are NOT shared between all overlay `span` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLSpanElement):void} [callback=()=>{}] - Additional JS modification to the `span`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.55.8
+   * @example
+   * // Assume all <span> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addSpan({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <span id="foo" class="bar">Foobar.</span>
+   * </body>
+   */
+  addSpan(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <span> DOM properties
+
+    const span = this.#createElement('span', properties, additionalProperties); // Creates the <span> element
+    callback(this, span); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `details` to the overlay.
+   * This `details` element will have properties shared between all `details` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `details` that are NOT shared between all overlay `details` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLDetailsElement):void} [callback=()=>{}] - Additional JS modification to the `details`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.96
+   * @example
+   * // Assume all <details> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addDetails({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <details id="foo" class="bar"></details>
+   * </body>
+   */
+  addDetails(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <details> DOM properties
+
+    const details = this.#createElement('details', properties, additionalProperties); // Creates the <details> element
+    callback(this, details); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `summary` to the overlay.
+   * This `summary` element will have properties shared between all `summary` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `summary` that are NOT shared between all overlay `summary` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLElement):void} [callback=()=>{}] - Additional JS modification to the `summary`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.96
+   * @example
+   * // Assume all <summary> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addSummary({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <summary id="foo" class="bar">Foobar.</summary>
+   * </body>
+   */
+  addSummary(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <summary> DOM properties
+
+    const summary = this.#createElement('summary', properties, additionalProperties); // Creates the <summary> element
+    callback(this, summary); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `img` to the overlay.
+   * This `img` element will have properties shared between all `img` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `img` that are NOT shared between all overlay `img` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLImageElement):void} [callback=()=>{}] - Additional JS modification to the `img`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.2
+   * @example
+   * // Assume all <img> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addimg({'id': 'foo', 'src': './img.png'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <img id="foo" src="./img.png" class="bar">
+   * </body>
+   */
+  addImg(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <img> DOM properties
+
+    const img = this.#createElement('img', properties, additionalProperties); // Creates the <img> element
+    callback(this, img); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a header to the overlay.
+   * This header element will have properties shared between all header elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {number} level - The header level. Must be between 1 and 6 (inclusive)
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the header that are NOT shared between all overlay header elements. These should be camelCase.
+   * @param {function(Overlay, HTMLHeadingElement):void} [callback=()=>{}] - Additional JS modification to the header.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.7
+   * @example
+   * // Assume all header elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addHeader(6, {'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <h6 id="foo" class="bar">Foobar.</h6>
+   * </body>
+   */
+  addHeader(level, additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared header DOM properties
+
+    const header = this.#createElement('h' + level, properties, additionalProperties); // Creates the header element
+    callback(this, header); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `hr` to the overlay.
+   * This `hr` element will have properties shared between all `hr` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `hr` that are NOT shared between all overlay `hr` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLHRElement):void} [callback=()=>{}] - Additional JS modification to the `hr`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.7
+   * @example
+   * // Assume all <hr> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addhr({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <hr id="foo" class="bar">
+   * </body>
+   */
+  addHr(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <hr> DOM properties
+
+    const hr = this.#createElement('hr', properties, additionalProperties); // Creates the <hr> element
+    callback(this, hr); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `br` to the overlay.
+   * This `br` element will have properties shared between all `br` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `br` that are NOT shared between all overlay `br` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLBRElement):void} [callback=()=>{}] - Additional JS modification to the `br`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.11
+   * @example
+   * // Assume all <br> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addbr({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <br id="foo" class="bar">
+   * </body>
+   */
+  addBr(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <br> DOM properties
+
+    const br = this.#createElement('br', properties, additionalProperties); // Creates the <br> element
+    callback(this, br); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `form` to the overlay.
+   * This `form` element will have properties shared between all `form` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `form` that are NOT shared between all overlay `form` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLFormElement):void} [callback=()=>{}] - Additional JS modification to the `form`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.246
+   * @example
+   * // Assume all <form> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addForm({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <form id="foo" class="bar"></form>
+   * </body>
+   */
+  addForm(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <form> DOM properties
+
+    const form = this.#createElement('form', properties, additionalProperties); // Creates the <form> element
+    callback(this, form); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `fieldset` to the overlay.
+   * This `fieldset` element will have properties shared between all `fieldset` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `fieldset` that are NOT shared between all overlay `fieldset` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLFieldSetElement):void} [callback=()=>{}] - Additional JS modification to the `fieldset`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.246
+   * @example
+   * // Assume all <fieldset> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addFieldset({'id': 'foo'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <fieldset id="foo" class="bar"></fieldset>
+   * </body>
+   */
+  addFieldset(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <fieldset> DOM properties
+
+    const fieldset = this.#createElement('fieldset', properties, additionalProperties); // Creates the <fieldset> element
+    callback(this, fieldset); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `legend` to the overlay.
+   * This `legend` element will have properties shared between all `legend` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `legend` that are NOT shared between all overlay `legend` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLLegendElement):void} [callback=()=>{}] - Additional JS modification to the `legend`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.246
+   * @example
+   * // Assume all <legend> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addLegend({'id': 'foo', textContent: 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <legend id="foo" class="bar">
+   *     "Foobar."
+   *   </legend>
+   * </body>
+   */
+  addLegend(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <legend> DOM properties
+
+    const legend = this.#createElement('legend', properties, additionalProperties); // Creates the <legend> element
+    callback(this, legend); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a checkbox to the overlay.
+   * This checkbox element will have properties shared between all checkbox elements in the overlay.
+   * You can override the shared properties by using a callback. Note: the checkbox element is inside a label element.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the checkbox that are NOT shared between all overlay checkbox elements. These should be camelCase.
+   * @param {function(Overlay, HTMLLabelElement, HTMLInputElement):void} [callback=()=>{}] - Additional JS modification to the checkbox.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.10
+   * @example
+   * // Assume all checkbox elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addCheckbox({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <label>
+   *     <input type="checkbox" id="foo" class="bar">
+   *     "Foobar."
+   *   </label>
+   * </body>
+   */
+  addCheckbox(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {'type': 'checkbox'}; // Shared checkbox DOM properties
+
+    // Stores the label content from the additional property
+    const labelContent = {};
+
+    // If the label content was passed in as 'textContent'...
+    if (!!additionalProperties['textContent']) {
+
+      // Store the information, then delete it from additionalProperties
+      labelContent['textContent'] = additionalProperties['textContent'];
+      delete additionalProperties['textContent']; // Deletes 'textContent' DOM property before adding the properties to the checkbox
+    } else if (!!additionalProperties['innerHTML']) {
+      // Else if the label content was passed in as 'innerHTML'...
+
+      // Store the information, then delete it from additionalProperties
+      labelContent['innerHTML'] = additionalProperties['innerHTML'];
+      delete additionalProperties['textContent'];
+    }
+
+    const label = this.#createElement('label', labelContent); // Creates the label element
+    const checkbox = this.#createElement('input', properties, additionalProperties); // Creates the checkbox element
+    label.insertBefore(checkbox, label.firstChild); // Makes the checkbox the first child of the label (before the text content)
+    this.buildElement(); // Signifies that we are done adding children to the checkbox
+    callback(this, label, checkbox); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a label & select element to the overlay.
+   * This select element will have properties shared between all select elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the checkbox that are NOT shared between all overlay select elements. These should be camelCase.
+   * @param {function(Overlay, HTMLLabelElement, HTMLSelectElement):void} [callback=()=>{}] - Additional JS modification to the label/select elements.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.243
+   * @example
+   * // Assume all select elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addSelect({'id': 'foo', 'textContent': 'Foobar: '}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <label for="foo">
+   *     "Foobar: "
+   *   </label>
+   *   <select id="foo" class="bar"></select>
+   * </body>
+   */
+  addSelect(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared select DOM properties
+
+    const label = this.#createElement('label', {'textContent': additionalProperties['textContent'] ?? '', 'for': additionalProperties['id'] ?? ''}); // Creates the label element
+    delete additionalProperties['textContent']; // Deletes 'textContent' DOM property before adding the properties to the select
+    this.buildElement(); // Signifies that we are done adding children to the label
+    
+    const select = this.#createElement('select', properties, additionalProperties); // Creates the select element
+    callback(this, label, select); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds an option to the overlay.
+   * This `option` element will have properties shared between all `option` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `option` that are NOT shared between all overlay `option` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLOptionElement):void} [callback=()=>{}] - Additional JS modification to the `option`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.244
+   * @example
+   * // Assume all <option> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addOption({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <option id="foo" class="bar">Foobar.</option>
+   * </body>
+   */
+  addOption(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <option> DOM properties
+
+    const option = this.#createElement('option', properties, additionalProperties); // Creates the <option> element
+    callback(this, option); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds an ordered list to the overlay.
+   * This `ol` element will have properties shared between all `ol` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `ol` that are NOT shared between all overlay `ol` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLOListElement):void} [callback=()=>{}] - Additional JS modification to the `ol`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <ol> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addOl({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <ol id="foo" class="bar">Foobar.</ol>
+   * </body>
+   */
+  addOl(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <ol> DOM properties
+
+    const ol = this.#createElement('ol', properties, additionalProperties); // Creates the <ol> element
+    callback(this, ol); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds an unordered list to the overlay.
+   * This `ul` element will have properties shared between all `ul` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `ul` that are NOT shared between all overlay `ul` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLUListElement):void} [callback=()=>{}] - Additional JS modification to the `ul`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <ul> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addUl({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <ul id="foo" class="bar">Foobar.</ul>
+   * </body>
+   */
+  addUl(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <ul> DOM properties
+
+    const ul = this.#createElement('ul', properties, additionalProperties); // Creates the <ul> element
+    callback(this, ul); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `menu` to the overlay.
+   * This `menu` element will have properties shared between all `menu` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `menu` that are NOT shared between all overlay `menu` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLMenuElement):void} [callback=()=>{}] - Additional JS modification to the `menu`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <menu> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addMenu({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <menu id="foo" class="bar">Foobar.</menu>
+   * </body>
+   */
+  addMenu(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <menu> DOM properties
+
+    const menu = this.#createElement('menu', properties, additionalProperties); // Creates the <menu> element
+    callback(this, menu); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a list item to the overlay.
+   * This `li` element will have properties shared between all `li` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `li` that are NOT shared between all overlay `li` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLLIElement):void} [callback=()=>{}] - Additional JS modification to the `li`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <li> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addLi({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <li id="foo" class="bar">Foobar.</li>
+   * </body>
+   */
+  addLi(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <li> DOM properties
+
+    const li = this.#createElement('li', properties, additionalProperties); // Creates the <li> element
+    callback(this, li); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table to the overlay.
+   * This `table` element will have properties shared between all `table` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `table` that are NOT shared between all overlay `table` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableElement):void} [callback=()=>{}] - Additional JS modification to the `table`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <table> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTable({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <table id="foo" class="bar">Foobar.</table>
+   * </body>
+   */
+  addTable(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <table> DOM properties
+
+    const table = this.#createElement('table', properties, additionalProperties); // Creates the <table> element
+    callback(this, table); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table caption to the overlay.
+   * This `caption` element will have properties shared between all `caption` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `caption` that are NOT shared between all overlay `caption` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableCaptionElement):void} [callback=()=>{}] - Additional JS modification to the `caption`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <caption> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addCaption({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <caption id="foo" class="bar">Foobar.</caption>
+   * </body>
+   */
+  addCaption(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <caption> DOM properties
+
+    const caption = this.#createElement('caption', properties, additionalProperties); // Creates the <caption> element
+    callback(this, caption); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table header to the overlay.
+   * This `thead` element will have properties shared between all `thead` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `thead` that are NOT shared between all overlay `thead` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableSectionElement):void} [callback=()=>{}] - Additional JS modification to the `thead`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <thead> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addThead({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <thead id="foo" class="bar">Foobar.</thead>
+   * </body>
+   */
+  addThead(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <thead> DOM properties
+
+    const thead = this.#createElement('thead', properties, additionalProperties); // Creates the <thead> element
+    callback(this, thead); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table body to the overlay.
+   * This `tbody` element will have properties shared between all `tbody` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `tbody` that are NOT shared between all overlay `tbody` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableSectionElement):void} [callback=()=>{}] - Additional JS modification to the `tbody`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <tbody> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTbody({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <tbody id="foo" class="bar">Foobar.</tbody>
+   * </body>
+   */
+  addTbody(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <tbody> DOM properties
+
+    const tbody = this.#createElement('tbody', properties, additionalProperties); // Creates the <tbody> element
+    callback(this, tbody); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table footer to the overlay.
+   * This `tfoot` element will have properties shared between all `tfoot` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `tfoot` that are NOT shared between all overlay `tfoot` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableSectionElement):void} [callback=()=>{}] - Additional JS modification to the `tfoot`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <tfoot> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTfoot({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <tfoot id="foo" class="bar">Foobar.</tfoot>
+   * </body>
+   */
+  addTfoot(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <tfoot> DOM properties
+
+    const tfoot = this.#createElement('tfoot', properties, additionalProperties); // Creates the <tfoot> element
+    callback(this, tfoot); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table row to the overlay.
+   * This `tr` element will have properties shared between all `tr` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `tr` that are NOT shared between all overlay `tr` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableRowElement):void} [callback=()=>{}] - Additional JS modification to the `tr`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <tr> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTr({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <tr id="foo" class="bar">Foobar.</tr>
+   * </body>
+   */
+  addTr(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <tfoot> DOM properties
+
+    const tr = this.#createElement('tr', properties, additionalProperties); // Creates the <tr> element
+    callback(this, tr); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table header (label) cell to the overlay.
+   * This `th` element will have properties shared between all `th` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `th` that are NOT shared between all overlay `th` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableCellElement):void} [callback=()=>{}] - Additional JS modification to the `th`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <th> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTh({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <th id="foo" class="bar">Foobar.</th>
+   * </body>
+   */
+  addTh(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <th> DOM properties
+
+    const th = this.#createElement('th', properties, additionalProperties); // Creates the <th> element
+    callback(this, th); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a table data cell to the overlay.
+   * This `td` element will have properties shared between all `td` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `td` that are NOT shared between all overlay `td` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTableCellElement):void} [callback=()=>{}] - Additional JS modification to the `td`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.180
+   * @example
+   * // Assume all <td> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTd({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <td id="foo" class="bar">Foobar.</td>
+   * </body>
+   */
+  addTd(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <td> DOM properties
+
+    const td = this.#createElement('td', properties, additionalProperties); // Creates the <td> element
+    callback(this, td); // Runs any script passed in through the callback
+    return this;
+  }
+  
+  /** Adds a `button` to the overlay.
+   * This `button` element will have properties shared between all `button` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `button` that are NOT shared between all overlay `button` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLButtonElement):void} [callback=()=>{}] - Additional JS modification to the `button`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.12
+   * @example
+   * // Assume all <button> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addButton({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <button id="foo" class="bar">Foobar.</button>
+   * </body>
+   */
+  addButton(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <button> DOM properties
+
+    const button = this.#createElement('button', properties, additionalProperties); // Creates the <button> element
+    callback(this, button); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a help button to the overlay. It will have a "?" icon unless overridden in callback.
+   * On click, the button will attempt to output the title to the output element (ID defined in Overlay constructor).
+   * This `button` element will have properties shared between all `button` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `button` that are NOT shared between all overlay `button` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLButtonElement):void} [callback=()=>{}] - Additional JS modification to the `button`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.12
+   * @example
+   * // Assume all help button elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addButtonHelp({'id': 'foo', 'title': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <button id="foo" class="bar" title="Help: Foobar.">?</button>
+   * </body>
+   * @example
+   * // Assume all help button elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addButtonHelp({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <button id="foo" class="bar" title="Help: Foobar.">?</button>
+   * </body>
+   */
+  addButtonHelp(additionalProperties = {}, callback = () => {}) {
+
+    const tooltip = additionalProperties['title'] ?? additionalProperties['textContent'] ?? 'Help: No info'; // Retrieves the tooltip
+
+    // Makes sure the tooltip is stored in the title property
+    delete additionalProperties['textContent'];
+    additionalProperties['title'] = `Help: ${tooltip}`;
+
+    // Shared help button DOM properties
+    const properties = {
+      'textContent': '?',
+      'className': 'bm-help',
+      'onclick': () => {
+        this.updateInnerHTML(this.outputStatusId, tooltip);
+      }
+    };
+
+    const help = this.#createElement('button', properties, additionalProperties); // Creates the <button> element
+    callback(this, help); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `input` to the overlay.
+   * This `input` element will have properties shared between all `input` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `input` that are NOT shared between all overlay `input` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLInputElement):void} [callback=()=>{}] - Additional JS modification to the `input`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.13
+   * @example
+   * // Assume all <input> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addInput({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <input id="foo" class="bar">Foobar.</input>
+   * </body>
+   */
+  addInput(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <input> DOM properties
+
+    const input = this.#createElement('input', properties, additionalProperties); // Creates the <input> element
+    callback(this, input); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a file input to the overlay with enhanced visibility controls.
+   * This input element will have properties shared between all file input elements in the overlay.
+   * Uses multiple hiding methods to prevent browser native text from appearing during minimize/maximize.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the file input that are NOT shared between all overlay file input elements. These should be camelCase.
+   * @param {function(Overlay, HTMLDivElement, HTMLInputElement, HTMLButtonElement):void} [callback=()=>{}] - Additional JS modification to the file input.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.17
+   * @example
+   * // Assume all file input elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addInputFile({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <div>
+   *     <input type="file" id="foo" class="bar" style="display: none"></input>
+   *     <button>Foobar.</button>
+   *   </div>
+   * </body>
+   */
+  addInputFile(additionalProperties = {}, callback = () => {}) {
+    
+    const properties = {
+      'type': 'file', 
+      'tabindex': '-1',
+      'aria-hidden': 'true'
+    }; // Complete file input hiding to prevent native browser text interference
+    const text = additionalProperties['textContent'] ?? ''; // Retrieves the text content
+
+    delete additionalProperties['textContent']; // Deletes the text content before applying the additional properties to the file input
+
+    const container = this.#createElement('div'); // Container for file input
+    const input = this.#createElement('input', properties, additionalProperties); // Creates the file input
+    this.buildElement(); // Signifies that we are done adding children to the file input
+    const button = this.#createElement('button', {'textContent': text});
+    this.buildElement(); // Signifies that we are done adding children to the button
+    this.buildElement(); // Signifies that we are done adding children to the container
+    
+    button.addEventListener('click', () => {
+      input.click(); // Clicks the file input
+    });
+
+    // Update button text when file is selected
+    input.addEventListener('change', () => {
+      button.style.maxWidth = `${button.offsetWidth}px`;
+      if (input.files.length > 0) {
+        button.textContent = input.files[0].name;
+      } else {
+        button.textContent = text;
+      }
+    });
+
+    callback(this, container, input, button); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a `textarea` to the overlay.
+   * This `textarea` element will have properties shared between all `textarea` elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the `textarea` that are NOT shared between all overlay `textarea` elements. These should be camelCase.
+   * @param {function(Overlay, HTMLTextAreaElement):void} [callback=()=>{}] - Additional JS modification to the `textarea`.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.43.13
+   * @example
+   * // Assume all <textarea> elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTextarea({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <textarea id="foo" class="bar">Foobar.</textarea>
+   * </body>
+   */
+  addTextarea(additionalProperties = {}, callback = () => {}) {
+
+    const properties = {}; // Shared <textarea> DOM properties
+
+    const textarea = this.#createElement('textarea', properties, additionalProperties); // Creates the <textarea> element
+    callback(this, textarea); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a dragbar `div` element to the overlay.
+   * This dragbar element will have properties shared between all dragbar elements in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the dragbar that are NOT shared between all overlay dragbars. These should be camelCase.
+   * @param {function(Overlay, HTMLDivElement):void} [callback=()=>{}] - Additional JS modification to the dragbar.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.145
+   * @example
+   * // Assume all dragbar elements have a shared class (e.g. {'className': 'bar'})
+   * overlay.addDragbar({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <div id="foo" class="bar">Foobar.</div>
+   * </body>
+   */
+  addDragbar(additionalProperties = {}, callback = () => {}) {
+
+    // Shared dragbar DOM properties
+    const properties = {
+      'class': 'bm-dragbar'
+    };
+
+    const dragbar = this.#createElement('div', properties, additionalProperties); // Creates the dragbar element
+    callback(this, dragbar); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Adds a timer `time` element to the overlay.
+   * This timer will countdown until it reaches the end date that was passed in.
+   * Additionally, you can update the end date by changing the endDate dataset attribute on the element.
+   * Timer elements are not localized. Output is HH:MM:SS with no units.
+   * This timer will have properties shared between all timers in the overlay.
+   * You can override the shared properties by using a callback.
+   * @param {Date} [endDate=Date.now()] - The time to count down to.
+   * @param {number} [updateInterval=500] - The time in milliseconds to update the display of the timer. Default is 500 milliseconds.
+   * @param {Object.<string, any>} [additionalProperties={}] - The DOM properties of the timer that are NOT shared between all overlay timers. These should be camelCase.
+   * @param {function(Overlay, HTMLTimeElement):void} [callback=()=>{}] - Additional JS modification to the timer.
+   * @returns {Overlay} Overlay class instance (this)
+   * @since 0.88.313
+   * @example
+   * // Assume all timers have a shared class (e.g. {'className': 'bar'})
+   * overlay.addTimer(Date.now() + 2211632704000, 500, {'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+   * // Output:
+   * // (Assume <body> already exists in the webpage)
+   * <body>
+   *   <time id="bm-timer-dh8fhw80" class="bar" datetime="PT27H34M56S" data-end-date="1771749296000">27:34:56</div>
+   * </body>
+   */
+  addTimer(endDate = Date.now(), updateInterval = 500, additionalProperties = {}, callback = () => {}) {
+
+    const timerClass = 'bm-timer';
+
+    // Use the provided ID attribute. Otherwise, generate a random one.
+    // "Random" ID is 8 pseudo-random hexdecimal characters from a UUIDv4 string
+    // The ID has a prefix that is equal to the bm-timer class, then a dash, then the random hexdecimal digits
+    const timerID = additionalProperties?.['id'] || (timerClass + '-' + crypto.randomUUID().slice(0, 8));
+
+    // Shared timer DOM properties
+    const properties = {
+      'class': timerClass
+    }
+
+    const timer = this.#createElement('time', properties, additionalProperties); // Creates the timer element
+    timer.id = timerID; // Adds the ID to the timer
+    timer.dataset['endDate'] = endDate; // Adds the end date to the timer
+
+    // Creates the logic that keeps updating the timer
+    setInterval(() => {
+
+      // Kills the timer logic if the timer element does not exist in the main DOM tree
+      if (!timer.isConnected) {/*clearInterval(timer);*/ return;}
+
+      // Returns time remaining in seconds, or 0 seconds if timer has reached end time.
+      // "Total" indicates it is the total time for that unit. E.g. 62 minutes is "62" minutes.
+      const timeRemainingTotalMs = Math.max(timer.dataset['endDate'] - Date.now(), 0);
+      const timeRemainingTotalSec = Math.floor(timeRemainingTotalMs / 1000);
+      const timeRemainingTotalHr = Math.floor(timeRemainingTotalSec / 3600);
+      
+      // Remaining time in certain units.
+      // "Only" indicates it is formatted in that unit. E.g. 62 minutes is "2" minutes.
+      const timeRemainingOnlySec = Math.floor(timeRemainingTotalSec % 60);
+      const timeRemainingOnlyMin = Math.floor((timeRemainingTotalSec % 3600) / 60);
+
+      // Date-time string that is compliant with the HTML Standard for durations
+      timer.setAttribute('datetime', `PT${timeRemainingTotalHr}H${timeRemainingOnlyMin}M${timeRemainingOnlySec}S`);
+      // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-duration-string
+
+      // Formats the timer as HH:MM:SS and displays it to the user
+      timer.textContent = 
+        String(timeRemainingTotalHr).padStart(2, '0') + ':' +
+        String(timeRemainingOnlyMin).padStart(2, '0') + ':' +
+        String(timeRemainingOnlySec).padStart(2, '0')
+      ;
+    }, updateInterval);
+
+    callback(this, timer); // Runs any script passed in through the callback
+    return this;
+  }
+
+  /** Updates the inner HTML of the element.
+   * The element is discovered by it's id.
+   * If the element is an `input`, it will modify the value attribute instead.
+   * @param {string} id - The ID of the element to change
+   * @param {string} html - The HTML/text to update with
+   * @param {boolean} [doSafe] - (Optional) Should `textContent` be used instead of `innerHTML` to avoid XSS? False by default
+   * @since 0.24.2
+   */
+  updateInnerHTML(id, html, doSafe=false) {
+
+    const element = document.getElementById(id.replace(/^#/, '')); // Retrieve the element from the 'id' (removed the '#')
+    
+    if (!element) {return;} // Kills itself if the element does not exist
+
+    // Input elements don't have innerHTML, so we modify the value attribute instead
+    if (element instanceof HTMLInputElement) {
+      element.value = html;
+      return;
+    } 
+
+    if (doSafe) {
+      element.textContent = html; // Populate element with plain-text HTML/text
+    } else {
+      element.innerHTML = html; // Populate element with HTML/text
+    }
+  }
+
+  /** Handles the minimization logic for windows spawned by Blue Marble
+   * @param {HTMLButtonElement} button - The UI button that triggered this minimization event
+   * @since 0.88.142
+  */
+  handleMinimization(button) {
+
+    if (button.disabled) {return;} // Don't minimize if the window is currently minimizing
+
+    button.disabled = true; // Disables the button until the transition ends
+    button.style.textDecoration = 'none'; // Disables the disabled button text decoration strikethrough line
+
+    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
+
+    window.parentElement.append(window); // Moves the window to the top
+
+    // If window content is open...
+    if (button.dataset['buttonStatus'] == 'expanded') {
+      // ...we want to close it
+      
+      // Logic for the transition animation to collapse the window
+      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
+        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
+      });
+      
+      // Makes a clone of the h1 element inside the window, and adds it to the dragbar
+      const dragbarHeader1 = header.cloneNode(true);
+      const dragbarHeader1Text = dragbarHeader1.textContent;
+      button.nextElementSibling.appendChild(dragbarHeader1);
+      
+      button.textContent = '▶'; // Swap button icon
+      button.dataset['buttonStatus'] = 'collapsed'; // Swap button status tracker
+      button.ariaLabel = `Unminimize window "${dragbarHeader1Text}"`; // Screen reader label
+    } else {
+      // Else, the window is closed, and we want to open it
+
+      // Deletes the h1 element inside the dragbar
+      const dragbarHeader1 = dragbar.querySelector('h1');
+      const dragbarHeader1Text = dragbarHeader1.textContent;
+      dragbarHeader1.remove();
+
+      // 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
+        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
+      });
+
+      button.textContent = '▼'; // Swap button icon
+      button.dataset['buttonStatus'] = 'expanded'; // Swap button status tracker
+      button.ariaLabel = `Minimize window "${dragbarHeader1Text}"`; // Screen reader label
+    }
+  }
+
+  /** Handles dragging of the overlay.
+   * Uses requestAnimationFrame for smooth animations and GPU-accelerated transforms.
+   * Make sure to use the appropriate CSS selectors.
+   * @param {string} moveMeSelector - The element to be moved
+   * @param {string} iMoveThingsSelector - The drag handle element
+   * @since 0.8.2
+  */
+  handleDrag(moveMeSelector, iMoveThingsSelector) {
+
+    // Retrieves the elements
+    const moveMe = document.querySelector(moveMeSelector);
+    const iMoveThings = document.querySelector(iMoveThingsSelector);
+    
+    // What to do when one of the two elements are not found
+    if (!moveMe || !iMoveThings) {
+      this.handleDisplayError(`Can not drag! ${!moveMe ? 'moveMe' : ''} ${!moveMe && !iMoveThings ? 'and ' : ''}${!iMoveThings ? 'iMoveThings ' : ''}was not found!`);
+      return; // Kills itself
+    }
+
+    let isDragging = false;
+    let offsetX, offsetY = 0;
+    let animationFrame = null;
+    let currentX = 0;
+    let currentY = 0;
+    let targetX = 0;
+    let targetY = 0;
+    let initialRect = null; // Cache initial position to avoid expensive getBoundingClientRect calls during drag
+
+    // Smooth animation loop using requestAnimationFrame for optimal performance
+    const updatePosition = () => {
+      if (isDragging) {
+        // Only update DOM if position changed significantly (reduce repaints)
+        const deltaX = Math.abs(currentX - targetX);
+        const deltaY = Math.abs(currentY - targetY);
+        
+        if (deltaX > 0.5 || deltaY > 0.5) {
+          currentX = targetX;
+          currentY = targetY;
+          
+          // Use CSS transform for GPU acceleration instead of left/top
+          moveMe.style.transform = `translate(${currentX}px, ${currentY}px)`;
+          moveMe.style.left = '0px';
+          moveMe.style.top = '0px';
+          moveMe.style.right = '';
+        }
+        
+        animationFrame = requestAnimationFrame(updatePosition);
+      }
+    };
+    
+    const startDrag = (clientX, clientY) => {
+      isDragging = true;
+      initialRect = moveMe.getBoundingClientRect();
+      offsetX = clientX - initialRect.left;
+      offsetY = clientY - initialRect.top;
+      
+      // Get current position from transform or use element position
+      const computedStyle = window.getComputedStyle(moveMe);
+      const transform = computedStyle.transform;
+      
+      if (transform && transform !== 'none') {
+        const matrix = new DOMMatrix(transform);
+        currentX = matrix.m41;
+        currentY = matrix.m42;
+      } else {
+        currentX = initialRect.left;
+        currentY = initialRect.top;
+      }
+      
+      targetX = currentX;
+      targetY = currentY;
+      
+      document.body.style.userSelect = 'none';
+      iMoveThings.classList.add('bm-dragging');
+
+      // Add move listeners when dragging starts
+      document.addEventListener('mousemove', onMouseMove);
+      document.addEventListener('touchmove', onTouchMove, { passive: false });
+      document.addEventListener('mouseup', endDrag);
+      document.addEventListener('touchend', endDrag);
+      document.addEventListener('touchcancel', endDrag);
+      
+      // Start animation loop
+      if (animationFrame) {
+        cancelAnimationFrame(animationFrame);
+      }
+      updatePosition();
+    };
+
+    const endDrag = () => {
+      isDragging = false;
+      if (animationFrame) {
+        cancelAnimationFrame(animationFrame);
+        animationFrame = null;
+      }
+      document.body.style.userSelect = '';
+      iMoveThings.classList.remove('bm-dragging');
+
+      // Remove move listeners when drag ends
+      document.removeEventListener('mousemove', onMouseMove);
+      document.removeEventListener('touchmove', onTouchMove);
+      document.removeEventListener('mouseup', endDrag);
+      document.removeEventListener('touchend', endDrag);
+      document.removeEventListener('touchcancel', endDrag);
+    };
+
+    // Mouse move
+    const onMouseMove = event => {
+      if (isDragging && initialRect) {
+        targetX = event.clientX - offsetX;
+        targetY = event.clientY - offsetY;
+      }
+    }
+
+    // Touch move
+    const onTouchMove = event => {
+      if (isDragging && initialRect) {
+        const touch = event.touches[0];
+        if (!touch) return;
+        targetX = touch.clientX - offsetX;
+        targetY = touch.clientY - offsetY;
+        event.preventDefault();
+      }
+    };
+
+    // Mouse down - start dragging
+    iMoveThings.addEventListener('mousedown', function(event) {
+      event.preventDefault();
+      startDrag(event.clientX, event.clientY);
+    });
+
+    // Touch start - start dragging
+    iMoveThings.addEventListener('touchstart', function(event) {
+      const touch = event?.touches?.[0];
+      if (!touch) {return;}
+      startDrag(touch.clientX, touch.clientY);
+      event.preventDefault();
+    }, { 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.
+   * @param {string} text - The status text to display.
+   * @since 0.58.4
+   */
+  handleDisplayStatus(text) {
+    const consoleInfo = console.info; // Creates a copy of the console.info function
+    consoleInfo(`${this.name}: ${text}`); // Outputs something like "ScriptName: text" as an info message to the console
+    this.updateInnerHTML(this.outputStatusId, 'Status: ' + text, true); // Update output Status box
+  }
+
+  /** Handles error display.
+   * This will output plain text into the output Status box.
+   * Additionally, this will output an error to the console.
+   * @param {string} text - The error text to display.
+   * @since 0.41.6
+   */
+  handleDisplayError(text) {
+    const consoleError = console.error; // Creates a copy of the console.error function
+    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
+  }
+}
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/SettingsManager.html b/docs/SettingsManager.html new file mode 100644 index 0000000..56cdc11 --- /dev/null +++ b/docs/SettingsManager.html @@ -0,0 +1,196 @@ + + + + + + SettingsManager - Documentation + + + + + + + + + + + + + + + + + +
+ +

SettingsManager

+ + + + + + + +
+ +
+ +

+ SettingsManager +

+ + +
+ +
+
+ + +
+ + + +

new SettingsManager()

+ + + + + +
+

SettingsManager class for handling user settings and making them persist between sessions. +Logic for WindowSettings is managed here. +"Flags" should follow the same styling as .classList() and should not contain spaces. +A flag should always be false by default. +When a flag is false, it will not exist in the "flags" Array. +(Therefore, "flags" should be [] by default) +If it exists in the "flags" Array, then the flag is true.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
Example
+ +
{
+  "uuid": "497dcba3-ecbf-4587-a2dd-5eb0665e6880",
+  "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]
+}
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Template.html b/docs/Template.html new file mode 100644 index 0000000..86c3cbf --- /dev/null +++ b/docs/Template.html @@ -0,0 +1,178 @@ + + + + + + Template - Documentation + + + + + + + + + + + + + + + + + +
+ +

Template

+ + + + + + + +
+ +
+ +

+ Template +

+ + +
+ +
+
+ + +
+ + + +

new Template()

+ + + + + +
+

An instance of a template. +Handles all mathematics, manipulation, and analysis regarding a single template.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Template.js.html b/docs/Template.js.html new file mode 100644 index 0000000..d5def7f --- /dev/null +++ b/docs/Template.js.html @@ -0,0 +1,458 @@ + + + + + + Template.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

Template.js

+ + + + + + + +
+
+
import { sleep, uint8ToBase64, viewCanvasInNewTab } from "./utils";
+
+/** An instance of a template.
+ * Handles all mathematics, manipulation, and analysis regarding a single template.
+ * @class Template
+ * @since 0.65.2
+ */
+export default class Template {
+
+  /** The constructor for the {@link Template} class with enhanced pixel tracking.
+   * @param {Object} [params={}] - Object containing all optional parameters
+   * @param {string} [params.displayName='My template'] - The display name of the template
+   * @param {number} [params.sortID=0] - The sort number of the template for rendering priority
+   * @param {string} [params.authorID=''] - The user ID of the person who exported the template (prevents sort ID collisions)
+   * @param {string} [params.url=''] - The URL to the source image
+   * @param {File} [params.file=null] - The template file (pre-processed File or processed bitmap)
+   * @param {Array<number, number, number, number>} [params.coords=null] - The coordinates of the top left corner as (tileX, tileY, pixelX, pixelY)
+   * @param {Object} [params.chunked=null] - The affected chunks of the template, and their template for each chunk as a bitmap
+   * @param {Object} [params.chunked32={}] - The affected chunks of the template, and their template for each chunk as a Uint32Array
+   * @param {number} [params.tileSize=1000] - The size of a tile in pixels (assumes square tiles)
+   * @param {Object} [params.pixelCount={total:0, colors:Map}] - Total number of pixels in the template (calculated automatically during processing)
+   * @since 0.65.2
+   */
+  constructor({
+    displayName = 'My template',
+    sortID = 0,
+    authorID = '',
+    url = '',
+    file = null,
+    coords = null,
+    chunked = null,
+    chunked32 = {},
+    tileSize = 1000,
+  } = {}) {
+    this.displayName = displayName;
+    this.sortID = sortID;
+    this.authorID = authorID;
+    this.url = url;
+    this.file = file;
+    this.coords = coords;
+    this.chunked = chunked;
+    this.chunked32 = chunked32;
+    this.tileSize = tileSize;
+    /** Total pixel count in template @type {{total: number, colors: Map<number, number>, correct?: { [key: string]: Map<number, number> }}} */
+    this.pixelCount = { total: 0, colors: new Map() };
+
+    this.shouldSkipTransTiles = true; // Should transparent template tiles be skipped during template creation?
+    this.shouldAggSkipTransTiles = false; // Should transparent template tiles be aggressively skipped during tempalte creation?
+  }
+
+  /** Creates chunks of the template for each tile.
+   * @param {Number} tileSize - Size of the tile as determined by templateManager
+   * @param {Object} paletteBM - An collection of Uint32Arrays containing the palette BM uses
+   * @param {boolean} shouldSkipTransTiles - Should transparent tiles be skipped over when creating the template?
+   * @param {boolean} shouldAggSkipTransTiles - Should transparent tiles be aggressively skipped over when creating the template?
+   * @returns {Object} Collection of template bitmaps & buffers organized by tile coordinates
+   * @since 0.65.4
+   */
+  async createTemplateTiles(tileSize, paletteBM, shouldSkipTransTiles, shouldAggSkipTransTiles) {
+    console.log('Template coordinates:', this.coords);
+
+    // Updates the class instance variable with the new information
+    this.shouldSkipTransTiles = shouldSkipTransTiles;
+    this.shouldAggSkipTransTiles = shouldAggSkipTransTiles;
+
+    const shreadSize = 3; // Scale image factor for pixel art enhancement (must be odd)
+    const bitmap = await createImageBitmap(this.file); // Create efficient bitmap from uploaded file
+    const imageWidth = bitmap.width;
+    const imageHeight = bitmap.height;
+
+    this.tileSize = tileSize; // Tile size predetermined by the templateManager
+
+    const templateTiles = {}; // Holds the template tiles
+    const templateTilesBuffers = {}; // Holds the buffers of the template tiles
+
+    // The main canvas used during template creation
+    const canvas = new OffscreenCanvas(this.tileSize, this.tileSize);
+    const context = canvas.getContext('2d', { willReadFrequently: true });
+
+    // The canvas used to check if a specific template tile is transparent or not
+    const transCanvas = new OffscreenCanvas(this.tileSize, this.tileSize);
+    const transContext = transCanvas.getContext('2d', { willReadFrequently: true });
+
+    // Makes it so that `.drawImage()` calls on the canvas used to calculate transparency always draw below what is already on the canvas
+    transContext.globalCompositeOperation = "destination-over";
+  
+    // Prep the canvas for drawing the entire template (so we can find total pixels)
+    canvas.width = imageWidth;
+    canvas.height = imageHeight;
+    context.imageSmoothingEnabled = false; // Nearest neighbor
+
+    context.drawImage(bitmap, 0, 0); // Draws the template to the canvas
+
+    let timer = Date.now();
+    const totalPixelMap = this.#calculateTotalPixelsFromImageData(context.getImageData(0, 0, imageWidth, imageHeight), paletteBM); // Calculates total pixels from the template buffer retrieved from the canvas context image data
+    console.log(`Calculating total pixels took ${(Date.now() - timer) / 1000.0} seconds`);
+
+    let totalPixels = 0; // Will store the total amount of non-Transparent color pixels
+    const transparentColorID = 0; // Color ID for the Transparent color
+
+    // For each color in the total pixel Map...
+    for (const [color, total] of totalPixelMap) {
+
+      if (color == transparentColorID) {continue;} // Skip Transparent color
+
+      totalPixels += total; // Adds the total amount for the pixel color to the total amount for all colors
+    }
+
+    this.pixelCount = { total: totalPixels, colors: totalPixelMap }; // Stores the total pixel count in the Template instance
+
+    timer = Date.now();
+
+    // Creates a mask where the middle pixel is white, and everything else is transparent
+    const canvasMask = new OffscreenCanvas(3, 3);
+    const contextMask = canvasMask.getContext("2d");
+    contextMask.clearRect(0, 0, 3, 3);
+    contextMask.fillStyle = "white";
+    contextMask.fillRect(1, 1, 1, 1);
+
+    // For every tile...
+    for (let pixelY = this.coords[3]; pixelY < imageHeight + this.coords[3];) {
+
+      // Draws the partial tile first, if any
+      // This calculates the size based on which is smaller:
+      // A. The top left corner of the current tile to the bottom right corner of the current tile
+      // B. The top left corner of the current tile to the bottom right corner of the image
+      const drawSizeY = Math.min(this.tileSize - (pixelY % this.tileSize), imageHeight - (pixelY - this.coords[3]));
+
+      console.log(`Math.min(${this.tileSize} - (${pixelY} % ${this.tileSize}), ${imageHeight} - (${pixelY - this.coords[3]}))`);
+
+      for (let pixelX = this.coords[2]; pixelX < imageWidth + this.coords[2];) {
+
+        console.log(`Pixel X: ${pixelX}\nPixel Y: ${pixelY}`);
+
+        // Draws the partial tile first, if any
+        // This calculates the size based on which is smaller:
+        // A. The top left corner of the current tile to the bottom right corner of the current tile
+        // B. The top left corner of the current tile to the bottom right corner of the image
+        const drawSizeX = Math.min(this.tileSize - (pixelX % this.tileSize), imageWidth - (pixelX - this.coords[2]));
+
+        // If the user wants to skip any tiles where the template is transparent...
+        if (shouldSkipTransTiles) {
+
+          // Detects if the canvas is fully transparent
+          const isTemplateTileTransparent = !this.calculateCanvasTransparency({
+            bitmap: bitmap,
+            bitmapParams: [pixelX - this.coords[2], pixelY - this.coords[3], drawSizeX, drawSizeY], // Top left X, Top left Y, Width, Height
+            transCanvas: transCanvas,
+            transContext: transContext
+          });
+
+          console.log(`Tile contains template: ${!isTemplateTileTransparent}`);
+
+          // If the template in this tile is transparent...
+          if (isTemplateTileTransparent) {
+            pixelX += drawSizeX; // If you remove this, it will get stuck forever processing the template
+            continue; // ...the user does not want to save this tile, so we skip to the next tile
+          }
+        }
+        
+        console.log(`Math.min(${this.tileSize} - (${pixelX} % ${this.tileSize}), ${imageWidth} - (${pixelX - this.coords[2]}))`);
+
+        console.log(`Draw Size X: ${drawSizeX}\nDraw Size Y: ${drawSizeY}`);
+
+        // Change the canvas size and wipe the canvas
+        const canvasWidth = drawSizeX * shreadSize;// + (pixelX % this.tileSize) * shreadSize;
+        const canvasHeight = drawSizeY * shreadSize;// + (pixelY % this.tileSize) * shreadSize;
+        canvas.width = canvasWidth;
+        canvas.height = canvasHeight;
+
+        console.log(`Draw X: ${drawSizeX}\nDraw Y: ${drawSizeY}\nCanvas Width: ${canvasWidth}\nCanvas Height: ${canvasHeight}`);
+
+        context.imageSmoothingEnabled = false; // Nearest neighbor
+
+        console.log(`Getting X ${pixelX}-${pixelX + drawSizeX}\nGetting Y ${pixelY}-${pixelY + drawSizeY}`);
+
+        // Draws the template segment on this tile segment
+        context.clearRect(0, 0, canvasWidth, canvasHeight); // Clear any previous drawing (only runs when canvas size does not change)
+        context.drawImage(
+          bitmap, // Bitmap image to draw
+          pixelX - this.coords[2], // Coordinate X to draw *from*
+          pixelY - this.coords[3], // Coordinate Y to draw *from*
+          drawSizeX, // X width to draw *from*
+          drawSizeY, // Y height to draw *from*
+          0, // Coordinate X to draw *at*
+          0, // Coordinate Y to draw *at*
+          drawSizeX * shreadSize, // X width to draw *at*
+          drawSizeY * shreadSize // Y height to draw *at*
+        ); // Coordinates and size of draw area of source image, then canvas
+
+        context.save(); // Saves the current context of the canvas
+        context.globalCompositeOperation = "destination-in"; // The existing canvas content is kept where both the new shape and existing canvas content overlap. Everything else is made transparent.
+        // For our purposes, this means any non-transparent pixels on the mask will be kept
+
+        console.log(`Should Skip: ${shouldSkipTransTiles}; Should Agg Skip: ${shouldAggSkipTransTiles}`);
+
+        // Fills the canvas with the mask
+        context.fillStyle = context.createPattern(canvasMask, "repeat");
+        context.fillRect(0, 0, canvasWidth, canvasHeight);
+
+        context.restore(); // Restores the context of the canvas to the previous save
+
+        const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight); // Data of the image on the canvas
+
+        console.log(`Shreaded pixels for ${pixelX}, ${pixelY}`, imageData);
+
+        // Creates the "0000,0000,000,000" key name
+        const templateTileName = `${
+          (this.coords[0] + Math.floor(pixelX / 1000)).toString().padStart(4, '0')},${
+          (this.coords[1] + Math.floor(pixelY / 1000)).toString().padStart(4, '0')},${
+          (pixelX % 1000).toString().padStart(3, '0')},${
+          (pixelY % 1000).toString().padStart(3, '0')
+        }`;
+
+        this.chunked32[templateTileName] = new Uint32Array(imageData.data.buffer); // Creates the Uint32Array
+
+        templateTiles[templateTileName] = await createImageBitmap(canvas); // Creates the bitmap
+        
+        const canvasBlob = await canvas.convertToBlob();
+        const canvasBuffer = await canvasBlob.arrayBuffer();
+        const canvasBufferBytes = Array.from(new Uint8Array(canvasBuffer));
+        templateTilesBuffers[templateTileName] = uint8ToBase64(canvasBufferBytes); // Stores the buffer
+
+        console.log(templateTiles);
+
+        pixelX += drawSizeX;
+      }
+
+      pixelY += drawSizeY;
+    }
+
+    console.log(`Parsing template took ${(Date.now() - timer) / 1000.0} seconds`);
+    console.log('Template Tiles: ', templateTiles);
+    console.log('Template Tiles Buffers: ', templateTilesBuffers);
+    console.log('Template Tiles Uint32Array: ', this.chunked32);
+    return { templateTiles, templateTilesBuffers };
+  }
+
+  /** Detects if the canvas is transparent.
+   * @param {Object} param - Object that contains the parameters for the function
+   * @param {ImageBitmap} param.bitmap - The bitmap template image
+   * @param {Array<number, number, number, number>} param.bitmapParams - The parameters to obtain the template tile image from the bitmap
+   * @param {OffscreenCanvas | HTMLCanvasElement} param.transCanvas - The canvas to draw to in order to calculate this
+   * @param {OffscreenCanvasRenderingContext2D} param.transContext - The context for the transparent canvas to draw to
+   * @return {boolean} Is the canvas transparent? If transparent, then `true` is returned. Otherwise, `false`.
+   * @since 0.91.75
+   */
+  calculateCanvasTransparency({
+    bitmap: bitmap,
+    bitmapParams: bitmapParams,
+    transCanvas: transCanvas,
+    transContext: transContext
+  }) {
+
+    console.log(`Calculating template tile transparency...`);
+
+    console.log(`Should Skip: ${this.shouldSkipTransTiles}; Should Agg: ${this.shouldAggSkipTransTiles}`);
+
+    const timer = Date.now(); // Starts the timer
+
+    // Contains the directions to move the canvas when duplicating, in the unit of pixels
+    const duplicationCoordinateArray = [
+      [  0,   1], // E.g. move 0 on the x axis, and 1 down on the y axis
+      [  1,   0],
+      [  0,  -2], // E.g. move 0 on the x axis, and 2 up on the y axis
+      [ -2,   0],
+      [  0,   4],
+      [  4,   0],
+      [  0,  -8],
+      [ -8,   0],
+      [  0,  16],
+      [ 16,   0],
+      [  0, -32],
+      [-32,   0]
+    ];
+
+    // Changes the size of the canvas so that it equals the template tile
+    const transCanvasWidth = bitmapParams[2];
+    const transCanvasHeight = bitmapParams[3];
+    transCanvas.width = transCanvasWidth;
+    transCanvas.height = transCanvasHeight;
+
+    transContext.clearRect(0, 0, transCanvasWidth, transCanvasHeight); // Clear any previous drawing (only runs when canvas size does not change)
+
+    // If the user does want to aggressively skip transparent template tiles...
+    if (this.shouldAggSkipTransTiles) {
+      // (This code will only run if `this.shouldSkipTransTiles` is `true`)
+
+      // Draw the template tile onto the canvas scaled down to 10x10
+      transContext.drawImage(
+        bitmap, // The bitmap image
+        ...bitmapParams, // Bitmap image parameters (x, y, width, height)
+        0, 0, // The coordinate draw the output *at*
+        10, 10 // The width and height of the output
+      );
+    } else {
+      // Else, the user wants to skip transparent template tiles normally...
+
+      // Draw the template tile onto the canvas
+      transContext.drawImage(
+        bitmap, // The bitmap image
+        ...bitmapParams, // Bitmap image parameters (x, y, width, height)
+        0, 0, // The coordinate draw the output *at*
+        transCanvasWidth, transCanvasHeight // Stretch to canvas (the canvas should already be the same size as the template image)
+      )
+
+      // For each canvas duplication...
+      for (const [relativeX, relativeY] of duplicationCoordinateArray) {
+
+        // Duplicate the canvas onto itself, but shifted slightly
+        transContext.drawImage(
+          transCanvas, // The canvas we are drawing to *is* the source image
+          0, 0, transCanvasWidth, transCanvasHeight, // The entire canvas (as a source image)
+          relativeX, relativeY, transCanvasWidth, transCanvasHeight // The output coordinates and size on the same canvas
+        )
+      }
+
+      // Scale down the image to 10x10, and store it between (0, 0) and (9, 9) on the canvas
+      transContext.drawImage(
+        transCanvas, // The canvas we are drawing to *is* the source image
+        0, 0, transCanvasWidth, transCanvasHeight, // The entire canvas (as a source image)
+        0, 0, 10, 10 // The output coordinates and size on the same canvas
+      );
+    }
+
+    const shunkCanvas = transContext.getImageData(0, 0, 10, 10);
+    const shunkCanvas32 = new Uint32Array(shunkCanvas.data.buffer);
+
+    console.log(`Calculated canvas transparency in ${(Date.now() - timer) / 1000} seconds.`);
+
+    // For every pixel in the `shrunkCanvas32` array...
+    for (const pixel of shunkCanvas32) {
+
+      // If the pixel is NOT 100% transparent
+      if (!!pixel) {
+        return true; // Return `true` early since we confirmed a template exists in the tile
+      }
+    }
+
+    return false; // Since we could not confirm any template exists, we assume no template eixsts in this tile
+  }
+
+  /** Calculates top left coordinate of template.
+   * It uses `Template.chunked` to update `Template.coords`
+   * @since 0.88.504
+   */
+  calculateCoordsFromChunked() {
+    let topLeftCoord = [Infinity, Infinity, Infinity, Infinity];
+    const tileKeys = Object.keys(this.chunked).sort(); // Sorts the tile keys
+    tileKeys.forEach((key, index) => { // For each tile key...
+      const [tileX, tileY, pixelX, pixelY] = key.split(',').map(Number); // Deconstruct the tile key
+      if ((tileY < topLeftCoord[1]) || (tileY == topLeftCoord[1] && tileX < topLeftCoord[0])) {
+        topLeftCoord = [tileX, tileY, pixelX, pixelY]; // Record the smallest tile key coordinates. Otherwise, use previous best
+      }
+    });
+    this.coords = topLeftCoord;
+  }
+
+  /** Calculates the total pixels for each color for the image.
+   * 
+   * @param {ImageData} imageData - The pre-shreaded image "casted" onto a canvas
+   * @param {Object} paletteBM - The palette Blue Marble uses for colors
+   * @param {Number} paletteTolerance - How close an RGB color has to be in order to be considered a palette color. A tolerance of "3" means the sum of the RGB can be up to 3 away from the actual value.
+   * @returns {Map<Number, Number>} A map where the key is the color ID, and the value is the total pixels for that color ID
+   * @since 0.88.6
+   */
+  #calculateTotalPixelsFromImageData(imageData, paletteBM) {
+
+    const buffer32Arr = new Uint32Array(imageData.data.buffer); // RGB values as a Uint32Array. Each index represents 1 pixel.
+    const { palette: _, LUT: lookupTable } = paletteBM; // Obtains the palette and LUT
+
+    // Makes a copy of the color palette Blue Marble uses, turns it into a Map, and adds data to count the amount of each color
+    const _colorpalette = new Map(); // Temp color palette
+
+    // For every pixel...
+    for (let pixelIndex = 0; pixelIndex < buffer32Arr.length; pixelIndex++) {
+      
+      const pixel = buffer32Arr[pixelIndex]; // Current pixel to check
+      let bestColorID = -2; // Will eventually store the best match for color ID
+
+      // If the pixel is transparent...
+      if ((pixel >>> 24) == 0) {
+        bestColorID = 0; // Set the color ID to 0
+      } else {
+        // Else, look up the color ID in the "cube" LUT. If none is found, fallback to -2 ("Other")
+        bestColorID = lookupTable.get(pixel) ?? -2;
+      }
+
+      // Increments the count by 1 for the best matching color ID (which can be negative).
+      // If the color ID has not been counted yet, default to 1
+      const colorIDcount = _colorpalette.get(bestColorID);
+      _colorpalette.set(bestColorID, colorIDcount ? colorIDcount + 1 : 1);
+    }
+
+    console.log(_colorpalette);
+    return _colorpalette;
+  }
+}
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/TemplateManager.html b/docs/TemplateManager.html new file mode 100644 index 0000000..911559f --- /dev/null +++ b/docs/TemplateManager.html @@ -0,0 +1,256 @@ + + + + + + TemplateManager - Documentation + + + + + + + + + + + + + + + + + +
+ +

TemplateManager

+ + + + + + + +
+ +
+ +

+ TemplateManager +

+ + +
+ +
+
+ + +
+ + + +

new TemplateManager()

+ + + + + +
+

Manages the template system. +This class handles all external requests for template modification, creation, and analysis. +It serves as the central coordinator between template instances and the user interface.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.55.8
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
Examples
+ +
// JSON structure for a template made in schema version 2.0.0.
+// Note: The pixel "colors" Object contains more than 2 keys.
+// Note: The template tiles are stored as base64 PNG images.
+{
+  "whoami": "BlueMarble",
+  "scriptVersion": "1.13.0",
+  "schemaVersion": "2.0.0",
+  "templates": {
+    "0 $Z": {
+      "name": "My Template",
+      "enabled": true,
+      "pixels": {
+        "total": 40399,
+        "colors": {
+          "-2": 40000,
+          "0": 399
+        }
+      }
+      "tiles": {
+        "1231,0047,183,593": "iVBORw0KGgoAAAANSUhEUgAA",
+        "1231,0048,183,000": "AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+      }
+    },
+    "1 $Z": {
+      "name": "My Template",
+      "URL": "https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/dist/assets/Favicon.png",
+      "URLType": "template",
+      "enabled": false,
+      "pixels": {
+        "total": 40399,
+        "colors": {
+          "-2": 40000,
+          "0": 399
+        }
+      }
+      "tiles": {
+        "375,1846,276,188": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA",
+        "376,1846,000,188": "data:image/png;AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+      }
+    }
+  }
+}
+ +
// JSON structure for a template made in schema version 1.0.0.
+// Note: The template tiles are stored as base64 PNG images.
+{
+  "whoami": "BlueMarble",
+  "scriptVersion": "1.13.0",
+  "schemaVersion": "1.0.0",
+  "templates": {
+    "0 $Z": {
+      "name": "My Template",
+      "enabled": true,
+      "coords": "2000, 230, 45, 201"
+      "palette": {
+        "0,0,0": {
+           "count": 123,
+           "enabled": true
+        },
+        "255,255,255": {
+           "count": 1315,
+           "enabled": false
+        }
+      }
+      "tiles": {
+        "1231,0047,183,593": "iVBORw0KGgoAAAANSUhEUgAA",
+        "1231,0048,183,000": "AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+      }
+    }
+  }
+}
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowCredits.html b/docs/WindowCredits.html new file mode 100644 index 0000000..9139816 --- /dev/null +++ b/docs/WindowCredits.html @@ -0,0 +1,184 @@ + + + + + + WindowCredits - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowCredits

+ + + + + + + +
+ +
+ +

+ WindowCredits +

+ + +
+ +
+
+ + +
+ + + +

new WindowCredits()

+ + + + + +
+

Manages the credits window for Blue Marble.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.9
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowCredits.js.html b/docs/WindowCredits.js.html new file mode 100644 index 0000000..c088b53 --- /dev/null +++ b/docs/WindowCredits.js.html @@ -0,0 +1,178 @@ + + + + + + WindowCredits.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowCredits.js

+ + + + + + + +
+
+
import Overlay from "./Overlay";
+import { localizeDate } from "./utils";
+
+/** Manages the credits window for Blue Marble.
+ * @class WindowCredits
+ * @since 0.90.9
+ * @see {@link Overlay} for examples
+ */
+export default class WindowCredts 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(name, version) {
+    super(name, version); // Executes the code in the Overlay constructor
+    this.window = null; // Contains the *window* DOM tree
+    this.windowID = 'bm-window-credits'; // The ID attribute for this window
+    this.windowParent = document.body; // The parent of the window DOM tree
+  }
+
+  /** 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() {
+
+    // ASCII art of "Blue Marble"
+    const ascii = `
+██████╗ ██╗     ██╗   ██╗███████╗
+██╔══██╗██║     ██║   ██║██╔════╝
+██████╔╝██║     ██║   ██║█████╗  
+██╔══██╗██║     ██║   ██║██╔══╝  
+██████╔╝███████╗╚██████╔╝███████╗
+╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
+
+███╗   ███╗ █████╗ ██████╗ ██████╗ ██╗     ███████╗
+████╗ ████║██╔══██╗██╔══██╗██╔══██╗██║     ██╔════╝
+██╔████╔██║███████║██████╔╝██████╔╝██║     █████╗  
+██║╚██╔╝██║██╔══██║██╔══██╗██╔══██╗██║     ██╔══╝  
+██║ ╚═╝ ██║██║  ██║██║  ██║██████╔╝███████╗███████╗
+╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═════╝ ╚══════╝╚══════╝
+`;
+
+    // If a credits window already exists, close it
+    if (document.querySelector(`#${this.windowID}`)) {
+      document.querySelector(`#${this.windowID}`).remove();
+      return;
+    }
+
+    // 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) => {
+          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
+        .addButton({'class': 'bm-button-circle', 'textContent': '✖', 'aria-label': 'Close window "Credits"'}, (instance, button) => {
+          button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
+          button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
+        }).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 <a href="https://bluemarble.lol/" target="_blank" rel="noopener noreferrer">Blue Marble Website</a> is made by <a href="https://github.com/crqch" target="_blank" rel="noopener noreferrer">crqch</a>.'}).buildElement()
+          .addBr().buildElement()
+          .addSpan({'textContent': `The Blue Marble Website used until ${localizeDate(new Date(1756069320 * 1000))} 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, <a href="https://github.com/TouchedByDarkness" target="_blank" rel="noopener noreferrer">darkness</a> for creating similar userscripts!'}).buildElement()
+            .addLi({'innerHTML': '<a href="https://wondapon.net/" target="_blank" rel="noopener noreferrer">Wonda</a> for the Blue Marble banner image!'}).buildElement()
+            .addLi({'innerHTML': '<a href="https://github.com/BullStein" target="_blank" rel="noopener noreferrer">BullStein</a>, <a href="https://github.com/allanf181" target="_blank" rel="noopener noreferrer">allanf181</a> for being early beta testers!'}).buildElement()
+            .addLi({'innerHTML': 'guidu_ and <a href="https://github.com/Nick-machado" target="_blank" rel="noopener noreferrer">Nick-machado</a> for the original "Minimize" Button code!'}).buildElement()
+            .addLi({'innerHTML': 'Nomad and <a href="https://www.youtube.com/@gustav_vv" target="_blank" rel="noopener noreferrer">Gustav</a> for the tutorials!'}).buildElement()
+            .addLi({'innerHTML': '<a href="https://github.com/cfpwastaken" target="_blank" rel="noopener noreferrer">cfp</a> for creating the template overlay that Blue Marble was based on!'}).buildElement()
+            .addLi({'innerHTML': '<a href="https://forcenetwork.cloud/" target="_blank" rel="noopener noreferrer">Force Network</a> for hosting the <a href="https://github.com/SwingTheVine/Wplace-TelemetryServer" target="_blank" rel="noopener noreferrer">telemetry server</a>!'}).buildElement()
+            .addLi({'innerHTML': '<a href="https://thebluecorner.net" target="_blank" rel="noopener noreferrer">TheBlueCorner</a> for getting me interested in online pixel canvases!'}).buildElement()
+          .buildElement()
+          .addBr().buildElement()
+          .addSpan({'innerHTML': '<a href="https://ko-fi.com/swingthevine" target="_blank" rel="noopener noreferrer">Donators</a>:'}).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);
+
+    // Creates dragging capability on the drag bar for dragging the window
+    this.handleDrag(`#${this.windowID}.bm-window`, `#${this.windowID} .bm-dragbar`);
+  }
+}
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/WindowFilter.html b/docs/WindowFilter.html new file mode 100644 index 0000000..d495d93 --- /dev/null +++ b/docs/WindowFilter.html @@ -0,0 +1,184 @@ + + + + + + WindowFilter - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowFilter

+ + + + + + + +
+ +
+ +

+ WindowFilter +

+ + +
+ +
+
+ + +
+ + + +

new WindowFilter()

+ + + + + +
+

This class handles the overlay UI for the color filter window of the Blue Marble userscript.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.329
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowFilter.js.html b/docs/WindowFilter.js.html new file mode 100644 index 0000000..5c81432 --- /dev/null +++ b/docs/WindowFilter.js.html @@ -0,0 +1,767 @@ + + + + + + WindowFilter.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowFilter.js

+ + + + + + + +
+
+
import ConfettiManager from "./confetttiManager";
+import Overlay from "./Overlay";
+import { calculateRelativeLuminance, localizeDate, localizeNumber, localizePercent, rgbToHex } from "./utils";
+
+/** 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
+ * @since 0.88.329
+ * @see {@link Overlay} for examples
+ */
+export default class WindowFilter extends Overlay {
+
+  /** Constructor for the color filter window
+   * @param {*} executor - The executing class
+   * @since 0.88.329
+   * @see {@link Overlay#constructor}
+   */
+  constructor(executor) {
+    super(executor.name, executor.version); // Executes the code in the Overlay constructor
+    this.window = null; // Contains the *window* DOM tree
+    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
+
+    /** The templateManager instance currently being used. @type {TemplateManager} */
+    this.templateManager = executor.apiManager?.templateManager;
+
+    // Eye icons
+    this.eyeOpen = '<svg viewBox="0 .5 6 3"><path d="M0,2Q3-1 6,2Q3,5 0,2H2A1,1 0 1 0 3,1Q3,2 2,2"/></svg>';
+    this.eyeClosed = '<svg viewBox="0 1 12 6"><mask id="a"><path d="M0,0H12V8L0,2" fill="#fff"/></mask><path d="M0,4Q6-2 12,4Q6,10 0,4H4A2,2 0 1 0 6,2Q6,4 4,4ZM1,2L10,6.5L9.5,7L.5,2.5" mask="url(#a)"/></svg>';
+
+    // Obtains the color palette Blue Marble currently uses
+    const { palette: palette, LUT: _ } = this.templateManager.paletteBM;
+    this.palette = palette;
+
+    // Tile quantity information
+    this.tilesLoadedTotal = 0; // Number of tiles that have been loaded in this session
+    this.tilesTotal = 0; // Number of tiles total, across all templates
+
+    // Pixel statistics
+    this.allPixelsColor = new Map(); // The amount of pixels total per color as a Map
+    this.allPixelsCorrect = new Map(); // The amount of correct pixels per color as a Map
+    this.allPixelsCorrectTotal = 0; // Sums the pixels placed as "correct" per everything
+    this.allPixelsTotal = 0; // Sums the pixels placed as "total" per everything
+    this.timeRemaining = 0; // Calculates the date & time the user will complete the templates
+    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.showUnused = false; // Were unused colors shown the last time the user sorted the color list?
+  }
+
+  /** 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.
+   * @since 0.88.149
+   */
+  buildWindow() {
+
+    // If a color filter wizard window already exists, close it
+    if (document.querySelector(`#${this.windowID}`)) {
+      document.querySelector(`#${this.windowID}`).remove();
+      return;
+    }
+    
+    // Creates a new color filter window
+    this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window'}, (instance, div) => {
+      // div.onclick = (event) => {
+      //   if (event.target.closest('button, a, input, select')) {return;} // Exit-early if interactive child was clicked
+      //   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) => {
+          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) => {
+            button.onclick = () => {
+              document.querySelector(`#${this.windowID}`)?.remove();
+              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();};
+            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'})
+          .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) => {
+            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) => {
+            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'})
+              .addSpan({'id': 'bm-filter-tile-load', 'innerHTML': '<b>Tiles Loaded:</b> 0 / ???'}).buildElement()
+              .addBr().buildElement()
+              .addSpan({'id': 'bm-filter-tot-correct', 'innerHTML': '<b>Correct Pixels:</b> ???'}).buildElement()
+              .addBr().buildElement()
+              .addSpan({'id': 'bm-filter-tot-total', 'innerHTML': '<b>Total Pixels:</b> ???'}).buildElement()
+              .addBr().buildElement()
+              .addSpan({'id': 'bm-filter-tot-remaining', 'innerHTML': '<b>Complete:</b> ??? (???)'}).buildElement()
+              .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('<svg', '<svg aria-label="Eye Open"')} will be shown on the canvas. Colors with the icon ${this.eyeClosed.replace('<svg', '<svg aria-label="Eye Closed"')} will not be shown on the canvas. The "Hide All Colors" and "Show All Colors" buttons only apply to colors that display in the list below. The amount of correct pixels is dependent on how many tiles of the template you have loaded since you last opened Wplace.live. If all tiles have been loaded, then the "correct pixel" count is accurate.`}).buildElement()
+            .buildElement()
+            .addHr().buildElement()
+            .addForm({'class': 'bm-container'})
+              .addFieldset()
+                .addLegend({'textContent': 'Sort Options:', 'style': 'font-weight: 700;'}).buildElement()
+                .addDiv({'class': 'bm-container'})
+                  .addSelect({'id': 'bm-filter-sort-primary', 'name': 'sortPrimary', 'textContent': 'I want to view '})
+                    .addOption({'value': 'id', 'textContent': 'color IDs'}).buildElement()
+                    .addOption({'value': 'name', 'textContent': 'color names'}).buildElement()
+                    .addOption({'value': 'premium', 'textContent': 'premium colors'}).buildElement()
+                    .addOption({'value': 'percent', 'textContent': 'percentage'}).buildElement()
+                    .addOption({'value': 'correct', 'textContent': 'correct pixels'}).buildElement()
+                    .addOption({'value': 'incorrect', 'textContent': 'incorrect pixels'}).buildElement()
+                    .addOption({'value': 'total', 'textContent': 'total pixels'}).buildElement()
+                  .buildElement()
+                  .addSelect({'id': 'bm-filter-sort-secondary', 'name': 'sortSecondary', 'textContent': ' in '})
+                    .addOption({'value': 'ascending', 'textContent': 'ascending'}).buildElement()
+                    .addOption({'value': 'descending', 'textContent': 'descending'}).buildElement()
+                  .buildElement()
+                  .addSpan({'textContent': ' order.'}).buildElement()
+                .buildElement()
+                .addDiv({'class': 'bm-container'})
+                  .addCheckbox({'id': 'bm-filter-show-unused', 'name': 'showUnused', 'textContent': 'Show unused colors'}).buildElement()
+                .buildElement()
+              .buildElement()
+              .addDiv({'class': 'bm-container'})
+                .addButton({'textContent': 'Sort Colors', 'type': 'submit'}, (instance, button) => {
+                  button.onclick = (event) => {
+                    event.preventDefault(); // Stop default form submission
+
+                    // Get the form data
+                    const formData = new FormData(document.querySelector(`#${this.windowID} form`));
+                    const formValues = {};
+                    for (const [input, value] of formData) {
+                      formValues[input] = value;
+                    }
+                    console.log(`Primary: ${formValues['sortPrimary']}; Secondary: ${formValues['sortSecondary']}; Unused: ${formValues['showUnused'] == 'on'}`);
+                    
+                    // Sort the color list
+                    this.#sortColorList(formValues['sortPrimary'], formValues['sortSecondary'], formValues['showUnused'] == 'on');
+                  }
+                }).buildElement()
+              .buildElement()
+            .buildElement()
+          .buildElement()
+          // Color list will appear here in the DOM tree
+        .buildElement()
+      .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`);
+
+    // 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.#sortColorList(this.sortPrimary, this.sortSecondary, this.showUnused);
+
+    // Displays some template statistics to the user
+    this.updateInnerHTML('#bm-filter-tile-load', `<b>Tiles Loaded:</b> ${localizeNumber(this.tilesLoadedTotal)} / ${localizeNumber(this.tilesTotal)}`);
+    this.updateInnerHTML('#bm-filter-tot-correct', `<b>Correct Pixels:</b> ${localizeNumber(this.allPixelsCorrectTotal)}`);
+    this.updateInnerHTML('#bm-filter-tot-total', `<b>Total Pixels:</b> ${localizeNumber(this.allPixelsTotal)}`);
+    this.updateInnerHTML('#bm-filter-tot-remaining', `<b>Remaining:</b> ${localizeNumber((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0))} (${localizePercent(((this.allPixelsTotal || 0) - (this.allPixelsCorrectTotal || 0)) / (this.allPixelsTotal || 1))})`);
+    this.updateInnerHTML('#bm-filter-tot-completed', `<b>Completed at:</b> <time datetime="${this.timeRemaining.toISOString().replace(/\.\d{3}Z$/, 'Z')}">${this.timeRemainingLocalized}</time>`);
+  }
+
+  /** Spawns a windowed 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.
+   * @since 0.90.35
+   */
+  buildWindowed() {
+
+    // If a color filter wizard window already exists, close it
+    if (document.querySelector(`#${this.windowID}`)) {
+      document.querySelector(`#${this.windowID}`).remove();
+      return;
+    }
+
+    // Creates a new windowed color filter window
+    this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window bm-windowed'})
+      .addDragbar()
+        .addButton({'class': 'bm-button-circle', 'textContent': '▼', '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) {
+              windowedColorTotals.style.display = (button.dataset['buttonStatus'] == 'expanded') ? 'none' : '';
+            }
+            instance.handleMinimization(button);
+          };
+          button.ontouchend = () => {button.click()}; // Needed only to negate weird interaction with dragbar
+        }).buildElement()
+        .addDiv()
+          .addSpan({'id': 'bm-filter-windowed-color-totals', 'class': 'bm-dragbar-text', 'style': 'font-weight: 700;'}).buildElement() // Contains correct / total pixel values
+          // 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) => {
+            button.onclick = () => {
+              document.querySelector(`#${this.windowID}`)?.remove();
+              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();};
+            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'})
+          .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) => {
+            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) => {
+            button.onclick = () => this.#selectColorList(true);
+          }).buildElement()
+        .buildElement()
+        .addDiv({'class': 'bm-container bm-scrollable'})
+          // Color list will appear here
+        .buildElement()
+      .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`);
+
+    // 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.#sortColorList(this.sortPrimary, this.sortSecondary, this.showUnused);
+  }
+
+  /** Creates the color list container.
+   * @param {HTMLElement} parentElement - Parent element to add the color list to as a child
+   * @since 0.88.222
+   */
+  #buildColorList(parentElement) {
+
+    // Figures out if this window is fullscreen or windowed mode
+    const isWindowedMode = parentElement.closest(`#${this.windowID}`)?.classList.contains('bm-windowed');
+    // Note: `undefined` is expected to behave as if `false`
+    
+    console.log(`Is Windowed Mode: ${isWindowedMode}`);
+
+    const colorList = new Overlay(this.name, this.version);
+    colorList.addDiv({'id': this.colorListID})
+    // We leave it open so we can add children to the grid
+
+    // Generated by #updateColorList()
+    const colorStatistics = this.updateColorList();
+
+    // For each color in the palette...
+    for (const color of this.palette) {
+
+      // Converts the RGB color to hexdecimal
+      const colorValueHex = '#' + rgbToHex(color.rgb).toUpperCase();
+
+      // Relative Luminance
+      const lumin = calculateRelativeLuminance(color.rgb);
+
+      // Calculates if white or black text would contrast better with the palette color
+      let textColorForPaletteColorBackground = 
+      (((1.05) / (lumin + 0.05)) > ((lumin + 0.05) / 0.05)) 
+      ? 'white' : 'black';
+
+      // However, if the color is "Transparent" (or there is no color ID), then we make the text color transparent
+      if (!color.id) {
+        textColorForPaletteColorBackground = 'transparent';
+      }
+
+      // Changes the luminance of the hover/focus button effect
+      const bgEffectForButtons = (textColorForPaletteColorBackground == 'white') ? 'bm-button-hover-white' : 'bm-button-hover-black';
+
+      // Generated by #updateColorList()
+      const {
+        colorCorrect: colorCorrect,
+        colorCorrectLocalized: colorCorrectLocalized,
+        colorPercent: colorPercent,
+        colorTotal: colorTotal,
+        colorTotalLocalized: colorTotalLocalized,
+        colorIncorrect: colorIncorrect
+      } = colorStatistics[color.id];
+
+      const isColorHidden = !!(this.templateManager.shouldFilterColor.get(color.id) || false);
+
+      // Add the color to the color list DOM
+      if (isWindowedMode) {
+
+        // The star pattern for premium colors
+        const styleBackgroundStar = `background-size: auto 100%; background-repeat: repeat-x; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path d='M50,5L79,91L2,39L98,39L21,91' fill='${textColorForPaletteColorBackground}' fill-opacity='.1'/></svg>");`;
+
+        // Add windowed mode color DOM to color list
+        colorList.addDiv({'class': 'bm-container bm-filter-color bm-flex-between',
+          // Dataset
+          'data-id': color.id,
+          'data-name': color.name,
+          'data-premium': +color.premium,
+          'data-correct': !Number.isNaN(parseInt(colorCorrect)) ? colorCorrect : '0',
+          'data-total': colorTotal,
+          'data-percent': (colorPercent.slice(-1) == '%') ? colorPercent.slice(0, -1) : '0',
+          'data-incorrect': colorIncorrect || 0
+        }).addDiv({'class': 'bm-filter-container-rgb', 'style': `background-color: rgb(${color.rgb?.map(channel => Number(channel) || 0).join(',')});${color.premium ? styleBackgroundStar : ''}`})
+            .addButton({
+              '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('<svg', `<svg fill="${textColorForPaletteColorBackground}"`) : this.eyeOpen.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`)},
+              (instance, button) => {
+
+                // When the button is clicked
+                button.onclick = () => {
+                  button.style.textDecoration = 'none';
+                  button.disabled = true;
+                  if (button.dataset['state'] == 'shown') {
+                    button.innerHTML = this.eyeClosed.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`);
+                    button.dataset['state'] = 'hidden';
+                    button.ariaLabel = `Show the color ${color.name || ''} on templates.`;
+                    this.templateManager.shouldFilterColor.set(color.id, true);
+                  } else {
+                    button.innerHTML = this.eyeOpen.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`);
+                    button.dataset['state'] = 'shown';
+                    button.ariaLabel = `Hide the color ${color.name || ''} on templates.`;
+                    this.templateManager.shouldFilterColor.delete(color.id);
+                  }
+                  button.disabled = false;
+                  button.style.textDecoration = '';
+                }
+
+                // Disables the "hide color" button if the color is "Transparent" (or no ID exists)
+                if (!color.id) {button.disabled = true;}
+              }
+            ).buildElement()
+            .addSmall({'textContent': `#${color.id.toString().padStart(2, 0)}`, 'style': `color: ${((color.id == -1) || (color.id == 0)) ? 'white' : textColorForPaletteColorBackground}`}).buildElement()
+            .addHeader(2, {'textContent': color.name, 'style': `color: ${((color.id == -1) || (color.id == 0)) ? 'white' : textColorForPaletteColorBackground}`}).buildElement()
+            .addSmall({'class': 'bm-filter-color-pxl-cnt', 'textContent': `${colorCorrectLocalized} / ${colorTotalLocalized}`, 'style': `color: ${((color.id == -1) || (color.id == 0)) ? 'white' : textColorForPaletteColorBackground}; flex: 1 1 auto; text-align: right;`}).buildElement()
+          .buildElement()
+        .buildElement();
+      } else {
+        // Else we are in fullscreen mode.
+
+        // Add fullscreen mode color DOM to color list
+        colorList.addDiv({'class': 'bm-container bm-filter-color bm-flex-between',
+          'data-id': color.id,
+          'data-name': color.name,
+          'data-premium': +color.premium,
+          'data-correct': !Number.isNaN(parseInt(colorCorrect)) ? colorCorrect : '0',
+          'data-total': colorTotal,
+          'data-percent': (colorPercent.slice(-1) == '%') ? colorPercent.slice(0, -1) : '0',
+          'data-incorrect': colorIncorrect || 0
+        }).addDiv({'class': 'bm-flex-center', 'style': 'flex-direction: column;'})
+            .addDiv({'class': 'bm-filter-container-rgb', 'style': `background-color: rgb(${color.rgb?.map(channel => Number(channel) || 0).join(',')});`})
+              .addButton({
+                '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('<svg', `<svg fill="${textColorForPaletteColorBackground}"`) : this.eyeOpen.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`)},
+                (instance, button) => {
+
+                  // When the button is clicked
+                  button.onclick = () => {
+                    button.style.textDecoration = 'none';
+                    button.disabled = true;
+                    if (button.dataset['state'] == 'shown') {
+                      button.innerHTML = this.eyeClosed.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`);
+                      button.dataset['state'] = 'hidden';
+                      button.ariaLabel = `Show the color ${color.name || ''} on templates.`;
+                      this.templateManager.shouldFilterColor.set(color.id, true);
+                    } else {
+                      button.innerHTML = this.eyeOpen.replace('<svg', `<svg fill="${textColorForPaletteColorBackground}"`);
+                      button.dataset['state'] = 'shown';
+                      button.ariaLabel = `Hide the color ${color.name || ''} on templates.`;
+                      this.templateManager.shouldFilterColor.delete(color.id);
+                    }
+                    button.disabled = false;
+                    button.style.textDecoration = '';
+                  }
+
+                  // Disables the "hide color" button if the color is "Transparent" (or no ID exists)
+                  if (!color.id) {button.disabled = true;}
+                }
+              ).buildElement()
+            .buildElement()
+            .addSmall({'textContent': (color.id == -2) ? '???????' : colorValueHex}).buildElement()
+          .buildElement()
+          .addDiv({'class': 'bm-flex-between'})
+            .addHeader(2, {'textContent': (color.premium ? '★ ' : '') + color.name}).buildElement()
+            .addDiv({'class': 'bm-flex-between', 'style': 'gap: 1.5ch;'})
+              .addSmall({'textContent': `#${color.id.toString().padStart(2, 0)}`}).buildElement()
+              .addSmall({'class': 'bm-filter-color-pxl-cnt', 'textContent': `${colorCorrectLocalized} / ${colorTotalLocalized}`}).buildElement()
+            .buildElement()
+            .addP({'class': 'bm-filter-color-pxl-desc', 'textContent': `${((typeof colorIncorrect == 'number') && !isNaN(colorIncorrect)) ? colorIncorrect : '???'} incorrect pixel${(colorIncorrect == 1) ? '' : 's'}. Completed: ${colorPercent}`}).buildElement()
+          .buildElement()
+        .buildElement();
+      }
+    }
+
+    // Adds the colors to the color container in the filter window
+    colorList.buildOverlay(parentElement);
+  }
+
+  /** Sorts the color list & hides unused colors
+   * @param {string} sortPrimary - The name of the dataset attribute to sort by.
+   * @param {string} sortSecondary - Secondary sort. It can be either 'ascending' or 'descending'.
+   * @param {boolean} showUnused - Should unused colors be displayed in the list to the user?
+   * @since 0.88.222
+   */
+  #sortColorList(sortPrimary, sortSecondary, showUnused) {
+
+    // Update memorised sort settings
+    this.sortPrimary = sortPrimary;
+    this.sortSecondary = sortSecondary;
+    this.showUnused = showUnused;
+
+    const colorList = document.querySelector(`#${this.colorListID}`);
+
+    const colors = Array.from(colorList.children);
+
+    colors.sort((index, nextIndex) => {
+      const indexValue = index.getAttribute('data-' + sortPrimary);
+      const nextIndexValue = nextIndex.getAttribute('data-' + sortPrimary);
+
+      const indexValueNumber = parseFloat(indexValue);
+      const nextIndexValueNumber = parseFloat(nextIndexValue);
+
+      const indexValueNumberIsNumber = !isNaN(indexValueNumber);
+      const nextIndexValueNumberIsNumber = !isNaN(nextIndexValueNumber);
+
+      // If the user wants to show unused colors...
+      if (showUnused) {
+        index.classList.remove('bm-color-hide'); // Show the color
+      } else if (!Number(index.getAttribute('data-total'))) {
+        // ...else if the user wants to hide unused colors, and this color is unused...
+        
+        index.classList.add('bm-color-hide'); // Hide the color
+      }
+
+      // If both index values are numbers...
+      if (indexValueNumberIsNumber && nextIndexValueNumberIsNumber) {
+        // Perform numeric comparison
+        return sortSecondary === 'ascending' ? indexValueNumber - nextIndexValueNumber : nextIndexValueNumber - indexValueNumber;
+      } else {
+        // Otherwise, perform string comparison
+        const indexValueString = indexValue.toLowerCase();
+        const nextIndexValueString = nextIndexValue.toLowerCase();
+        if (indexValueString < nextIndexValueString) return sortSecondary === 'ascending' ? -1 : 1;
+        if (indexValueString > nextIndexValueString) return sortSecondary === 'ascending' ? 1 : -1;
+        return 0;
+      }
+    });
+
+    colors.forEach(color => colorList.appendChild(color));
+  }
+
+  /** (Un)selects all colors in the color list that are visible to the user.
+   * @param {boolean} userWantsUnselect - Does the user want to unselect colors?
+   * @since 0.88.222
+   */
+  #selectColorList(userWantsUnselect) {
+
+    // Gets the colors
+    const colorList = document.querySelector(`#${this.colorListID}`);
+    const colors = Array.from(colorList.children);
+
+    // For each color...
+    for (const color of colors) {
+
+      // Skip this color if it is hidden
+      if (color.classList?.contains('bm-color-hide')) {continue;}
+
+      // Gets the button to click
+      const button = color.querySelector('.bm-filter-container-rgb button');
+      
+      // Exits early if the button is in its proper state
+      if ((button.dataset['state'] == 'hidden') && !userWantsUnselect) {continue;} // If the button is selected, and the user wants to select all buttons, then skip this one
+      if ((button.dataset['state'] == 'shown') && userWantsUnselect) {continue;} // If the button is not selected, and the user wants to unselect all buttons, then skip this one
+      
+      button.click(); // If the button is not in its proper state, then we click it
+    }
+  }
+
+  /** The information about a specific color on the palette.
+   * @typedef {Object} ColorData
+   * @property {number | string} colorTotal
+   * @property {string} colorTotalLocalized
+   * @property {number | string} colorCorrect
+   * @property {string} colorCorrectLocalized
+   * @property {string} colorPercent
+   * @property {number} colorIncorrect
+   */
+
+  /** Updates the information inside the colors in the color list.
+   * If the color list does not exist yet, it returns the color information instead.
+   * This assumes the information inside each element is the same between fullscreen and windowed mode.
+   * @since 0.90.60
+   * @returns {Object.<number, ColorData>}
+   */
+  updateColorList() {
+
+    this.#calculatePixelStatistics(); // Updates the pixel statistics in the class instance variables
+
+    const colorList = document.querySelector(`#${this.colorListID}`);
+
+    const colorStatistics = {};
+
+    // For each color...
+    for (const color of this.palette) {
+
+      // Turns "total" color into a string of a number; "0" if unknown
+      const colorTotal = this.allPixelsColor.get(color.id) ?? 0
+      const colorTotalLocalized = localizeNumber(colorTotal);
+      
+      // This will be displayed if the total pixels for this color is zero
+      let colorCorrect = 0;
+      let colorCorrectLocalized = '0';
+      let colorPercent = localizePercent(1);
+
+      // This will be displayed if the total pixels for this color is non-zero
+      if (colorTotal != 0) {
+
+        // Determines the correct pixels, or the proper fallback
+        colorCorrect = this.allPixelsCorrect.get(color.id) ?? '???';
+        if ((typeof colorCorrect != 'number') && (this.tilesLoadedTotal == this.tilesTotal) && !!color.id) {
+          colorCorrect = 0;
+        }
+
+        colorCorrectLocalized = (typeof colorCorrect == 'string') ? colorCorrect : localizeNumber(colorCorrect);
+        colorPercent = isNaN(colorCorrect / colorTotal) ? '???' : localizePercent(colorCorrect / colorTotal);
+      }
+      // There are four outcomes:
+      // 1. The correct pixel count is displayed, because there are correct pixels.
+      // 2. There are NO correct pixels, and the color is not transparent, but since all tiles are loaded, we know that the correct pixel count is actually 0.
+      // 3. There are NO correct pixels, and the color is not transparent, and not all tiles are loaded. We don't know if there are correct pixels or not, so we display "???" instead.
+      // 4. There are NO correct pixels, and the color is transparent, so we display '???' because tracking the "Transparent" color is currently disabled.
+
+      // Incorrect pixels for this color
+      const colorIncorrect = parseInt(colorTotal) - parseInt(colorCorrect);
+
+      colorStatistics[color.id] = {
+        colorTotal: colorTotal,
+        colorTotalLocalized: colorTotalLocalized,
+        colorCorrect: colorCorrect,
+        colorCorrectLocalized: colorCorrectLocalized,
+        colorPercent: colorPercent,
+        colorIncorrect: colorIncorrect
+      }
+    }
+
+    // Obtains the correct / total pixels display element, or `undefined` if in fullscreen mode
+    const windowedColorTotals = document.querySelector('#bm-filter-windowed-color-totals');
+
+    // If the element exists...
+    if (windowedColorTotals) {
+
+      // Returns the number, unlocalized (no space to localize)
+      // OR returns the three characters on either end of the string, with the middle replaced with an ellipse.
+      // E.g. '1234567' or '123…678'
+      const allCorrect = (this.allPixelsCorrectTotal.toString().length > 7) ? this.allPixelsCorrectTotal.toString().slice(0, 2) + '…' + this.allPixelsCorrectTotal.toString().slice(-3) : this.allPixelsCorrectTotal.toString();
+      const allTotal = (this.allPixelsTotal.toString().length > 7) ? this.allPixelsTotal.toString().slice(0, 2) + '…' + this.allPixelsTotal.toString().slice(-3) : this.allPixelsTotal.toString();
+
+      // Updates the display with XSS protection enabled (because why not)
+      this.updateInnerHTML('#bm-filter-windowed-color-totals', `${allCorrect}/${allTotal}`, true);
+    }
+
+    // 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;}
+
+    const colors = Array.from(colorList.children);
+
+    // For each color...
+    for (const color of colors) {
+
+      const colorID = parseInt(color.dataset['id']);
+
+      // Obtains the data to update then
+      const {
+        colorCorrect: colorCorrect,
+        colorCorrectLocalized: colorCorrectLocalized,
+        colorPercent: colorPercent,
+        colorTotal: colorTotal,
+        colorTotalLocalized: colorTotalLocalized,
+        colorIncorrect: colorIncorrect
+      } = colorStatistics[colorID];
+
+      // Update the dataset
+      color.dataset['correct'] = !Number.isNaN(parseInt(colorCorrect)) ? colorCorrect : '0';
+      color.dataset['total'] = colorTotal;
+      color.dataset['percent'] = (colorPercent.slice(-1) == '%') ? colorPercent.slice(0, -1) : '0';
+      color.dataset['incorrect'] = colorIncorrect || 0;
+
+      // Updates the pixel count if it exists
+      const pixelCount = document.querySelector(`#${this.windowID} .bm-filter-color[data-id="${colorID}"] .bm-filter-color-pxl-cnt`);
+      if (pixelCount) {pixelCount.textContent = `${colorCorrectLocalized} / ${colorTotalLocalized}`;}
+
+      // Updates the pixel description if it exists
+      const pixelDesc = document.querySelector(`#${this.windowID} .bm-filter-color[data-id="${colorID}"] .bm-filter-color-pxl-desc`);
+      if (pixelDesc) {pixelDesc.textContent = `${((typeof colorIncorrect == 'number') && !isNaN(colorIncorrect)) ? colorIncorrect : '???'} incorrect pixel${(colorIncorrect == 1) ? '' : 's'}. Completed: ${colorPercent}`;}
+    }
+
+    // Since the dataset has changed, we need to sort again
+    // Because if the user wants to sort by pixel count, the order should change
+    this.#sortColorList(this.sortPrimary, this.sortSecondary, this.showUnused);
+  }
+
+  /** Calculates all pixel statistics used in the color filter.
+   * @since 0.90.34
+   */
+  #calculatePixelStatistics() {
+
+    // Resets pixel totals to 0
+    this.allPixelsTotal = 0;
+    this.allPixelsCorrectTotal = 0;
+    this.allPixelsCorrect = new Map();
+    this.allPixelsColor = new Map();
+
+    // Sum the pixel totals across all templates.
+    // If there is no total for a template, it defaults to zero
+    for (const template of this.templateManager.templatesArray) {
+
+      const total = template.pixelCount?.total ?? 0;
+      this.allPixelsTotal += total ?? 0; // Sums the pixels placed as "total" per everything
+
+      const colors = template.pixelCount?.colors ?? new Map();
+
+      // Sums the color pixels placed as "total" per color ID
+      for (const [colorID, colorPixels] of colors) {
+        const _colorPixels = Number(colorPixels) || 0; // Boilerplate
+        const allPixelsColorSoFar = this.allPixelsColor.get(colorID) ?? 0; // The total color pixels for this color ID so far, or zero if none counted so far
+        this.allPixelsColor.set(colorID, allPixelsColorSoFar + _colorPixels);
+      }
+
+      // Object that contains the tiles which contain Maps as correct pixels per tile as the value in the key-value pair
+      const correctObject = template.pixelCount?.correct ?? {};
+
+      this.tilesLoadedTotal += Object.keys(correctObject).length; // Sums the total loaded tiles per template
+      this.tilesTotal += Object.keys(template.chunked).length; // Sums the total tiles per template
+
+      // Sums the pixels placed as "correct" per color ID
+      for (const map of Object.values(correctObject)) { // Per (loaded) tile per template
+        for (const [colorID, correctPixels] of map) { // Per color per (loaded) tile per template
+          const _correctPixels = Number(correctPixels) || 0; // Boilerplate
+          this.allPixelsCorrectTotal += _correctPixels; // Sums the pixels placed as "correct" per everything
+          const allPixelsCorrectSoFar = this.allPixelsCorrect.get(colorID) ?? 0; // The total correct pixels for this color ID so far, or zero if none counted so far
+          this.allPixelsCorrect.set(colorID, allPixelsCorrectSoFar + _correctPixels);
+        }
+      }
+    }
+
+    console.log(`Tiles loaded: ${this.tilesLoadedTotal} / ${this.tilesTotal}`);
+
+    // If the template is complete, and the pixel count is non-zero, and at least 1 template exists, and all template tiles have been loaded this session...
+    if ((this.allPixelsCorrectTotal >= this.allPixelsTotal) && !!this.allPixelsTotal && (this.tilesLoadedTotal == this.tilesTotal)) {
+      // Basically, only run if Blue Marble can confirm with 100% certanty that all (>0) templates are complete.
+      
+      // Create confetti in the color filter window
+      const confettiManager = new ConfettiManager();
+      confettiManager.createConfetti(document.querySelector(`#${this.windowID}`));
+    }
+
+    // Calculates the date & time the user will complete the templates
+    this.timeRemaining = new Date(((this.allPixelsTotal - this.allPixelsCorrectTotal) * 30 * 1000) + Date.now());
+    this.timeRemainingLocalized = localizeDate(this.timeRemaining);
+  }
+}
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/WindowMain.html b/docs/WindowMain.html new file mode 100644 index 0000000..34152ec --- /dev/null +++ b/docs/WindowMain.html @@ -0,0 +1,184 @@ + + + + + + WindowMain - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowMain

+ + + + + + + +
+ +
+ +

+ WindowMain +

+ + +
+ +
+
+ + +
+ + + +

new WindowMain()

+ + + + + +
+

This class handles the overlay UI for the main window of the Blue Marble userscript.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.326
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowMain.js.html b/docs/WindowMain.js.html new file mode 100644 index 0000000..816731e --- /dev/null +++ b/docs/WindowMain.js.html @@ -0,0 +1,316 @@ + + + + + + WindowMain.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowMain.js

+ + + + + + + +
+
+
import ConfettiManager from "./confetttiManager";
+import Overlay from "./Overlay";
+import { getClipboardData } from "./utils";
+import WindowCredts from "./WindowCredits";
+import WindowFilter from "./WindowFilter";
+import WindowWizard from "./WindowWizard";
+
+/** 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.
+ * @class WindowMain
+ * @since 0.88.326
+ * @see {@link Overlay} for examples
+ */
+export default class WindowMain 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(name, version) {
+    super(name, version); // Executes the code in the Overlay constructor
+    this.window = null; // Contains the *window* DOM tree
+    this.windowID = 'bm-window-main'; // The ID attribute for this window
+    this.windowParent = document.body; // The parent of the window DOM tree
+  }
+
+  /** Creates the main Blue Marble window.
+   * Parent/child relationships in the DOM structure below are indicated by indentation.
+   * @since 0.58.3
+   */
+  buildWindow() {
+
+    // If the main window already exists, throw an error and return early
+    if (document.querySelector(`#${this.windowID}`)) {
+      this.handleDisplayError('Main window already exists!');
+      return;
+    }
+
+    // Creates the window
+    this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window bm-windowed', 'style': 'top: 10px; left: unset; right: 75px;'}, (instance, div) => {
+      // div.onclick = (event) => {
+      //   if (event.target.closest('button, a, input, select')) {return;} // Exit-early if interactive child was clicked
+      //   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) => {
+          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
+      .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) => {
+            // Adds a birthday hat & confetti to the window if it is Blue Marble's birthday
+            const date = new Date();
+            const dayOfTheYear = Math.floor((date.getTime() - new Date(date.getFullYear(), 0, 1)) / (1000 * 60 * 60 * 24)) + 1;
+            if (dayOfTheYear == 204) {
+              img.parentNode.style.position = 'relative';
+              img.parentNode.innerHTML = img.parentNode.innerHTML + `<svg viewBox="0 0 9 7" width="2em" height="2em" style="position: absolute; top: -.75em; left: 3.25ch;"><path d="M0,3L9,0L2,7" fill="#0af"/><path d="M0,3A.4,.4 0 1 1 1,5" fill="#a00"/><path d="M1.5,6A1,1 0 0 1 3,6L2,7" fill="#a0f"/><path d="M4,5A.6,.6 0 1 1 5,4" fill="#0a0"/><path d="M6,3A.8,.8 0 1 1 7,2" fill="#fa0"/><path d="M4.5,1.5A1,1 0 0 1 3,2" fill="#aa0"/></svg>`;
+              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(), 1000, {'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'})
+            .addButton({'class': 'bm-button-circle bm-button-pin', 'style': 'margin-top: 0;', 'innerHTML': '<svg viewBox="0 0 4 6"><path d="M.5,3.4A2,2 0 1 1 3.5,3.4L2,6"/><circle cx="2" cy="2" r=".7" fill="#fff"/></svg>'},
+              (instance, button) => {
+                button.onclick = () => {
+                  const coords = instance.apiManager?.coordsTilePixel; // Retrieves the coords from the API manager
+                  if (!coords?.[0]) {
+                    instance.handleDisplayError('Coordinates are malformed! Did you try clicking on the canvas first?');
+                    return;
+                  }
+                  instance.updateInnerHTML('bm-input-tx', coords?.[0] || '');
+                  instance.updateInnerHTML('bm-input-ty', coords?.[1] || '');
+                  instance.updateInnerHTML('bm-input-px', coords?.[2] || '');
+                  instance.updateInnerHTML('bm-input-py', coords?.[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 => this.#coordinateInputPaste(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 => this.#coordinateInputPaste(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 => this.#coordinateInputPaste(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 => this.#coordinateInputPaste(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; // Disables the button until the transition ends
+                if (button.dataset['buttonStatus'] == 'shown') { // If templates are currently being 'shown' then hide them
+                  instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(false); // Disables templates from being drawn
+                  button.dataset['buttonStatus'] = 'hidden'; // Swap internal button status tracker
+                  button.textContent = 'Enable'; // Swap button text
+                  instance.handleDisplayStatus(`Disabled templates!`); // Inform the user
+                } else { // In all other cases, we should show templates instead of hiding them
+                  instance.apiManager?.templateManager?.setTemplatesShouldBeDrawn(true); // Allows templates to be drawn
+                  button.dataset['buttonStatus'] = 'shown'; // Swap internal button status tracker
+                  button.textContent = 'Disable'; // Swap button text
+                  instance.handleDisplayStatus(`Enabled templates!`); // Inform the user
+                }
+                button.disabled = false; // Enables the button
+              }
+            }).buildElement()
+            .addButton({'textContent': 'Create'}, (instance, button) => {
+              button.onclick = () => {
+                const input = document.querySelector(`#${this.windowID} .bm-input-file`);
+
+                // Checks to see if the coordinates are valid. Throws an error if they are not
+                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;}
+
+                // Kills itself if there is no file
+                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 = () => this.#buildWindowFilter();
+            }).buildElement()
+          .buildElement()
+          .addDiv({'class': 'bm-container'})
+            .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': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="#fff" style="width:80%; margin:auto;"><path d="M249.8 75c89.8 0 113 1.1 146.3 4.4 78.1 7.8 123.6 56 123.6 125.2l0 8.9c0 64.3-47.1 116.9-110.8 122.4-5 16.6-12.8 33.2-23.3 49.9-24.4 37.7-73.1 85.3-162.9 85.3l-17.7 0c-73.1 0-129.7-31.6-163.5-89.2-29.9-50.4-33.8-106.4-33.8-181.2 0-73.7 44.4-113.6 96.4-120.2 39.3-5 88.1-5.5 145.7-5.5zm0 41.6c-60.4 0-103.6 .5-136.3 5.5-46 6.7-64.3 32.7-64.3 79.2l.2 25.7c1.2 57.3 7.1 97.1 27.5 134.5 26.6 49.3 74.8 68.2 129.7 68.2l17.2 0c72 0 107-34.9 126.3-65.4 9.4-15.5 17.7-32.7 22.2-54.3l3.3-13.8 19.9 0c44.3 0 82.6-36 82.6-82l0-8.3c0-51.5-32.2-78.7-88.1-85.3-31.6-2.8-50.4-3.9-140.2-3.9zM267 169.2c38.2 0 64.8 31.6 64.8 67 0 32.7-18.3 61-42.1 83.1-15 15-39.3 30.5-55.9 40.5-4.4 2.8-10 4.4-16.7 4.4-5.5 0-10.5-1.7-15.5-4.4-16.6-10-41-25.5-56.5-40.5-21.8-20.8-39.2-46.9-41.3-77l-.2-6.1c0-35.5 25.5-67 64.3-67 22.7 0 38.8 11.6 49.3 27.7 11.6-16.1 27.2-27.7 49.9-27.7zm122.5-3.9c28.3 0 43.8 16.6 43.8 43.2s-15.5 42.7-43.8 42.7c-8.9 0-13.8-5-13.8-11.7l0-62.6c0-6.7 5-11.6 13.8-11.6z"/></svg>'}, (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);
+
+    // Creates dragging capability on the drag bar for dragging the window
+    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); // Creates a new color filter window instance
+    windowFilter.buildWindow();
+  }
+
+  /** Handles pasting into the coordinate input boxes in the main Blue Marble window.
+   * @param {Overlay} instance - The Overlay class instance
+   * @param {HTMLInputElement} input - The input element that was pasted into
+   * @param {ClipboardEvent} event - The event that triggered this
+   * @since 0.88.426
+   */
+  async #coordinateInputPaste(instance, input, event) {
+
+    event.preventDefault(); // Stops the paste so we can process it
+
+    const data = await getClipboardData(event); // Obtains the clipboard text
+
+    const coords = data.split(/[^a-zA-Z0-9]+/) // Split. Delimiter to split on is "alphanumeric" `f00 bar 4` -> `['f00', 'bar', '4', '']`
+      .filter(index => index) // Only preserves non-empty indexes `['f00', 'bar', '4']`
+      .map(Number) // Converts every index to a number `[NaN, NaN, 4]`
+      .filter(number => !isNaN(number) // Removes NaN `[4]`
+    );
+
+    // If there are only two coordinates, and they were pasted into the pixel coords...
+    if ((coords.length == 2) && (input.id == 'bm-input-px')) {
+      // ...then paste into the pixel inputs
+
+      instance.updateInnerHTML('bm-input-px', coords?.[0] || '');
+      instance.updateInnerHTML('bm-input-py', coords?.[1] || '');
+    } else if ((coords.length == 1)) {
+      // Else if there is only 1 coordinate, we paste into the input like normal
+
+      instance.updateInnerHTML(input.id, coords?.[0] || '');
+    } else {
+      // Else we paste like normal
+
+      instance.updateInnerHTML('bm-input-tx', coords?.[0] || '');
+      instance.updateInnerHTML('bm-input-ty', coords?.[1] || '');
+      instance.updateInnerHTML('bm-input-px', coords?.[2] || '');
+      instance.updateInnerHTML('bm-input-py', coords?.[3] || '');
+    }
+  }
+}
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/WindowSettings.html b/docs/WindowSettings.html new file mode 100644 index 0000000..be4d0a3 --- /dev/null +++ b/docs/WindowSettings.html @@ -0,0 +1,184 @@ + + + + + + WindowSettings - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowSettings

+ + + + + + + +
+ +
+ +

+ WindowSettings +

+ + +
+ +
+
+ + +
+ + + +

new WindowSettings()

+ + + + + +
+

This class handles the overlay UI for the settings window of the Blue Marble userscript.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowSettings.js.html b/docs/WindowSettings.js.html new file mode 100644 index 0000000..7b25c92 --- /dev/null +++ b/docs/WindowSettings.js.html @@ -0,0 +1,156 @@ + + + + + + WindowSettings.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowSettings.js

+ + + + + + + +
+
+
import Overlay from "./Overlay";
+
+/** The overlay builder for the settings window in Blue Marble.
+ * The logic for this window is managed in {@link SettingsManager}
+ * @description This class handles the overlay UI for the settings window of the Blue Marble userscript.
+ * @class WindowSettings
+ * @since 0.91.11
+ * @see {@link Overlay} for examples
+ */
+export default class WindowSettings extends Overlay {
+
+  /** Constructor for the Settings window
+   * @param {string} name - The name of the userscript
+   * @param {string} version - The version of the userscript
+   * @since 0.91.11
+   * @see {@link Overlay#constructor} for examples
+   */
+  constructor(name, version) {
+    super(name, version); // Executes the code in the Overlay constructor
+    this.window = null; // Contains the *window* DOM tree
+    this.windowID = 'bm-window-settings'; // The ID attribute for this window
+    this.windowParent = document.body; // The parent of the window DOM tree
+  }
+
+  /** Spawns a Settings window.
+   * If another settings window already exists, we DON'T spawn another!
+   * Parent/child relationships in the DOM structure below are indicated by indentation.
+   * @since 0.91.11
+   */
+  buildWindow() {
+
+    // If a settings window already exists, close it
+    if (document.querySelector(`#${this.windowID}`)) {
+      document.querySelector(`#${this.windowID}`).remove();
+      return;
+    }
+
+    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) => {
+          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) => {
+            button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
+            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'})
+          .addHeader(1, {'textContent': 'Settings'}).buildElement()
+        .buildElement()
+        .addHr().buildElement()
+        .addP({'textContent': 'Settings take 5 seconds to save.'}).buildElement()
+        .addDiv({'class': 'bm-container bm-scrollable'}, (instance, div) => {
+          // Each category in the settings window
+          this.buildHighlight();
+          this.buildTemplate();
+        }).buildElement()
+      .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`);
+  }
+
+  /** Displays an error when a settings category fails to load.
+   * @param {string} name - The name of the category
+   * @since 0.91.11
+   */
+  #errorOverrideFailure(name) {
+    this.window = this.addDiv({'class': 'bm-container'})
+      .addHeader(2, {'textContent': name}).buildElement()
+      .addHr().buildElement()
+      .addP({'innerHTML': `An error occured loading the ${name} category. <code>SettingsManager</code> failed to override the ${name} function inside <code>WindowSettings</code>.`}).buildElement()
+    .buildElement();
+  }
+
+  /** Builds the highlight section of the window.
+   * This should be overriden by {@link SettingsManager}
+   * @since 0.91.11
+   */
+  buildHighlight() {
+    this.#errorOverrideFailure('Pixel Highlight');
+  }
+
+  /** Builds the template section of the window.
+   * This should be overriden by {@link SettingsManager}
+   * @since 0.91.68
+   */
+  buildTemplate() {
+    this.#errorOverrideFailure('Template');
+  }
+}
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/WindowTelemetry.html b/docs/WindowTelemetry.html new file mode 100644 index 0000000..6122090 --- /dev/null +++ b/docs/WindowTelemetry.html @@ -0,0 +1,184 @@ + + + + + + WindowTelemetry - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowTelemetry

+ + + + + + + +
+ +
+ +

+ WindowTelemetry +

+ + +
+ +
+
+ + +
+ + + +

new WindowTelemetry()

+ + + + + +
+

This class handles the overlay UI for the telemetry window.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.339
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowTelemetry.js.html b/docs/WindowTelemetry.js.html new file mode 100644 index 0000000..db16735 --- /dev/null +++ b/docs/WindowTelemetry.js.html @@ -0,0 +1,167 @@ + + + + + + WindowTelemetry.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowTelemetry.js

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

WindowWizard

+ + + + + + + +
+ +
+ +

+ WindowWizard +

+ + +
+ +
+
+ + +
+ + + +

new WindowWizard()

+ + + + + +
+

Wizard that manages template updates & recovery

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.434
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/WindowWizard.js.html b/docs/WindowWizard.js.html new file mode 100644 index 0000000..1fa7b0c --- /dev/null +++ b/docs/WindowWizard.js.html @@ -0,0 +1,363 @@ + + + + + + WindowWizard.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

WindowWizard.js

+ + + + + + + +
+
+
import Overlay from "./Overlay";
+import Template from "./Template";
+import TemplateManager from "./templateManager";
+import { encodedToNumber, escapeHTML, getWplaceVersion, localizeDate, localizeNumber, sleep } from "./utils";
+
+/** Wizard that manages template updates & recovery
+ * @class WindowWizard
+ * @since 0.88.434
+ * @see {@link Overlay} for examples
+ */
+export default class WindowWizard extends Overlay {
+
+  /** Constructor for the Template Wizard window
+   * @param {string} name - The name of the userscript
+   * @param {string} version - The version of the userscript
+   * @param {string} schemaVersionBleedingEdge - The bleeding edge of schema versions for Blue Marble
+   * @param {TemplateManager} [templateManager=undefined] - (Optional) The TemplateManager class instance
+   * @since 0.88.434
+   * @see {@link Overlay#constructor} for examples
+   */
+  constructor(name, version, schemaVersionBleedingEdge, templateManager = undefined) {
+    super(name, version); // Executes the code in the Overlay constructor
+    this.window = null; // Contains the *window* DOM tree
+    this.windowID = 'bm-window-wizard'; // The ID attribute for this window
+    this.windowParent = document.body; // The parent of the window DOM tree
+
+    // Retrieves data from storage
+    this.currentJSON = JSON.parse(GM_getValue('bmTemplates', '{}')); // The current Blue Marble storage
+    this.scriptVersion = this.currentJSON?.scriptVersion; // Script version when template was created
+    this.schemaVersion = this.currentJSON?.schemaVersion; // Schema version when template was created
+
+    this.schemaHealth = undefined; // Current schema health. This is: 'Good', 'Poor', 'Bad', or 'Dead' for full match, MINOR mismatch, MAJOR mismatch, and unknown, respectively.
+    this.schemaVersionBleedingEdge = schemaVersionBleedingEdge; // Latest schema version
+
+    this.templateManager = templateManager;
+  }
+
+  /** Spawns a Template Wizard window.
+   * If another template wizard window already exists, we DON'T spawn another!
+   * Parent/child relationships in the DOM structure below are indicated by indentation.
+   * @since 0.88.434
+   */
+  buildWindow() {
+
+    // If a template wizard window already exists, close it
+    if (document.querySelector(`#${this.windowID}`)) {
+      document.querySelector(`#${this.windowID}`).remove();
+      return;
+    }
+
+    let style = ''; // Window style
+
+    // If the main window does not exist yet...
+    if (!document.querySelector(`#bm-window-main`)) {
+      style = style.concat('z-index: 9001;').trim();
+    }
+    // Forces the Wizard window to show above the main window if and only if the schema is bad when Blue Marble loads for the first time this session
+
+    // Creates a new template wizard window
+    this.window = this.addDiv({'id': this.windowID, 'class': 'bm-window', 'style': style}, (instance, div) => {
+      // div.onclick = (event) => {
+      //   if (event.target.closest('button, a, input, select')) {return;} // Exit-early if interactive child was clicked
+      //   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) => {
+          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
+        .addButton({'class': 'bm-button-circle', 'textContent': '✖', 'aria-label': 'Close window "Template Wizard"'}, (instance, button) => {
+          button.onclick = () => {document.querySelector(`#${this.windowID}`)?.remove();};
+          button.ontouchend = () => {button.click();}; // Needed only to negate weird interaction with dragbar
+        }).buildElement()
+      .buildElement()
+      .addDiv({'class': 'bm-window-content'})
+        .addDiv({'class': 'bm-container bm-center-vertically'})
+          .addHeader(1, {'textContent': 'Template Wizard'}).buildElement()
+        .buildElement()
+        .addHr().buildElement()
+        .addDiv({'class': 'bm-container'})
+          .addHeader(2, {'textContent': 'Status'}).buildElement()
+          .addP({'id': 'bm-wizard-status', 'textContent': 'Loading template storage status...'}).buildElement()
+        .buildElement()
+        .addDiv({'class': 'bm-container bm-scrollable'})
+          .addHeader(2, {'textContent': 'Detected templates:'}).buildElement()
+          // Detected templates will show up here
+        .buildElement()
+      .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.#displaySchemaHealth(); // Displays template storage health to the user
+    this.#displayTemplateList(); // Displays a list of all templates in the template storage
+  }
+
+  /** Determines how "healthy" the template storage is.
+   * @since 0.88.436
+   */
+  #displaySchemaHealth() {
+
+    // SemVer -> string[]
+    const schemaVersionArray = this.schemaVersion.split(/[-\.\+]/);
+    const schemaVersionBleedingEdgeArray = this.schemaVersionBleedingEdge.split(/[-\.\+]/);
+
+    // Calculates the health that is displayed as a banner
+    let schemaHealthBanner = '';
+    // If the MAJOR version is up-to-date...
+    if (schemaVersionArray[0] == schemaVersionBleedingEdgeArray[0]) {
+
+      // ...AND IF the MINOR version is up-to-date...
+      if (schemaVersionArray[1] == schemaVersionBleedingEdgeArray[1]) {
+        schemaHealthBanner = 'Template storage health: <b style="color:#0f0;">Healthy!</b><br>No futher action required. (Reason: Semantic version matches)';
+        this.schemaHealth = 'Good';
+      } else { // ...else, the MINOR version is out-of-date
+        schemaHealthBanner = 'Template storage health: <b style="color:#ff0;">Poor!</b><br>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.schemaHealth = 'Poor';
+      }
+    } else if (schemaVersionArray[0] < schemaVersionBleedingEdgeArray[0]) {
+      // ...ELSE IF the MAJOR version is out-of-date
+      
+      schemaHealthBanner = 'Template storage health: <b style="color:#f00;">Bad!</b><br>It is guaranteed that some features are broken. You <em>might</em> 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.schemaHealth = 'Bad';
+    } else {
+      // ...ELSE the Semantic version is unknown
+
+      schemaHealthBanner = 'Template storage health: <b style="color:#f00">Dead!</b><br>Blue Marble can not load the template storage. (Reason: MAJOR version unknown)';
+      this.schemaHealth = 'Dead';
+    }
+
+    // Further recovery directions (only displayed if health is NOT 'Good')
+    const recoveryInstructions = `<hr style="margin:.5ch">If you want to continue using your current templates, then make sure the template storage (schema) is up-to-date.<br>If you don't want to update the template storage, then downgrade Blue Marble to version <b>${escapeHTML(this.scriptVersion)}</b> to continue using your templates.<br>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.`;
+
+    // Obtains the last time Wplace was updated
+    const wplaceUpdateTime = getWplaceVersion();
+    let wplaceUpdateTimeLocalized = wplaceUpdateTime ? localizeDate(wplaceUpdateTime) : '???';
+
+    // Display schema health to user
+    this.updateInnerHTML('#bm-wizard-status', `${schemaHealthBanner}<br>Your templates were created during Blue Marble version <b>${escapeHTML(this.scriptVersion)}</b> with schema version <b>${escapeHTML(this.schemaVersion)}</b>.<br>The current Blue Marble version is <b>${escapeHTML(this.version)}</b> and requires schema version <b>${escapeHTML(this.schemaVersionBleedingEdge)}</b>.<br>Wplace was last updated on <b>${wplaceUpdateTimeLocalized}</b>.${(this.schemaHealth != 'Good') ? recoveryInstructions : ''}`);
+    
+    // Create button options (only if schema is not 'Dead')
+    const buttonOptions = new Overlay(this.name, this.version);
+    if (this.schemaHealth != 'Dead') {
+      buttonOptions.addDiv({'class': 'bm-container bm-flex-center bm-center-vertically', 'style': 'gap: 1.5ch;'})
+        buttonOptions.addButton({'textContent': 'Download all templates'}, (instance, button) => {
+          button.onclick = () => {
+            button.disabled = true;
+            this.templateManager.downloadAllTemplatesFromStorage().then(() => {
+              button.disabled = false;
+            })
+          };
+        }).buildElement();
+      // Leave the container open for the next button to be added
+    }
+    // If the schema health is Poor or Bad, then show update option
+    if ((this.schemaHealth == 'Poor') || (this.schemaHealth == 'Bad')) {
+      buttonOptions.addButton({'textContent': `Update template storage to ${this.schemaVersionBleedingEdge}`}, (instance, button) => {
+        button.onclick = () => {
+
+          button.disabled = true; // Disables the button
+
+          // Converts the template schema from 1.x.x to 2.x.x
+          this.#convertSchema_1_x_x_To_2_x_x(true);
+        };
+      }).buildElement();
+    }
+
+    // Add the button options DOM tree to the actual DOM tree
+    buttonOptions.buildElement().buildOverlay(document.querySelector('#bm-wizard-status').parentNode);
+  }
+
+  /** Displays loaded templates to the user.
+   * @since 0.88.441
+   */
+  #displayTemplateList() {
+
+    const templates = this.currentJSON?.templates; // Templates in user storage
+
+    // If there is at least one template loaded...
+    if (Object.keys(templates).length > 0) {
+
+      // Obtains the parent element for the template list
+      const templateListParentElement = document.querySelector(`#${this.windowID} .bm-scrollable`);
+
+      // Creates the template list DOM tree
+      const templateList = new Overlay(this.name, this.version);
+      templateList.addDiv({'id': 'bm-wizard-tlist', 'class': 'bm-container'})
+
+      // For each template...
+      for (const template in templates) {
+
+        const templateKey = template; // The identification key for the template. E.g., "0 $Z"
+        const templateValue = templates[template]; // The actual content of the template
+
+        // If the template is a direct child of the templates Object...
+        if (templates.hasOwnProperty(template)) {
+
+          // Obtain template information
+          const templateKeyArray = templateKey.split(' '); // E.g., "0 $Z" -> ["0", "$Z"]
+          const sortID = Number(templateKeyArray?.[0]); // Sort ID of the template
+          const authorID = encodedToNumber(templateKeyArray?.[1] || '0', this.templateManager.encodingBase); // User ID of the person who exported the template
+          const displayName = templateValue.name || `Template ${sortID || ''}`; // Display name of the template
+          const coords = templateValue?.coords?.split(',').map(Number); // "1,2,3,4" -> [1, 2, 3, 4]
+          const totalPixelCount = templateValue.pixels?.total ?? undefined;
+          const templateImage = undefined; // TODO: Add template image
+
+          // Localization of information to display to the user
+          const sortIDLocalized = (typeof sortID == 'number') ? localizeNumber(sortID) : '???';
+          const authorIDLocalized = (typeof authorID == 'number') ? localizeNumber(authorID) : '???';
+          const totalPixelCountLocalized = (typeof totalPixelCount == 'number') ? localizeNumber(totalPixelCount) : '???';
+
+          templateList.addDiv({'class': 'bm-container bm-flex-center'})
+            .addDiv({'class': 'bm-flex-center', 'style': 'flex-direction: column; gap: 0;'})
+              .addDiv({'class': 'bm-wizard-template-container-image', 'textContent': templateImage || '🖼️'})
+                // TODO: Add image element and SVG fallback
+              .buildElement()
+              .addSmall({'textContent': `#${sortIDLocalized}`}).buildElement()
+            .buildElement()
+            .addDiv({'class': 'bm-flex-center bm-wizard-template-container-flavor'})
+              .addHeader(3, {'textContent': displayName}).buildElement()
+              .addSpan({'textContent': `Uploaded by user #${authorIDLocalized}`}).buildElement()
+              .addSpan({'textContent': `Coordinates: ${coords.join(', ')}`}).buildElement()
+              .addSpan({'textContent': `Total Pixels: ${totalPixelCountLocalized}`}).buildElement()
+            .buildElement()
+          .buildElement()
+        }
+      }
+
+      // Adds the template list to the real DOM tree
+      templateList.buildElement().buildOverlay(templateListParentElement);
+    }
+  }
+
+  /** Converts schema version 1.0.0 to schema version 2.0.0.
+   * @param {boolean} shouldWindowWizardOpen - Should we open a new Template Wizard window after schema conversion? This will close any Template Wizard already open.
+   * @since 0.88.504
+   */
+  async #convertSchema_1_x_x_To_2_x_x(shouldWindowWizardOpen) {
+
+    // Creates loading screen
+    if (shouldWindowWizardOpen) {
+      
+      // Obtains the Template Wizard window content container
+      const windowContent = document.querySelector(`#${this.windowID} .bm-window-content`);
+
+      // Deletes all content in the Template Wizard window content container
+      windowContent.innerHTML = '';
+
+      const loadingScreen = new Overlay(this.name, this.version);
+      loadingScreen.addDiv({'class': 'bm-container'})
+        .addDiv({'class': 'bm-container bm-center-vertically'})
+          .addHeader(1, {'textContent': 'Template Wizard'}).buildElement()
+        .buildElement()
+        .addHr().buildElement()
+        .addDiv({'class': 'bm-container'})
+          .addHeader(2, {'textContent': 'Status'}).buildElement()
+          .addP({'textContent': 'Updating template storage. Please wait...'}).buildElement()
+        .buildElement()
+      .buildElement().buildOverlay(windowContent);
+    }
+
+    // Deletes the bmCoords value set in 1.0.0 which is unused in 2.0.0
+    GM_deleteValue('bmCoords');
+
+    // Obtains the templates from JSON storage
+    const templates = this.currentJSON?.templates;
+
+    // If there is at least one template loaded...
+    if (Object.keys(templates).length > 0) {
+
+      // For each template loaded...
+      for (const [key, template] of Object.entries(templates)) {
+
+        // If the template is a direct child of the templates Object...
+        if (templates.hasOwnProperty(key)) {
+
+          // Creates a dummy Template class instance
+          const _template = new Template({
+            displayName: template.name,
+            chunked: template.tiles
+          });
+
+          _template.calculateCoordsFromChunked(); // Updates `Template.coords`
+
+          // Converts the template to a Blob
+          const blob = await this.templateManager.convertTemplateToBlob(_template);
+
+          // Uses the information from the dummy Template class instance to make the actual Template
+          await this.templateManager.createTemplate(blob, _template.displayName, _template.coords);
+        }
+      }
+    }
+
+    // If it has been requested that we open a new Template Wizard window, we do so
+    if (shouldWindowWizardOpen) {
+      console.log(`Restarting Template Wizard...`);
+      document.querySelector(`#${this.windowID}`).remove();
+      new WindowWizard(this.name, this.version, this.schemaVersionBleedingEdge, this.templateManager).buildWindow();
+    }
+  }
+}
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/apiManager.js.html b/docs/apiManager.js.html new file mode 100644 index 0000000..1816554 --- /dev/null +++ b/docs/apiManager.js.html @@ -0,0 +1,363 @@ + + + + + + apiManager.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

apiManager.js

+ + + + + + + +
+
+
/** ApiManager class for handling API requests, responses, and interactions.
+ * Note: Fetch spying is done in main.js, not here.
+ * @class ApiManager
+ * @since 0.11.1
+ */
+
+import TemplateManager from "./templateManager.js";
+import { consoleError, escapeHTML, localizeNumber, numberToEncoded, serverTPtoDisplayTP } from "./utils.js";
+
+export default class ApiManager {
+
+  /** Constructor for ApiManager class
+   * @param {TemplateManager} templateManager 
+   * @since 0.11.34
+   */
+  constructor(templateManager) {
+    this.templateManager = templateManager;
+    this.disableAll = false; // Should the entire userscript be disabled?
+    this.chargeRefillTimerID = ''; // Contains the Charge refill timer element ID attribute so we can update the timer.
+    this.coordsTilePixel = []; // Contains the last detected tile/pixel coordinate pair requested
+    this.templateCoordsTilePixel = []; // Contains the last "enabled" template coords
+  }
+
+  /** Determines if the spontaneously received response is something we want.
+   * Otherwise, we can ignore it.
+   * Note: Due to aggressive compression, make your calls like `data['jsonData']['name']` instead of `data.jsonData.name`
+   * 
+   * @param {Overlay} overlay - The Overlay class instance
+   * @since 0.11.1
+  */
+  spontaneousResponseListener(overlay) {
+
+    // Triggers whenever a message is sent
+    window.addEventListener('message', async (event) => {
+
+      const data = event.data; // The data of the message
+      const dataJSON = data['jsonData']; // The JSON response, if any
+
+      // Kills itself if the message was not intended for Blue Marble
+      if (!(data && data['source'] === 'blue-marble')) {return;}
+
+      // Kills itself if the message has no endpoint (intended for Blue Marble, but not this function)
+      if (!data['endpoint']) {return;}
+
+      // Trims endpoint to the second to last non-number, non-null directoy.
+      // E.g. "wplace.live/api/pixel/0/0?payload" -> "pixel"
+      // E.g. "wplace.live/api/files/s0/tiles/0/0/0.png" -> "tiles"
+      const endpointText = data['endpoint']?.split('?')[0].split('/').filter(s => s && isNaN(Number(s))).filter(s => s && !s.includes('.')).pop();
+
+      console.log(`%cBlue Marble%c: Recieved message about "%s"`, 'color: cornflowerblue;', '', endpointText);
+
+      // Each case is something that Blue Marble can use from the fetch.
+      // For instance, if the fetch was for "me", we can update the overlay stats
+      switch (endpointText) {
+
+        case 'me': // Request to retrieve user data
+
+          // If the game can not retrieve the userdata...
+          if (dataJSON['status'] && 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: <b>${localizeNumber(dataJSON['droplets'])}</b>`); // Updates the text content of the droplets field
+          overlay.updateInnerHTML('bm-user-nextlevel', `Next level in <b>${localizeNumber(nextLevelPixels)}</b> pixel${nextLevelPixels == 1 ? '' : 's'}`); // Updates the text content of the next level field
+          break;
+
+        case 'pixel': // Request to retrieve pixel data
+          const coordsTile = data['endpoint'].split('?')[0].split('/').filter(s => s && !isNaN(Number(s))); // Retrieves the tile coords as [x, y]
+          const payloadExtractor = new URLSearchParams(data['endpoint'].split('?')[1]); // Declares a new payload deconstructor and passes in the fetch request payload
+          const coordsPixel = [payloadExtractor.get('x'), payloadExtractor.get('y')]; // Retrieves the deconstructed pixel coords from the payload
+          
+          // Don't save the coords if there are previous coords that could be used
+          if (this.coordsTilePixel.length && (!coordsTile.length || !coordsPixel.length)) {
+            overlay.handleDisplayError(`Coordinates are malformed!\nDid you try clicking the canvas first?`);
+            return; // Kills itself
+          }
+          
+          this.coordsTilePixel = [...coordsTile, ...coordsPixel]; // Combines the two arrays such that [x, y, x, y]
+          
+          const displayTP = serverTPtoDisplayTP(coordsTile, coordsPixel); // Retrieves the coordinates that Wplace displays for this region
+
+          const spanElements = document.querySelectorAll('span'); // Retrieves all span elements
+
+          // For every span element, find the one we want (pixel numbers when canvas clicked)
+          for (const element of spanElements) {
+            // We use the pixel numbers to find this element because it is the only identifiable piece of information, assuming the website can load in non-Engligh languages.
+
+            const elementTextTrimmed = element.textContent.trim(); // Stores the text of the span element, without leading or trailing spaces
+
+            // If the text content of the element includes both coordinates seperatly (avoids failure when the comma seperator changes due to localization)
+            if (elementTextTrimmed.includes(displayTP[0]) && elementTextTrimmed.includes(displayTP[1])) {
+
+              let displayCoords = document.querySelector('#bm-display-coords'); // Find the additional pixel coords span
+
+              const text = `(Tl X: ${coordsTile[0]}, Tl Y: ${coordsTile[1]}, Px X: ${coordsPixel[0]}, Px Y: ${coordsPixel[1]})`;
+              
+              // All 4 coordinate labels, IDs, and values
+              const coordsLabel = ['Tl X:', 'Tl Y:', 'Px X:', 'Px Y:'];
+              const coordsID = ['bm-tile-x', 'bm-tile-y', 'bm-pixel-x', 'bm-pixel-y'];
+              const coordsCombined = [...coordsTile, ...coordsPixel];
+
+              // If we could not find the addition coord span, we make it then update the textContent with the new coords
+              if (!displayCoords) {
+                displayCoords = document.createElement('span');
+                displayCoords.id = 'bm-display-coords';
+                displayCoords.style = 'display: flex; flex-wrap: wrap; gap: 0 1ch; font-size: small;';
+
+                // For each of the 4 coordinates...
+                for (const [coordIndex, coordValue] of coordsCombined.entries()) {
+
+                  const coordElement = document.createElement('span'); // Creates a `<span>` element
+
+                  coordElement.id = coordsID[coordsCombined.indexOf(coordValue) ?? '']; // Applys the ID to the coord element
+
+                  // Outputs something like "Tl X: 483"
+                  coordElement.textContent = `${coordsLabel[coordIndex] ?? '??:'} ${coordValue}`;
+                  // Or if the amount of labels is less than the provided values, it outputs something like "??: 483" instead of failing
+
+                  displayCoords.appendChild(coordElement); // Adds the span coordinate as a child for the flexbox container
+                }
+
+                // Adds the display coordinate flexbox container to the pixel info menu
+                element.parentNode.parentNode.parentNode.insertAdjacentElement('afterend', displayCoords);
+              } else {
+                
+                // For each of the 4 coordinates...
+                for (const [coordIndex, coordID] of coordsID.entries()) {
+
+                  const coordElement = document.getElementById(coordID); // Obtains the coordinate element
+
+                  // Outputs something like "Tl X: 483"
+                  coordElement.textContent = `${coordsLabel[coordIndex] ?? '??:'} ${coordsCombined[coordIndex]}`;
+                  // Or if the amount of labels is less than the provided values, it outputs something like "??: 483" instead of failing
+                }
+              }
+            }
+          }
+          break;
+        
+        case 'tile':
+        case 'tiles':
+
+          let tileCoordsTile = data['endpoint'].split('/');
+          tileCoordsTile = [parseInt(tileCoordsTile[tileCoordsTile.length - 2]), parseInt(tileCoordsTile[tileCoordsTile.length - 1].replace('.png', ''))];
+          
+          const blobUUID = data['blobID'];
+          const blobData = data['blobData'];
+          
+          const timer = Date.now();
+          const templateBlob = await this.templateManager.drawTemplateOnTile(blobData, tileCoordsTile);
+          console.log(`Finished loading the tile in ${(Date.now() - timer) / 1000} seconds!`);
+
+          window.postMessage({
+            source: 'blue-marble',
+            blobID: blobUUID,
+            blobData: templateBlob,
+            blink: data['blink']
+          });
+          break;
+
+        case 'robots': // Request to retrieve what script types are allowed
+          this.disableAll = dataJSON['userscript']?.toString().toLowerCase() == 'false'; // Disables Blue Marble if site owner wants userscripts disabled
+          break;
+      }
+    });
+  }
+
+  // Sends a heartbeat to the telemetry server
+  async sendHeartbeat(version) {
+
+    console.log('Sending heartbeat to telemetry server...');
+
+    let userSettings = GM_getValue('bmUserSettings', '{}')
+    userSettings = JSON.parse(userSettings);
+
+    if (!userSettings || !userSettings.telemetry || !userSettings.uuid) {
+      console.log('Telemetry is disabled, not sending heartbeat.');
+      return; // If telemetry is disabled, do not send heartbeat
+    }
+
+    const ua = navigator.userAgent;
+    let browser = await this.getBrowserFromUA(ua);
+    let os = this.getOS(ua);
+
+    GM_xmlhttpRequest({
+      method: 'POST',
+      url: 'https://telemetry.thebluecorner.net/heartbeat',
+      headers: {
+        'Content-Type': 'application/json'
+      },
+      data: JSON.stringify({
+        uuid: userSettings.uuid,
+        version: version,
+        browser: browser,
+        os: os,
+      }),
+      onload: (response) => {
+        if (response.status !== 200) {
+          consoleError('Failed to send heartbeat:', response.statusText);
+        }
+      },
+      onerror: (error) => {
+        consoleError('Error sending heartbeat:', error);
+      }
+    });
+  }
+
+  async getBrowserFromUA(ua = navigator.userAgent) {
+    ua = ua || "";
+
+    // Opera
+    if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
+
+    // Edge (Chromium-based uses "Edg/")
+    if (ua.includes("Edg/")) return "Edge";
+
+    // Vivaldi
+    if (ua.includes("Vivaldi")) return "Vivaldi";
+
+    // Yandex
+    if (ua.includes("YaBrowser")) return "Yandex";
+
+    // Kiwi (not guaranteed, but typically shows "Kiwi")
+    if (ua.includes("Kiwi")) return "Kiwi";
+
+    // Brave (doesn't expose in UA by default; heuristic via Brave/ token in some versions)
+    if (ua.includes("Brave")) return "Brave";
+
+    // Firefox
+    if (ua.includes("Firefox/")) return "Firefox";
+
+    // Chrome (catch-all for Chromium browsers)
+    if (ua.includes("Chrome/")) return "Chrome";
+
+    // Safari (must be after Chrome check)
+    if (ua.includes("Safari/")) return "Safari";
+
+    // Brave special check
+    if (navigator.brave && typeof navigator.brave.isBrave === "function") {
+      if (await navigator.brave.isBrave()) return "Brave";
+    }
+
+    // Fallback
+    return 'Unknown';
+  }
+
+  getOS(ua = navigator.userAgent) {
+    ua = ua || "";
+
+    if (/Windows NT 11/i.test(ua)) return "Windows 11";
+    if (/Windows NT 10/i.test(ua)) return "Windows 10";
+    if (/Windows NT 6\.3/i.test(ua)) return "Windows 8.1";
+    if (/Windows NT 6\.2/i.test(ua)) return "Windows 8";
+    if (/Windows NT 6\.1/i.test(ua)) return "Windows 7";
+    if (/Windows NT 6\.0/i.test(ua)) return "Windows Vista";
+    if (/Windows NT 5\.1|Windows XP/i.test(ua)) return "Windows XP";
+
+    if (/Mac OS X 10[_\.]15/i.test(ua)) return "macOS Catalina";
+    if (/Mac OS X 10[_\.]14/i.test(ua)) return "macOS Mojave";
+    if (/Mac OS X 10[_\.]13/i.test(ua)) return "macOS High Sierra";
+    if (/Mac OS X 10[_\.]12/i.test(ua)) return "macOS Sierra";
+    if (/Mac OS X 10[_\.]11/i.test(ua)) return "OS X El Capitan";
+    if (/Mac OS X 10[_\.]10/i.test(ua)) return "OS X Yosemite";
+    if (/Mac OS X 10[_\.]/i.test(ua)) return "macOS"; // Generic fallback
+
+    if (/Android/i.test(ua)) return "Android";
+    if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
+
+    if (/Linux/i.test(ua)) return "Linux";
+
+    return "Unknown";
+  }
+}
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/confetttiManager.js.html b/docs/confetttiManager.js.html new file mode 100644 index 0000000..3197472 --- /dev/null +++ b/docs/confetttiManager.js.html @@ -0,0 +1,123 @@ + + + + + + confetttiManager.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

confetttiManager.js

+ + + + + + + +
+
+
import { colorpalette } from "./utils";
+
+/** Manages the confetti animation.
+ * @description Manages any confetti animation used by Blue Marble.
+ * @class ConfettiManager
+ * @since 0.88.356
+ */
+export default class ConfettiManager {
+
+  /** The constructor for the confetti manager.
+   * @since 0.88.356
+   */
+  constructor() {
+
+    // Shards of confetti to spawn
+    this.confettiCount = Math.ceil((80 / 1300) * window.innerWidth);
+    // Number of confetti is based on screen width
+
+    this.colorPalette = colorpalette.slice(1); // Color palette for confetti
+  }
+
+  /** Immedently creates confetti inside the parent element.
+   * @param {HTMLElement} parentElement - The parent element to create confetti inside of
+   * @since 0.88.356
+   */
+  createConfetti(parentElement) {
+
+    const confettiContainer = document.createElement('div'); // Creates confetti container
+
+    // For each piece of confetti to spawn...
+    for (let currentCount = 0; currentCount < this.confettiCount; currentCount++) {
+
+      // Creates a new confetti shard
+      const confettiShard = document.createElement('confetti-piece');
+
+      // Randomly generates the variables that the CSS needs
+      confettiShard.style.setProperty('--x', `${Math.random() * 100}vw`);
+      confettiShard.style.setProperty('--delay', `${Math.random() * 2}s`);
+      confettiShard.style.setProperty('--duration', `${3 + Math.random() * 3}s`);
+      confettiShard.style.setProperty('--rot', `${Math.random() * 360}deg`);
+      confettiShard.style.setProperty('--size', `${6 + Math.random() * 6}px`);
+      confettiShard.style.backgroundColor = `rgb(${this.colorPalette[Math.floor(Math.random() * this.colorPalette.length)].rgb.join(',')})`;
+
+      // Deletes the confetti shard when the animation ends
+      confettiShard.onanimationend = () => {
+        if (confettiShard.parentNode.childElementCount <= 1) {
+          confettiShard.parentNode.remove(); // Deletes the container instead if no confetti is left
+        } else {
+          confettiShard.remove(); // Removes the confetti shard
+        }
+      }
+
+      confettiContainer.appendChild(confettiShard); // Adds the confetti shard to the container
+    }
+
+    // Adds the container to the parent element
+    parentElement.appendChild(confettiContainer);
+  }
+}
+
+// Creates a custom element called <confetti-piece>
+class BlueMarbleConfettiPiece extends HTMLElement {}
+customElements.define('confetti-piece', BlueMarbleConfettiPiece);
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..5d20d916338a5890a033952e2e07ba7380f5a7d3 GIT binary patch literal 19544 zcmZsBRZtvE7wqD@i!HFY1b24`kj35I-CYBL;O-Dy7Y*)i!Ciy9OMu`K2ubeuzujAP z&(u^;b@!=xJ5w`f^ppUAR7C&)@xOr#_z%&6s7NTth=|AtfF4A^f1HxqH6mcokP-l6 z{7?U16e0j9|A(M9nJ@pt|2J>}ssJ~DHNfRRlP19YKlJ?100c+?Tmeo1tN+$S0Gx`?s1CFN7eMUDk_WsHBTfGwNlSoSO;j5Y2+U^b7c?fa0Y^S_)w3$t3v&# z{~&TTlM zt?Lt*SHuem8SrEC@7zaU<-qSuQW-60?>}hkJOK8c63ZzHHJk8oZ^lJI@4J}J-UW#v z``};wWo2yOy5j-i>^G*aArwT)Vs*SHt6!%SuA2O<_J=(LpNDHvxaKhxXh#=~9&&Ym z(3h3}YEDIOIJiClxPx>szhB_|HF$A3M_(n`EZ{OfeopPhu5a!iV`!-MGz%=Z=6_KhH^># zc0eZ(i}Fam9zt=@^nI}P1TS0OA-NjllZr>npsHhjY^(twm8{D3gzMI3wz*wpNrf_@ z*a?QZ6Zge*92n!$$Tj4PYIXRs9DZwFAPAN5P1wKY;CH_ec^<;uNX&@i#260}94dT^ zt<=Np#*{u2jSWT-*MlH7@a5$;Wa{AyjRD3+-J*f z6&WMZwq>z5b$RG4+v&bc?4gk|zg$9}VoVrJ;Y}$~Y0v{16FHY4IxFkRaW%N-2|Ez= z_qUxB0-(|bh+%0a;3Ta?`XQ4zkOvWpkM=>=!Ky%oa>mUWp zD$PDk^y_cvj^9Y{zV+u>JQ0cidbEQJqsLJULLuYmMt{g`2A(e4Jx<)36FnSe9e>oE zxzOk@q#7!!I{#p>ubQPjK^X81+Uk6pgDIe@S%bvBM{r0gP<&p2HpJ{Dw?tBkQcYmf z)epzhSW{ofDYZ3@A~&Vc)p5lIB(G1Z(li%c#2C<(XdagusQ++&BM8?0j@5^olZU_% z=m7z5F=9%B3}Q*r?Z~~~QTicWnWMz%)ac2D(&K?a;ZmiIghUkmX^}3?DlhKXR*uytr?z?QgE=}; zOa!lz=(^W8!o_2yeZanFSf4l&pD~$9%qw3~q-JTwS{q=h8Z&*)#=pau`crUY8{{Xe zbG(-h4xKWAgfOI21Y+*SHvt*(jZOiBe~sW$i5tg5gJmQj!DRql3=`3nCTPe<85)Wv zDNcRZs>LpDMFIfBrMTi`Q=*uwc+(sNa(GH4V2;xllPE^eRd>%>?~<(DMkaHf*T4XQ z+U1nL|7aS>kOnGROHo}SZGERinov(cPMN+*C&qAc;KcZoErZ@htW9oyc8;-|!FrJq zWzc0=Z%7ImftY2Q1-AIz!2659@GzAk9Jg;F=}^jfq7YR0o}=6_?iu=(#FW0B7rvDm zn1c)hm^PqMaV$*U;T1f3Mq+R(f~gewI%O_(HCtJrr?aR}fm z^A5Nj&5bCD$&Zf4xcV+~Qxl;W7z!#yKm?fy{LsOD_z)&hz#E*1kcMLh{L3Pv46?s4 zdU|hZ!MYD2kv5!^pxI+?dVB71MvQ>)UiEJ@W37&wY1Frz(*jm6 zk|~Vew*ICqWr+{TfI1k%y(OI(S@~Ybjw34_tN3CkER8Wz-_7e@GSF5bBv56k)#w>4 zBJ&uc1o(x~|0<=JLj1+p9|#)e_9d6LEKN9K6?7Zwu+&cA2(Tf`G1&JnTKK;q|8>j2ztI4Bd}xKh$Ra!yFi$u>QQy2jhQuk%;V z8agmZLNW??oDq5&mtPbcc$hRlu<_ThWmGOqdt~T%1iy#AFDP1tgms>gw;8T?hb`>- zpN@N7#D#?I|Gg50kkVY{;9rb?KBbHtYoEAIxuhIL7e2Bsk5YeGX)!~AZ%NT z@&|>qOb$uDe$|(76~Ihc3bzsC+AjB$L*`YX<|&XOMtpbN4l0ut6#XN*X#vhU z+W6Gx3F=~fCf?=t_d~;Bdeqnz%~sZ;ekDKz4XwxFBddSrhzj3j1Jx`IIUD7y7M8-- z-9-|ccrC_9J}BI}K~etcC?%Lm7$E;WF#P(W9Zi2^2NJL14lA!Nnqs0@Ne^Y`t~emz zB2hvC!<7eO00Y@WTsb!3As(&f{2(ZZ5D=lqP_1J+;AFv#Xh&%UU^zhl(yskwZrrh+ z1Y!^Hp|{%zjqwuA`_$m);XzPJsr7e&oK+bW75~_?>-XkyGpurn*Ov-WXDxIF!;6a; zY-Rzp;&@DcWDuKI8W;90BZ=z^)~PWz?xdLaj?*X-U(m)W#`J;5_wz@sJtx``4)rL# zL&rY@x9GxIjC9gy0kve>w+5W);Q6CV7Fe>C&Xpu}y9Vz@x$_sEZSnSMr{M^gjfYei z4Lb-Z)j=!#Gdf15PpC8HP@nD~7jq9rpMR!R$FWbTnm&Qw| zBL@G`s*^SEq1DA>ns}cS_A&ZUva;SsX0Hy-uYli3k!hLB%m zorJ;k*m^ztGZh7lwDzBDWXH%&iJy8N%c}9$Kil z;I*C{Av2(ZOxfmo$P>uLtJg3|rJM=4da4&75^UCP4-RVvUM)jo-EI(FpHS*$V2U_@ zr`a0Xa*AQj!lE&v6M^TzPTem1DF8pYve zy>^orHFfarN*2R6;&Fl%pvuE%oo3g+v6L!wT+_d;>E7j8ep)$;7iBcIV#$v7gNOS; z!!V4jg30}|4l4jhf=N++7>kqop0bhFx0qJGFqto$2hsOAgXajjDV$l-1vOtt9z7pD z%UR9KT1HC2Xmv%LNiBW**YOQjYJZ**N4u*X|5;J1qjZ@M+O`0X*B#EL?%oV z=<4VYw>B%iK*J{E7=*En`lt!SIyyQocG0XUYRk?Sz#;>+MZmyHD}tFtVPj#OXgl432N05e@4`#Pra z7?)%r5rWZ3n@CmbgiK6azZ~#lSx9lkC(-B%dM?liI&R@-{N??}2=t;5D=kOdM{!Ys z;E(^B(6?fpxblMb-ePZ^Ow@4aaA*Ym+eU-B*OfnZj0KGOJhNU&sb;FwWe$wm=$AU+ zeIQHU7^-f8)Nrlyma2pcxs!K}!%1(11a1&DM&{SRI=zhLzqA-MW5g_rSOI!PeTCSB1V@ ze5`RMw(u1EoNxZf6c!%RlwjE+{w4agvwuZ!%)ZWe;m_>=FkC|uH+n9I5! zBObd>e}@6L>RXGvvNaHa7;_ymEU`+rJ7$n8uz$nuHC%YBB+nz}L9j^$A6#cwG!Fia zKgt)k+#A#80|9m(b!qE5iKFniV`82mQnwE=i46L{EE$C63p@ z1&V@Og*CSVFU^D_aAJp({4FeasEPR_ZU+MM*4+HagyvFnm8=*2aiWqG(kq^i6y9 zK9o~%mqLo^jdN0`4SDyMRQ+DizvAXDkH%SC1`{v-_^G*tU;#v3ZzUaPdQs|bqB}yi zFBYhuG}IG1{F?bu=BMR-nlmWhZ(jG}G6w^ejf+{OjANnCgJtiU7g8z$A!{$2Q60>_*AY^h^%3 zet=#D#2HqPia@kP1azEQ6PQ*BtH<5*9)o*`D7uNpNXqG_G@65yccncDNR&wvq8^T# zbQn<%?0SRg{$#fFGOA(3DqNG4=^UNn4WvpuT>E&R0QarW;0ld z$|U|uy2YYF`A`r<+ig8f_MUr)mh_MG3QLNODZrpY{AbgZ>)7C-Qu2~r9Ih)Ov+!Ia zuE#Y3aWo~S+;9aKW!Xcy{=XkxCeG%W`xvb6(Dm5E8z~!?a&*Yh*y77RvFe`kZcPfF z5z@rD$JQ&M#t(zX_-ya&iKs&BX~pSUkafVww)ym{?ig;xT{7ucGXy;6LXi2M*wJVW zhnO6L7JJ6TrRJf4oy+sFdw0$X?PmDUo4`R_;n_C4dS2~k%I4xEBMXN}cH?$9b_G5D zR4nV7LJMc?koICX{)5|5m=9>5{v#@_p58o-OeLsy6U6m5Rtc_7TYr|Ug)O#X-UGq@ zBvRTOiWMD$f+5Rfn#gFp!P>&0zaVyn|7`@7K;XDu{r z5#ymDq$&2BeA)XU2Qr$2+8S*NE0&9u2TvtBWA2I)ZhFPvUCbbzA|7qMzy9arvdZEP zzrIhYUFFJ3E_OGqe1(-MZs$YF{-tCA+c-=y_)w&z*bhY*8uETY*uRjts_e*Zm> z#X4q!T|V}5Rx<7LGq}QtCr;m4r$n8BtY3l=WqWOeq#82!twIBu)sWGLL^)3(&cjGM zUwfS&mh>T^!-F(kP_TI16N%k=A(^2bD)?9BH^g>TBRZ%+9*7-^f}R8UDofvwlsOr2 z#6(Gco__DIrTU8}>`=00_)gU5T8&haeZDXn86`otY)G&Vk(KLdt-#)_QkDl^$F-EA zfYe}zpa}86yJL#%gKaEj;&N2d|9AamL$8r5VM?$j!q^9ws4Q~j5fB^(X)xXpBPZpb zZQ zpO=8PS-{sKI;g}8ml2+lFmx<-I2PuOjDh%x;|M%1!PTw&^*n-eArC>mdGFPz!S&By z#=SiyQ$uF-(_D|80kf??b5#a5G;1~le8{Zv4&w&U3RqXZ9^h1>7DGPmfzjVy*m5!` zaD}I`Ow_{DE)twMGqD#tqf7LvO>`{gO=&1s6T7xE7B*om)eshq{JM*5u*L9a1aPpo z=+epa^`tIb%9Ew@A?QA3uJS$ZO75hy$I2sC@CIsiCUa%guB=h?l1+u;px_cgd3I^+ z9&WN@a8qCW#PAR80=!-D9X%rSoBLUX{%66>d?hDa`E`jjPw$uiq(&5bR(sVfMV8mGIBKX-)TfR_(3b9gX70B zNaSCKW_e}3Xypy7H`NccT{m~yeH-?F`qDIan#6ou5=``K5mra)aRGdhwUg*$Q~$d6 zD5FQRL0tn$q~tL}%nZEGj~cnGOJ89eW5t}> z@0A6;=QNnj_uUjxFXkL8SH%{PsavXCG>sX_-_wpOJx|IE=DUO&OQhb$n_H3rR0`BIukhCmxU^YjqQ`Q`RNf*DnAb0^=-uVUKg(fxVB1W7i3 zNXx*3IxRTVOhXspC7V|;(HpL4ju6c)+d2S$!a^3709WB84fUhL`{U13IEzpZgG%GOE>27OZH9Zx;8v10YJS_PuMP-SSy z@hb8;mB>V22sgWaE>r)ck|QLG8%qS#e&mh|a|Xv(&yWnXQTd4OgM)st6xkUhOpXmk zIe}ThDr(&LK>v>e;?ymsWQ2Js82J;(i&P7AX1+iKP*ufIY_zPy+_X%clOY$rG8K}3 zITj1C{lni?LHp=6TFfxJVJ#nNuby~c?_SbC>-q*c?5sIsTr&K|YtzAn)e^k%uXva@%|y7dICt9o$5nk($aa){E^) z%D(=0GY9d_&W-Q~yr1u|D4zoDkn*LBJ)7~@c%m}7SA~VbFzpI4^(@_jfLcc~gq7ZJ zi=pxzEzu0_Nhy@gIls@Y);UMB1OVHSwxm3&4U~{93qXW#v8)8;BjvXU1U{82xLl7N ze&kF|a}(a|UP3%rn~Kq;j30Gtw@^9NcMott3sv zS4~$V9oEy>lXPO*9$Qxwa!WCC4Wz>>p{kBJB-=BP@=-)Trv*vO9pe05&$S1lfPyGB zfb^eW)|RXG7z$2DdhGX3-!wPr826oG29$3&X$!0|jzTB`ii(E|0Zix`E&u*neyI9B zU5U1&I&fbpb}j>G0+ikqtK-~LlBn=ubci}C7*^kUez`*jPV5Ehzi?Z(&c#Y-X z&j1%Rmi_#T)|_vde52V!D51BdYuFVW2Xw4_HbMI>9q&ilzD)qt#*aOR^9;c9ufEq- zLNzyh8iO`BQCT*~rt>|GkO?gb(FA&uK(Kp7oQX~LLkDg{*XlwxmcU#Jb=EA}F$h-EvIyzO76 zjmLNnr&RR1XDGG7Z6+l&zc98A$pp)t<%#_Jgj`+LD5;WZ|2$Lksy0G?#24YMQX@Q% z8ahfr!cFn-Bd|3Yi3-u5CP8zJztxw^y0B8D@$YW%CnPmo_cocpe`fSZ8?H)plyFu4 z$W-Pz^PpyKH12~w33&kvo@GS}m_F5rfB8vBKk>kWSkr5gAC6WO^GH@jd7J!LRA1h8 z-PBMx>plM3hBZJfJKCgYAAoGu?|$XyeGMN>A&Zh&}7?JTI2?-MF1MTMivF#oKx z9#C-EDIlZ)_JsWLpqzC^+Uxb| zk2*~=5SW;gKG^aMy-)RTvShQ9e3#QonW+-5k-#GpeS7P}#OKASEJ{K0?LxQX3B5(s zCah5;$LH4{tR+{}@KuMa>$dUL9~xdv+j*$C7B4nsiX>KV)(5j7XM($`1K<}Tur5l> zn4y&dREx5rDQ0@ot6SKAv*C5&>c^DsumrXf1w`H3gaXH5jOMazHhIBdFrquOtHJIc zV>ubojQKtF4vXjyfx>+by#l%^_y|BR%8#;Fcv8L~2J2SfHZ+IccP2$4WaSUV9j=ny zXtD1AgvTn#>#(Ng=cSb2C(OQ7OU6#3hmC+-6*@(~YA(`O^w@~qk96WW#6fP6YeXW%#x>EBL>LX8mbVL*)cLcGYoWIxZ?T{nFH1I}u)u-elaKU^Y3T z%;Ft&iF|Yxg9E^E_h&u+81*x7LrCZ!edSV_0?lXEArHXMKb3nB?+v67oCLqLNjiPE zI|ZbfNEj$#VA5jhCKkO&wO=4_EAsJ5Z>*ANyds+#=u>L-ysutu!`&ro&Qf3>1X$H^ z;Z*?=4w#`xXATFp3lPv!ocA4{p9b(AS#TlT70PSlT1v)-dCOw-i*z<{y!am^=aT8e#k)=Um2u*1%^ zpu{A&EK!(#qWH$qqlN}LSs`4&&27+MRTLMkJf$<(RLq5f=H73q!- z36EksF&O3<+8Q-*lhG6#mxko5sGHPet|EKcC6+5074 zMNgbI$-rcOxp|OsEAsnHc=v^&SgFyjL-VLGHF^>oa~CN5r`nRm{jWmV6*xn`Z}rGB z_G#!x6}2Q@_F6~xhZ=pX3_U#0hC)d`A``H`E!`>x?#de8ld;Hrlb{6Zz z9Ml2%p-ctIF5+n^ek58Um*N)G+x6>E2fQIwZ~$bAISo3tY<6j(OoQcV{w8N7JpQR}h2|iw)$tMk0rdyZb=HD0IQD zj#pL~@lk~9GLmu61|JuYEsD&ST)*$)G-6fM%6@nGwd6H=4BKCwkdJLn4`(ab*tu{r z!tfQWvbTT_gb(AdYME3^nAc*E_l zQK+rDS?+S?u3-U~zm$!&AVy9^k9aDALo=S;Wl0F_?i(sZzllHnR}3PPY>yQ}b}a;s z*$7^43R8}sqSQ=-uX$5j_79}o#5UyO(SoC2j%-M%A9c$gEredV2iFcgq1%>@o(H9N zMAW0>EQ$$3H_a?1&j{DN{aeg)r_AGXe}?fz_TcKK&`+#zlX`ySK}+O>Vfj%8OSa~z#HMIXO}die4ICwC>%-QEDdxc(5s0Gy?x>! zBlW{zAn`tO-ff-FSGp+5cn`R;Thpd>Fl;|ss=$Pu4%{@9M%cO%Tmo01BD9Du{`Q%w z0EY8Zy?}VQ1jl_Odt>}aCY<*yI?Y=H`3#$)a{OV$#o4Kg8g*&7mttP3b7f+b&QV>? zDsrq&dM-V(+CK^a+7pl5wtaXKy2(e3Lzxnn{MtD%hVomjO;Wl zs#5qMGZ9;8xhLPEBcw1108zI~z0$#90(wuh1b?XKlHK*=A@h+6xwi~#)C%ozNGX-8 zS+m^d=Z5#Pg;t@H{4ArWqGSX`$^PIyy%BAK@yj2KV>YX!igE$_a1P`5h zp4Fb2;G66W5@n2tSn(}y@!8*x8hBEjd?ld!LD3=Mg?A3Y`N;;i>x1`oEn=HIGUVIGf`TofG?m4+W#Ej>yod>Q4Dowr}CW^=$M ztkLXFgXH4*xE|`jRij;ZaB>7r6BwPdDuv{HzGP*?rL_fQs}%P>M$q(O2Kgu{chae{ zBV(i`hMG6S+YuWvs^dDdvz59w*9_iR2M`_!XrGq48EleMtg!ll&)vKs4mLJyD@BoN z0|>oEz0bb^?P?l7=4@y77)5JZ;0II#KR^y->9T0E0Ot&#g!z zrfL{#lgA?m(H!Yad47GA94Rme#C$K=d9TX|J}*XK=CGn&lEWFjI#u@bsmtAgw(UCfg{I4{&8bNd)cdo)kdWz5mGV?wkDq|?y&-UHH z!Imsw#_ymHnlaZ3h?KSJjB+Av^uP%Y7?h&wf`7vfe};&-n0+`glRqxbn3~33Cc%K} zCjR-mgoT*t001+OCO z3w(H5c8WIm4Ne%3tHW&^%Qgb*Q-y{dp$f5}uxZcvr7^H(^Q}l5#0n`P|D%!Bov+29 z-bw47KR&9lcFr@Js&NaucP;?%&Mv3)4$}g7TY@$J;?oA(hz#)g0s`Okp5RQ2%|SvKgp>JMYD&_HTWV>pQy@M9$ru-)i>!v4XH{ zPp~I)d2F}5tf(z!59#CBIa0Obwkse?X9b~bxCSv?GQ$hv4@N&`XVD^*%!o4l8x<_a zA+k`RC`~r-p;t{WbJ0=}WhKRC6zg+^Wha`zXC`0ebzY5-)JWa;8uh2X`u`-j8yQ6v zOC3{vGZkLwIj|Ep_H>wZ?oeUIG_E{>IuPf+2<{TJGBO^nSW9!BBsW|NqBq2Sx}hY@ ztEyj!;@&O|I%E56EuqFKfpb(Ng|S zi6l~+SkYFpOD+uCJJ;It{a=)UlR*f-YZ{p%iI^yCmey>C9}vWdP-Y!>b26zo85;tY z8P`PLBoOhJRS9gVoeTQ3yZ=orJ0&8Mm+m7RYVJ+?D)PoD!@vv0Nw0>xoUeVRVY;Mv z9=ze0!9U#lZ^e9ivhuO)P#4$#H8tSoMnrtv9&7}r1M1r7kP)tZTPKBi<6NT9X>H6b zaQMA{nduha_d4f0EaKu|D6jzYW4&fPt~SvqEu)ujxmx|VyK@9&O^X;F3A=r6yeVu# zK&zj;MGq2tX})pC7pCF@hWc=*LA;;xGE7!`l^iFvu~%U4n!ea3eXPbrAeq%$+>#Yh z-IA0YhS&CLvwf!ls1+;OS*Q5&U2iuQaZ1cu-a6{=<`@3tyF5hLORT+nbnGxG z!>{As#j?;3Hu@=9{}n_Ml;iMU-9f$a9Vpj?9WEe16B{I(HRUSw)a)MziQ^~E*P}aI zHiM`i31(l$7HHU|XEUKx#5*b#?OR*OOe#^|?Rn)Iv3v2SJw_`rXSrjrwEMG5Ri?Qr z#f7lj`N9zNLZ_mLZ3U02yn%OWuH*=){kKl4S|GZ zJ5YIlRAAF2V7?`#Q(*iIuPnx%Aw4zfOoQ2^kmpGE51X~7-w`}5l?*%1ElC;I?GMdG zV*9k%%jl@zG%`WX@a%uU%vR&PKYP3VN@xa;^BOcNUpIUc{wr;Y*g^x&I)zx=ku$Q z(-j)=rQG-xTut9%k<5xv!K^$53m>Mv$ow7T{edMR-%pxWcw<;O+k^{DUhpc@E@{@F z#)cVx8bYfH3?jM^H#QyqT(Q?eW(wvUUuzJiqn|&STP#&(kpcwO!02v*40y^OMKt#h zv)SX2{ifd8Vs%)WI%6%j{<1m}@vIS(tum)C$gQP&`Fu#5g23PN(AQ6$nqQZ9v5s~= z`bGJ_E;3n_lPm@hE;(?jwl={A7z(k)R8cffljocpxYIPMb$>+@30)$fBYEwUjw#b9 z3XV^xp_At9dzbTpEL<+QG%1U%-%l94EG8;knb@F-TUbn>T1QzNl7bb@CPAuP!4@0? zj*!LVHBqqewA$pIe4m-~gDYY-dg_k1*OQtLI+LvBqc7gV`I7|1s9J0xO*bETcsnWX zkxtpCjKhy?FMIcZaU(wo{rMWVtGk3)EO$mqPyzO_VP=t0v1%e9c_Vd63iEy-8_@gTBdrIizyy3Z z+Mg(&J+XnU;&H-F$!PK;-=|sM4~33IXb$3uL5Y(;m=M~JZo_Uh#@_@z4-WYgPqZy5 zKrQeIT(fIb98(nrgobElbw-wS_~z;NX+1B_igY27EB@N5SS|I=OD)a!3rTWH!ND6Y zrcnzL$F||p05v=DPp#+kJhZc@`>DtG3Yb@BB;t^fkeTP@4D|JO8ezMS7U(B zx=@0?JrAca9 z_}FybrE%n+Z!(fjthd%-=y4lYVwW$RVL+T5@ItyBEnOWZIbGW#@T;wVxbELF%fCgo z@@+SJP;DtA@{R8Dlc0~^O8Oj~b!Fx!nCD#j1afR=cVfKje(dIGgU?W{rjh25PN zU}B5=S?lpic-Df`!!OyYvjL6uL7o;!vb^755rQ^b%>%3B_k97e7pZNg^530kHbmIA zm(EAi*};J4IPuoz%%X86mnA-ldN#X558mxTR5j)g?e4p{b*dlGa$rVmfXA{S`f{0T zfUR<4P3BqEYc8eBut`V=5=q(}uIeAR_m+gXJQyfN2rGljuC8E%R@!b;wX?&r*ADly zWITeso~Zx~2EDds7hWSx1n#gy&?N-a$C&!fuBkuv_~8AF94nmh@m4mHFq%T$3W#Rr za=-{X*=r)?LNfmETs4U;s-7St+d_3Z`~kr9^ezqkE~P!`-Mg%S+F|cVMX6T9KHi+e zQNAiyf-Q#P4a3IgBan%z#VhFN3ut~OU;*gek$)F58p(98B+C(v)h7wEYw7sE2+z~2qC5cHk8Xe{j+DPZ&p1Eoh9W^RU4d^Gb&TRq?J zi25fp(Z0<@^~bpByECH*O!o=y<2KP>c|M~34)m<@5c%uiL$HL!opW}|YIgUmfdmzv zlWJpmVdG^D7)t{rx*EHopm#@$u3mL!%UwNb6X#X3zLoH^@zN!xVJ;PNIb+EC;un86 z+5K1#X5kgneZ%N$*E_>R_<`+Sul6N@7+os8^aInlTKgI)dV4LcZvCA5J->*6J<%OK z6!&@=m53kb#BJR-vj4r4Gz5*8wCR+FKF0QVp-`^P4f5KBfc4Dm%&k9QLH~V__#G@$@%r4OW4%Vp7s1W7*)Oa9;|1dr+|FV0(Ym#xtd$$te(6nu-155nKBkC0@j z@2c#r!lJq1e@atM>4b-#L{aAQ;=7&a9;_erO^6Dl&4Z2mJ-a)diP59#rR4(oUC zIC&ib2x$R-jYd{PfALCl%Fcx6UY+Fpb}ECF*RPrFMW*+xzSvRcU63P7NFsS&(864M!S9aqZ1*dGyjTzm!xzewUADc1 z>2YXxP9i`Qel3cb#p^q@6K^Xn+$X=qcL;am*Xe7_WiEs43rtz^VQ2U>7mpVtI!NpU z3L^#_$Y=R^Y{U0MMN zThXIK_rbKd#V{y3x?1upDv}!|>pwur8pD8jukyYiSEIY=SAXL64d06M)h;WgVc)_` znC^PRMdbYerDr*jcm-|NHjNPAotqX~Z^gkNPUHydv@fbC9)pn)2NJqQIgPu6#5sey z7&P&1)K#ldPdi-lv; z)WcWpSKfX@!X34ga@gs@&#Y)M2UXIvaCh$J78^%2Nm~6Rh2%-Xv&>&^M%eH9h0NtM z09fqkz^_@qbW~W{!Q-C8Z^>G8+4-)zIxK_{p@Z2StD($PsyJneDH>UMMJC8`0V?j8 z269&NVpQdXDRdf!))G0Bks80FT*OQXW1m$b?)GX=5MHxbD~-L-wwZA!i`#)h`xrI6 z)Cmd}!yS!M_aVIRN;taqi}Whuc}y&L*jQ%_zB}H;Y(4(6@N;=itQOOAG%osygsJD* zef9Z?hrp)b>ba!%!?0PQh{zvyF)0+6Bn1J!rEld@c%U_D!u1}BwbU0YvZDkkyN>;@6f4A1 z0Vl!QO0vrEKKdH6o)gMCq}?&1@1N@7{k$JNqH8Bfk9G69DT zMtK_UEChKMb)+=xJ9V*sed12tw3`ZsBl?){!c6LaM}Ll_eM%;h<7Uh9`bA*)1-Ikl zS54H=FrW_fCW$uzz@RCyO zh+P85tK4!)5{ZuLTGEQ>v-ePgxif@o$T-cfC~b2ajF5_3JIl?Ylvu`?YU~_v6gFO6)T3ypp`Ccl_qoDukY+hi3;Ca#ie_q!DxqKaIsDH)svQrpD5T2%7bMd-E+zuZl8|m2k6rv>ycqm$2IF#FqQM{DO?ZzJF{T2g z9w1PqSsOln9d}reg6Kqc7LhD0Y(aIMBxz4CIPfE{ZfMco0ZMAwW`;w_lr2_>{tSl? zgN_wwrLvC9skr<9P|Hx!AJt9*GoKZ~0SQhlCRiUn^nWROnQ4r}qAFo-3MW>@%D=t} zMZiGE@aR)8PGaCJI3X&)Obpnh6r*v?05426F)Wl)AwRwri51ztJMICE3eO z=ryFWrTzfa{&lAxLT^hhZZD6iu^G7gb&f&MCMXqV<^OTEF~q}o%=iF#*vDG zE$sZXvmwFu!~C|Wo56r=1u*9}-2v&yT%P+ujZwC_x;Z_K(5$pGYAKtIvSM%|XG|{d zYK#?hRFVZ)(y4S3dvgyXWz`ah=uugangy*Q#GJ_4@RR(YDp^L@8?a&@FUwMSuQ+%x z6rF?2)^DNgmgu!s8Nu%nKCJMe{Awh!u^0nToUE*Eul9?7WMeyZU`)bitpbXzzZbLE zYxgo2Vg$#V7UaWX{L`!dSt{p)p+SghWwazC$FZKbZG>gHN_rp;FF8c*5=~i#Y5kjB z4_zzT7i(Xs=c4BPdQ`G+bqN=~?|)2;nPG4e`QEI)2eRh&4MU0(n9Xe8_aIBSzhtb| z*PXBUGEb0N`RkV0u@ zGX8{-*3J-p+fZae^U`Z}rulP}c{^If-7kd#q_Xt%HD^+YjPESii zWm_M5v^2ls)z`^2Jd77fZwo~z{Dhscefo`{1d+X1zzt7lP$}*!7aG`dc%dr?XE3jQ z(9N5j@MlK%O#9YjOp6LF_l8h#$T7MiiBGAFW3e$jNt}`4H>-wm1;kWv9tq9BSY%%M zt;qkrCVD+0FUbp6b4TPJv4niSpJYB+^+&Fd86iYJuzBXC0_InWxAz@#J34&TzC=Jh zGA|#6cy+ORwjh&ANqq+kTWeGtBEcQaGHaKMz!6aMm}x$kvhd^z!9bsbA~G+NBc1U` zBT9n>8@n)QjfWvl!)G3-JhAxr7J9c7{AL zsTohq6#D{uOsfrUj?%8T)8)B;N>F2hTNfUYscznjGzo6B(7(9Y*MutjJ7+ir|4xIR zUi($vyc=1xb?kz8}gf_O)_D54> zX3fJ~{bW#TR%I+|G91{NClMg!qt!YOT+|q$d%9I_GW8=ZKL03g29 z0rtUW3YJh$IcWzU8Iy6_C}IfD8f6(tGm7{fyHg5DKY%gUM)|=`WO;@CZ2KBwsnF%A&dRlYI+za zvxN*ygU(v986N+MpM#J162e8M`14tIOOGL2N^EvrY%`T8j;3v+5X4-{LI3a%btZ>v zH#!X&df)!W@e2=jY@KdAVdyQtJ)U4sJQ3hBXOCA8@J%{;#$mGOQIPtmLf%QpOA;L) zx?0!Z<3W@>93NN5;GeA^hk!(ekZxA1TnVbHRO@m5$cU~GvH%kSBQH+U*lV|GLXSqj z7Xg{C$v&+CpQu(~GNn3iWCymI=F{P57~o*cvpHyR6q@ygx8om0l zzR>IQZ2qkDSX|a36AmOHHskY(u@)6gcOgiQ9(kS#mfeREGc9Rk`m)}?+Kg^vCiQ*% zyE7uMc5$Tfi{WabhJq4bH=^5HdJ`=a5fw93eYhu~W^Kt{oJooIbNK9uD0SEe)eyPZ z5Q>5#uBAzjy;Nu=v(h-+Uggq|I)x0{%2yd=RQR-!xgPIf?OO#P?k;uOKyi!Y#bq0J zD@+keg%VlU#u4yIv*flA)6%+;3G$K@{IVV-LH>a!8(hmj8C30K^JtN?`8D0uoPjuJ zMlk>@i;cW_LAt$?ejjMmE`WrHS{wChP%DKo4JbKdrL+J^TT3+;>0EY43mwiGW|3?O zBu`J5MGbUxF3385CiwoCv8h7PdQM zSxA+6&hp4<%pFj$Qz}F9Ui}Gix`ccg7U=T(EL&(YiH4nl<(xScV@*_oF3XO1b=tkQ z71?5Et;JFwj2uG;HxvNyU5|8oOr|^3*~sPkb)j|i9MZDrseZl6cR5l=-?Vupla>4- zSno4Md5`-aaC~0k6-s8mD3DWRRItK^eM_m1f8UM7^Frz)f$-{C9LE6&Ly#Ii}?2*#498P zkeNK%4TV^!>cn5>XCO38o@OBsg(@9E1S3)mk&1e4tB%H&{{&-Zo5~ZK@CIF+qef;E z#bM+Q=gO04I0ty9H-?B(v+)?^uMe>YF%>-m7(3TAXPME|Yz)oDps;aD<$mlQ;U|{v zRCpa($hs_K24TSBVU0?5&V71u3xux0Xx0FhhVyh0mC6i573NVlt;QN(ZJh{gOm-qDPtPY~6~)A^KX;i44Oxa=zAB7z%I zO7X@OhQ9v_g=y0DA1A|_I(@)0Z?S@&fnW$jU`K2Aho6bC0Vfm5CBu~R zCy9^bL2U%7QAL8tW-NV_fQGrb+U2v0?YKv&;s$;nE8JDG90pb&03i#w1+>ancLH6F z1lkMjbHxy?i(e;xO9l#Ur;z|4zR17nN%OcVFbDt)m8~=Gn-+}Wh2728a5&6@p-gB9 zto;!k8AK7Ph;bkzgzN$qBql`qr){z$+!>7m$cVF~Rvg2XRk72Ox)_Eno0)?SSTkf5 zvLIt2+lnDIXuGat?WN{;`^HG=SlJz|n~lR`;(~Q5ZVoxY^$7qC_F;nKS3RS#DKs8$ zI!AWIy1!xj)cE%``Xe~r&AKb)F|gF$c0S*B8T=+>iufG#{p_pqvy9d zudlwlI1O9Z{7|xqPzB>ng3kf1ZLO>{)u35eV^#U+><}VHD8z{ilM5!@m2DW!1dE_> z5E_x6Y#`tOO+?2Jte_ZZ!_6gc=1fOfDMf**8ID1O=V!7(qn!$w@g){M!oXj`NJ4igaH?3ltH;0TeEQ$Y4_D|14~fgQBO zfTE&MQf(r10G?e40TwpI^PXQX2<<+2o$Sh%v=~#%o739L&hdGIVq$M|5p;FC|12QL z0a`scrA!d}ccxfK021(pn`32S&WcXw7~nfx&+z@pHy4pY;$zIg+VB50!EWb*V~)dB zcA&@=HKUEuQ9)!effMo>yYaq)^sh2tMn)HOGZhAV5;ebJ_-C*oTA9*j$5QKxpeHVP zMHv_+DK_x)KwJ0&^*MUr8veBx>uI%Ybuy4a98EJ7MTP7T%C6jsAS{v>T)(cdC+euk zYz`p`4?z2+I0ALUtDdKlL~1{43<1jhV`2UpLFkwN#5__wROh(?FNwMp25Eeryt*H~ zYPvL;h+>4wXWlB15tpop13tLlT?%x*vTt@p5bPCO2o<0$1bKFbak$^%xdq`-Sp@RP z!>9u@?9q!aN-9nDF{LeHY9DroQ}RedIY*eLPJNm~vxPh>L<9n&6HKZ^Mf!DZo{@gZly4ZtAf!u zPC8ilcR++GH8_Zb*@R#-N<%_orT#j}DVoUOIP>_XacM4s4f2^-v~LEoB-|H>J_u^kBN z`n0NgoQ8f$pn$nwKoo_+5=HQtHZZZglX5U=7SIeuf39`+x7`eu+dirX?L4o%azeHI zU^y#^S$Mhgfo>x!@)BJpIT*t%3SkLBPu!XU6wfZWln#)!vn-^#ww!r*Sq0l&Iya&7 zq$=gKg+X?O3rIfGK5S+qNXS8~$ajnkytXB3ghSRZH7-=tHRz->lMLIlYT5_E)LZ7z zG=2MF1nsPeEMk%;z@IXVNy;=EEBMTgr)Yo~Wf;w}7R#N(QL{|4(ad2sAyLk2q{l;z zGWclgWIz%X9VwG*vJV0neWo{;GRjn-8Cm!77%B((2r0QQreG$3m%PEEYx@P85O{m( zj&OXjmB{Tql0<0lV^vYvn+(We5D;X0Jf80ScA>LL0n(435RqaIK)`B?p7f8wBQ5aX zpEafAJIl#jK8TkZHS)tspx0DwYCMhO>_Etb*Fa1N1$&2Tr96D96-EixlLD%sa1cvJ zvDIZx*elZ>BS1P5cX`Pj=0A!92EOY(96oPa>ATkVP7V_?Ji;lVtn@^PlmKlm)zRg9 z`wjZk3??Lqse^mSAcXl+mSG_PMfqi{3lHGVNN3(9FF`|G{UL1EVq7vqJBs4O8QAr% zl!(iTELsbT%L?{eBm^3FmNeo?iE%kJu=JvD2I!hgChJxfhCuh&w|@<+uvP5!P{RtD z2-YaPidG;g(@Qqd4p0)fJ_VtdSQ_Zep%l$e@CeMuxn{kl*qAU#h?sVoGFip%Y^f3S z_1;|*MJ0g=9GH#h_o_lM07Z)PkCubs=jRE1bI-tVTDC$bxWF)P(~rPOq2-WRFCs(YN`snG z+z#;qq$pKcq}GCqu{0)1iGl6OiTXueo>emK{@Im9dy-tv2Yfs6y0y)M!esqTLK&lwl^FSZgwyDV*OW&Do7b62)h#&IIjOV=O^tZ=HT(~)0R<&6r@VQp%NrXIBR5yf*>G{kVnx$XXKG!b$+0y z_odiIvn8?}Pg{!R`I6`|9aSRt1iD8s9T#*ABdSYi3=CUn{OCHsyaDeSfzkqv5z5qL zhV;?~%L4>c%M_s<4w8JkW|SHLF}4ntk)hHGA?L9ExfEv&1Ua3!5{ain#8Cm@-+Ea| zW4yEmUr0!%p}P%=)+dpJPDWLmPtM2S#aKAI;&DGXI@{;$;=1N-!(?WV%;v-S#dz`o j!x{jHm-dM!L@tgKC!1~`DFP}XH6$TyA!EyeVAY!l>$s0Q literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..1205787b0ed50db71ebd4f8a7f85d106721ff258 GIT binary patch literal 22432 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 ig3W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ed760c0628b6a0026041f5b8bba466a0471fd2e0 GIT binary patch literal 23048 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..ff652e64356b538c001423b6aedefcf1ee66cd17 GIT binary patch literal 23188 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e786074813a27d0a7a249047832988d5bf0fe756 GIT binary patch literal 22248 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..8f445929ffb03b50e98c2a2f7d831a0cb1b276a2 GIT binary patch literal 20535 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 Z%0#`NexefPFV?Xj51H#Y#AC7WXn+Jg($4?@ literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..43e8b9e6cc061ff17fd2903075cbde12715512b3 GIT binary patch literal 23400 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 + + + + + + +
+ +

Global

+ + + + + + + +
+ +
+ +

+ +

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

(constant) colorpalette

+ + + + +
+

The color palette used by wplace.live

+
+ + + + + +
+ + + + +
Since:
+
  • 0.78.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

addBr(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a br to the overlay. +This br element will have properties shared between all br elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the br that are NOT shared between all overlay br elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the br.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <br> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addbr({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <br id="foo" class="bar">
+</body>
+ +
+ +
+ + +
+ + + +

addButton(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a button to the overlay. +This button element will have properties shared between all button elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.12
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the button that are NOT shared between all overlay button elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the button.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <button> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addButton({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <button id="foo" class="bar">Foobar.</button>
+</body>
+ +
+ +
+ + +
+ + + +

addButtonHelp(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a help button to the overlay. It will have a "?" icon unless overridden in callback. +On click, the button will attempt to output the title to the output element (ID defined in Overlay constructor). +This button element will have properties shared between all button elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.12
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the button that are NOT shared between all overlay button elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the button.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Examples
+ +
// Assume all help button elements have a shared class (e.g. {'className': 'bar'})
+overlay.addButtonHelp({'id': 'foo', 'title': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <button id="foo" class="bar" title="Help: Foobar.">?</button>
+</body>
+ +
// Assume all help button elements have a shared class (e.g. {'className': 'bar'})
+overlay.addButtonHelp({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <button id="foo" class="bar" title="Help: Foobar.">?</button>
+</body>
+ +
+ +
+ + +
+ + + +

addCaption(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table caption to the overlay. +This caption element will have properties shared between all caption elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the caption that are NOT shared between all overlay caption elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the caption.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <caption> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addCaption({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <caption id="foo" class="bar">Foobar.</caption>
+</body>
+ +
+ +
+ + +
+ + + +

addCheckbox(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a checkbox to the overlay. +This checkbox element will have properties shared between all checkbox elements in the overlay. +You can override the shared properties by using a callback. Note: the checkbox element is inside a label element.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.10
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the checkbox that are NOT shared between all overlay checkbox elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the checkbox.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all checkbox elements have a shared class (e.g. {'className': 'bar'})
+overlay.addCheckbox({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <label>
+    <input type="checkbox" id="foo" class="bar">
+    "Foobar."
+  </label>
+</body>
+ +
+ +
+ + +
+ + + +

addDetails(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a details to the overlay. +This details element will have properties shared between all details elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.96
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the details that are NOT shared between all overlay details elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the details.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <details> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addDetails({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <details id="foo" class="bar"></details>
+</body>
+ +
+ +
+ + +
+ + + +

addDiv(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a div to the overlay. +This div element will have properties shared between all div elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the div that are NOT shared between all overlay div elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the div.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <div> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addDiv({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <div id="foo" class="bar"></div>
+</body>
+ +
+ +
+ + +
+ + + +

addDragbar(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a dragbar div element to the overlay. +This dragbar element will have properties shared between all dragbar elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.145
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the dragbar that are NOT shared between all overlay dragbars. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the dragbar.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all dragbar elements have a shared class (e.g. {'className': 'bar'})
+overlay.addDragbar({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <div id="foo" class="bar">Foobar.</div>
+</body>
+ +
+ +
+ + +
+ + + +

addFieldset(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a fieldset to the overlay. +This fieldset element will have properties shared between all fieldset elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.246
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the fieldset that are NOT shared between all overlay fieldset elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the fieldset.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <fieldset> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addFieldset({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <fieldset id="foo" class="bar"></fieldset>
+</body>
+ +
+ +
+ + +
+ + + +

addForm(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a form to the overlay. +This form element will have properties shared between all form elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.246
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the form that are NOT shared between all overlay form elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the form.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <form> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addForm({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <form id="foo" class="bar"></form>
+</body>
+ +
+ +
+ + +
+ + + +

addHeader(level, additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a header to the overlay. +This header element will have properties shared between all header elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.7
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
level + + +number + + + + + + + + + + + + +

The header level. Must be between 1 and 6 (inclusive)

+ +
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the header that are NOT shared between all overlay header elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the header.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all header elements have a shared class (e.g. {'className': 'bar'})
+overlay.addHeader(6, {'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <h6 id="foo" class="bar">Foobar.</h6>
+</body>
+ +
+ +
+ + +
+ + + +

addHr(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a hr to the overlay. +This hr element will have properties shared between all hr elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.7
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the hr that are NOT shared between all overlay hr elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the hr.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <hr> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addhr({'id': 'foo'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <hr id="foo" class="bar">
+</body>
+ +
+ +
+ + +
+ + + +

addImg(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a img to the overlay. +This img element will have properties shared between all img elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the img that are NOT shared between all overlay img elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the img.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <img> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addimg({'id': 'foo', 'src': './img.png'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <img id="foo" src="./img.png" class="bar">
+</body>
+ +
+ +
+ + +
+ + + +

addInput(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a input to the overlay. +This input element will have properties shared between all input elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.13
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the input that are NOT shared between all overlay input elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the input.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <input> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addInput({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <input id="foo" class="bar">Foobar.</input>
+</body>
+ +
+ +
+ + +
+ + + +

addInputFile(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a file input to the overlay with enhanced visibility controls. +This input element will have properties shared between all file input elements in the overlay. +Uses multiple hiding methods to prevent browser native text from appearing during minimize/maximize. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.17
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the file input that are NOT shared between all overlay file input elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the file input.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all file input elements have a shared class (e.g. {'className': 'bar'})
+overlay.addInputFile({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <div>
+    <input type="file" id="foo" class="bar" style="display: none"></input>
+    <button>Foobar.</button>
+  </div>
+</body>
+ +
+ +
+ + +
+ + + +

addLegend(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a legend to the overlay. +This legend element will have properties shared between all legend elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.246
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the legend that are NOT shared between all overlay legend elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the legend.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <legend> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addLegend({'id': 'foo', textContent: 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <legend id="foo" class="bar">
+    "Foobar."
+  </legend>
+</body>
+ +
+ +
+ + +
+ + + +

addLi(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a list item to the overlay. +This li element will have properties shared between all li elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the li that are NOT shared between all overlay li elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the li.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <li> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addLi({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <li id="foo" class="bar">Foobar.</li>
+</body>
+ +
+ +
+ + +
+ + + +

addMenu(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a menu to the overlay. +This menu element will have properties shared between all menu elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the menu that are NOT shared between all overlay menu elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the menu.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <menu> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addMenu({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <menu id="foo" class="bar">Foobar.</menu>
+</body>
+ +
+ +
+ + +
+ + + +

addOl(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds an ordered list to the overlay. +This ol element will have properties shared between all ol elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the ol that are NOT shared between all overlay ol elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the ol.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <ol> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addOl({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <ol id="foo" class="bar">Foobar.</ol>
+</body>
+ +
+ +
+ + +
+ + + +

addOption(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds an option to the overlay. +This option element will have properties shared between all option elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.244
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the option that are NOT shared between all overlay option elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the option.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <option> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addOption({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <option id="foo" class="bar">Foobar.</option>
+</body>
+ +
+ +
+ + +
+ + + +

addP(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a p to the overlay. +This p element will have properties shared between all p elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the p that are NOT shared between all overlay p elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the p.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <p> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addP({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <p id="foo" class="bar">Foobar.</p>
+</body>
+ +
+ +
+ + +
+ + + +

addSelect(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a label & select element to the overlay. +This select element will have properties shared between all select elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.243
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the checkbox that are NOT shared between all overlay select elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the label/select elements.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all select elements have a shared class (e.g. {'className': 'bar'})
+overlay.addSelect({'id': 'foo', 'textContent': 'Foobar: '}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <label for="foo">
+    "Foobar: "
+  </label>
+  <select id="foo" class="bar"></select>
+</body>
+ +
+ +
+ + +
+ + + +

addSmall(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a small to the overlay. +This small element will have properties shared between all small elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.55.8
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the small that are NOT shared between all overlay small elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the small.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <small> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addSmall({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <small id="foo" class="bar">Foobar.</small>
+</body>
+ +
+ +
+ + +
+ + + +

addSpan(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a span to the overlay. +This span element will have properties shared between all span elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.55.8
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the span that are NOT shared between all overlay span elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the span.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <span> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addSpan({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <span id="foo" class="bar">Foobar.</span>
+</body>
+ +
+ +
+ + +
+ + + +

addSummary(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a summary to the overlay. +This summary element will have properties shared between all summary elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.96
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the summary that are NOT shared between all overlay summary elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the summary.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <summary> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addSummary({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <summary id="foo" class="bar">Foobar.</summary>
+</body>
+ +
+ +
+ + +
+ + + +

addTable(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table to the overlay. +This table element will have properties shared between all table elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the table that are NOT shared between all overlay table elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the table.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <table> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTable({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <table id="foo" class="bar">Foobar.</table>
+</body>
+ +
+ +
+ + +
+ + + +

addTbody(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table body to the overlay. +This tbody element will have properties shared between all tbody elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the tbody that are NOT shared between all overlay tbody elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the tbody.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <tbody> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTbody({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <tbody id="foo" class="bar">Foobar.</tbody>
+</body>
+ +
+ +
+ + +
+ + + +

addTd(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table data cell to the overlay. +This td element will have properties shared between all td elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the td that are NOT shared between all overlay td elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the td.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <td> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTd({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <td id="foo" class="bar">Foobar.</td>
+</body>
+ +
+ +
+ + +
+ + + +

addTextarea(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a textarea to the overlay. +This textarea element will have properties shared between all textarea elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.13
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the textarea that are NOT shared between all overlay textarea elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the textarea.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <textarea> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTextarea({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <textarea id="foo" class="bar">Foobar.</textarea>
+</body>
+ +
+ +
+ + +
+ + + +

addTfoot(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table footer to the overlay. +This tfoot element will have properties shared between all tfoot elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the tfoot that are NOT shared between all overlay tfoot elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the tfoot.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <tfoot> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTfoot({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <tfoot id="foo" class="bar">Foobar.</tfoot>
+</body>
+ +
+ +
+ + +
+ + + +

addTh(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table header (label) cell to the overlay. +This th element will have properties shared between all th elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the th that are NOT shared between all overlay th elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the th.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <th> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTh({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <th id="foo" class="bar">Foobar.</th>
+</body>
+ +
+ +
+ + +
+ + + +

addThead(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table header to the overlay. +This thead element will have properties shared between all thead elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the thead that are NOT shared between all overlay thead elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the thead.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <thead> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addThead({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <thead id="foo" class="bar">Foobar.</thead>
+</body>
+ +
+ +
+ + +
+ + + +

addTimer(endDateopt, updateIntervalopt, additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a timer time element to the overlay. +This timer will countdown until it reaches the end date that was passed in. +Additionally, you can update the end date by changing the endDate dataset attribute on the element. +Timer elements are not localized. Output is HH:MM:SS with no units. +This timer will have properties shared between all timers in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.313
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
endDate + + +Date + + + + + + <optional>
+ + + + + +
+ + Date.now() + + +

The time to count down to.

+ +
updateInterval + + +number + + + + + + <optional>
+ + + + + +
+ + 500 + + +

The time in milliseconds to update the display of the timer. Default is 500 milliseconds.

+ +
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the timer that are NOT shared between all overlay timers. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the timer.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all timers have a shared class (e.g. {'className': 'bar'})
+overlay.addTimer(Date.now() + 2211632704000, 500, {'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <time id="bm-timer-dh8fhw80" class="bar" datetime="PT27H34M56S" data-end-date="1771749296000">27:34:56</div>
+</body>
+ +
+ +
+ + +
+ + + +

addTr(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds a table row to the overlay. +This tr element will have properties shared between all tr elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the tr that are NOT shared between all overlay tr elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the tr.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <tr> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addTr({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <tr id="foo" class="bar">Foobar.</tr>
+</body>
+ +
+ +
+ + +
+ + + +

addUl(additionalPropertiesopt, callbackopt) → {Overlay}

+ + + + + +
+

Adds an unordered list to the overlay. +This ul element will have properties shared between all ul elements in the overlay. +You can override the shared properties by using a callback.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
additionalProperties + + +Object.<string, any> + + + + + + <optional>
+ + + + + +
+ + {} + + +

The DOM properties of the ul that are NOT shared between all overlay ul elements. These should be camelCase.

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + ()=>{} + + +

Additional JS modification to the ul.

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
// Assume all <ul> elements have a shared class (e.g. {'className': 'bar'})
+overlay.addUl({'id': 'foo', 'textContent': 'Foobar.'}).buildOverlay(document.body);
+// Output:
+// (Assume <body> already exists in the webpage)
+<body>
+  <ul id="foo" class="bar">Foobar.</ul>
+</body>
+ +
+ +
+ + +
+ + + +

base64ToUint8(base64) → {Uint8Array}

+ + + + + +
+

Decodes a base 64 encoded Uint8 array using the browser's built-in ASCII to binary function

+
+ + + + + +
+ + + + +
Since:
+
  • 0.72.9
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
base64 + + +Uint8Array + + + + +

The base 64 encoded Uint8Array to convert

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Uint8Array + + +
+
+ + +
+

The decoded Uint8Array

+
+ + +
+ + + +
+ + +
+ + + +

buildElement() → {Overlay}

+ + + + + +
+

Finishes building an element. +Call this after you are finished adding children. +If the element will have no children, call it anyways.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Overlay + + +
+
+ + +
+

Overlay class instance (this)

+
+ + +
+ + + +
+
Example
+ +
overlay
+  .addDiv()
+    .addHeader(1).buildElement() // Breaks out of the <h1>
+    .addP().buildElement() // Breaks out of the <p>
+  .buildElement() // Breaks out of the <div>
+  .addHr() // Since there are no more elements, calling buildElement() is optional
+.buildOverlay(document.body);
+ +
+ +
+ + +
+ + + +

buildHighlight()

+ + + + + +
+

Builds the highlight section of the window. +This should be overriden by SettingsManager

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildHighlight()

+ + + + + +
+

Builds the "highlight" category of the settings window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.18
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • WindowSettings#buildHighlight
  • +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildOverlay(parent)

+ + + + + +
+

Finishes building the overlay and displays it. +Call this when you are done chaining methods.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
parent + + +HTMLElement + + + + +

The parent HTMLElement this overlay should be appended to as a child.

+ +
+ + + + + + + + + + + + + + + + +
+
Example
+ +
overlay
+  .addDiv()
+    .addP().buildElement()
+  .buildElement()
+.buildOverlay(document.body); // Adds DOM structure to document body
+// <div><p></p></div>
+ +
+ +
+ + +
+ + + +

buildTemplate()

+ + + + + +
+

Builds the template section of the window. +This should be overriden by SettingsManager

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.68
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildTemplate()

+ + + + + +
+

Build the "template" category of settings window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.68
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • WindowSettings#buildTemplate
  • +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildWindow()

+ + + + + +
+

Creates the main Blue Marble window. +Parent/child relationships in the DOM structure below are indicated by indentation.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.58.3
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

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.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.149
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) buildWindow()

+ + + + + +
+

Spawns a telemetry window. +If another telemetry window already exists, we DON'T spawn another! +Parent/child relationships in the DOM structure below are indicated by indentation.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.339
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildWindow()

+ + + + + +
+

Spawns a Template Wizard window. +If another template wizard window already exists, we DON'T spawn another! +Parent/child relationships in the DOM structure below are indicated by indentation.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.434
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildWindow()

+ + + + + +
+

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
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildWindow()

+ + + + + +
+

Spawns a Settings window. +If another settings window already exists, we DON'T spawn another! +Parent/child relationships in the DOM structure below are indicated by indentation.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

buildWindowed()

+ + + + + +
+

Spawns a windowed 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.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.35
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

calculateCanvasTransparency(param) → {boolean}

+ + + + + +
+

Detects if the canvas is transparent.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.75
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
param + + +Object + + + + +

Object that contains the parameters for the function

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bitmap + + +ImageBitmap + + + + +

The bitmap template image

+ +
bitmapParams + + +Array.<number, number, number, number> + + + + +

The parameters to obtain the template tile image from the bitmap

+ +
transCanvas + + +OffscreenCanvas +| + +HTMLCanvasElement + + + + +

The canvas to draw to in order to calculate this

+ +
transContext + + +OffscreenCanvasRenderingContext2D + + + + +

The context for the transparent canvas to draw to

+ +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + +
+

Is the canvas transparent? If transparent, then true is returned. Otherwise, false.

+
+ + +
+ + + +
+ + +
+ + + +

calculateCoordsFromChunked()

+ + + + + +
+

Calculates top left coordinate of template. +It uses Template.chunked to update Template.coords

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.504
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

calculateRelativeLuminance(array) → {Number}

+ + + + + +
+

Calcualtes the relative luminance of an RGB value

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.180
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
array + + +Array.<Number, Number, Number> + + + + +

The RGB values as an array

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Number + + +
+
+ + +
+

The relative luminance as a Number

+
+ + +
+ + + +
+ + +
+ + + +

colorpaletteForBlueMarble()

+ + + + + +
+

Processes the palette used for Blue Marble. +Each ID is sorted from smallest to largest. +Color ID's are integers, which can be negative. +Custom colors have been added for the Blue Marble purposes. +Wplace palette colors have not been modified.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.6
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

consoleError(…args)

+ + + + + +
+

Bypasses terser's stripping of console function calls. +This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. +However, the distributed version needs to call the console somehow, so this wrapper function is how. +This is the same as console.error().

+
+ + + + + +
+ + + + +
Since:
+
  • 0.58.13
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
args + + +any + + + + + + + + + + <repeatable>
+ +
+

Arguments to be passed into the error() function of the Console

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

consoleLog(…args)

+ + + + + +
+

Bypasses terser's stripping of console function calls. +This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. +However, the distributed version needs to call the console somehow, so this wrapper function is how. +This is the same as console.log().

+
+ + + + + +
+ + + + +
Since:
+
  • 0.58.9
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
args + + +any + + + + + + + + + + <repeatable>
+ +
+

Arguments to be passed into the log() function of the Console

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

consoleWarn(…args)

+ + + + + +
+

Bypasses terser's stripping of console function calls. +This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. +However, the distributed version needs to call the console somehow, so this wrapper function is how. +This is the same as console.warn().

+
+ + + + + +
+ + + + +
Since:
+
  • 0.58.13
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
args + + +any + + + + + + + + + + <repeatable>
+ +
+

Arguments to be passed into the warn() function of the Console

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) convertTemplateToBlob() → {Promise.<Blob>}

+ + + + + +
+

Converts a Template class instance into a Blob. +Specifically, this takes Template.chunked and converts it to a Blob.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.504
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<Blob> + + +
+
+ + +
+

A Promise of a Blob PNG image of the template

+
+ + +
+ + + +
+ + +
+ + + +

createConfetti(parentElement)

+ + + + + +
+

Immedently creates confetti inside the parent element.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.356
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
parentElement + + +HTMLElement + + + + +

The parent element to create confetti inside of

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) createJSON() → {Object}

+ + + + + +
+

Creates the JSON object to store templates in

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.4
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Object + + +
+
+ + +
+

The JSON object

+
+ + +
+ + + +
+ + +
+ + + +

createObserverBody(target) → {Observers}

+ + + + + +
+

Creates the MutationObserver for document.body

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
target + + +HTMLElement + + + + +

Targeted element to watch

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Observers + + +
+
+ + +
+

this (Observers class)

+
+ + +
+ + + +
+ + +
+ + + +

(async) createTemplate(blob, name, coords)

+ + + + + +
+

Creates the template from the inputed file blob

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.77
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
blob + + +File + + + + +

The file blob to create a template from

+ +
name + + +string + + + + +

The display name of the template

+ +
coords + + +Array.<number, number, number, number> + + + + +

The coordinates of the top left corner of the template

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) createTemplateTiles(tileSize, paletteBM, shouldSkipTransTiles, shouldAggSkipTransTiles) → {Object}

+ + + + + +
+

Creates chunks of the template for each tile.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.4
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tileSize + + +Number + + + + +

Size of the tile as determined by templateManager

+ +
paletteBM + + +Object + + + + +

An collection of Uint32Arrays containing the palette BM uses

+ +
shouldSkipTransTiles + + +boolean + + + + +

Should transparent tiles be skipped over when creating the template?

+ +
shouldAggSkipTransTiles + + +boolean + + + + +

Should transparent tiles be aggressively skipped over when creating the template?

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Object + + +
+
+ + +
+

Collection of template bitmaps & buffers organized by tile coordinates

+
+ + +
+ + + +
+ + +
+ + + +

deleteTemplate()

+ + + + + +
+

Deletes a template from the JSON object. +Also delete's the corrosponding Template class instance

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) disableTemplate()

+ + + + + +
+

Disables the template from view

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) downloadAllTemplates()

+ + + + + +
+

Downloads all templates loaded.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.499
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) downloadAllTemplatesFromStorage()

+ + + + + +
+

Downloads all templates from Blue Marble's template storage.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.474
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) downloadTemplate(template)

+ + + + + +
+

Downloads the template passed-in.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.499
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
template + + +Template + + + + +

The template class instance to download

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) drawTemplateOnTile(tileBlob, tileCoords)

+ + + + + +
+

Draws all templates on the specified tile. +This method handles the rendering of template overlays on individual tiles.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.77
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tileBlob + + +File + + + + +

The pixels that are placed on a tile

+ +
tileCoords + + +Array.<number> + + + + +

The tile coordinates [x, y]

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

encodedToNumber(encoded, encoding) → {number}

+ + + + + +
+

Decodes a number from a custom encoded string.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.448
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
encoded + + +string + + + + +

The encoded string

+ +
encoding + + +string + + + + +

The characters to use when decoding

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + +
+

Decoded number

+
+ + +
+ + + +
+
Example
+ +
const encode = '012abcABC'; // Base 9
+console.log(encodedToNumber('0', encode));     // 0
+console.log(encodedToNumber('c', encode));     // 5
+console.log(encodedToNumber('1A', encode));    // 15
+console.log(encodedToNumber('1BCaA', encode)); // 12345
+ +
+ +
+ + +
+ + + +

escapeHTML(text) → {string}

+ + + + + +
+

Sanitizes HTML to display as plain-text. +This prevents some Cross Site Scripting (XSS). +This is handy when you are displaying user-made data, and you must use innerHTML.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.44.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
text + + +string + + + + +

The text to sanitize

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

HTML escaped string

+
+ + +
+ + + +
+
Example
+ +
const paragraph = document.createElement('p');
+paragraph.innerHTML = escapeHTML('<u>Foobar.</u>');
+// Output:
+// (Does not include the paragraph element)
+// (Output is not HTML formatted)
+<p>
+  "<u>Foobar.</u>"
+</p>
+ +
+ +
+ + +
+ + + +

getClipboardData(eventopt) → {string}

+ + + + + +
+

Handles reading from the clipboard. +Assume this only returns text. +Assume this requires user input.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.426
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
event + + +ClipboardEvent + + + + + + <optional>
+ + + + + +
+

(Optional) The clipboard event that triggered this to run

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

The clipboard data as a string

+
+ + +
+ + + +
+ + +
+ + + +

getObserverBody() → {MutationObserver}

+ + + + + +
+

Retrieves the MutationObserver that watches document.body

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +MutationObserver + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getWplaceVersion() → {Date|undefined}

+ + + + + +
+

Returns a Date of when Wplace was last updated. +This is obtained from a certain DOM element which contains the version of Wplace.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.25
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Date +| + +undefined + + +
+
+ + +
+
    +
  • The date that Wplace was last updated, as a Date.
  • +
+
+ + +
+ + + +
+ + +
+ + + +

handleDisplayError(text)

+ + + + + +
+

Handles error display. +This will output plain text into the output Status box. +Additionally, this will output an error to the console.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.41.6
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
text + + +string + + + + +

The error text to display.

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

handleDisplayStatus(text)

+ + + + + +
+

Handles status display. +This will output plain text into the output Status box. +Additionally, this will output an info message to the console.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.58.4
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
text + + +string + + + + +

The status text to display.

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

handleDrag(moveMeSelector, iMoveThingsSelector)

+ + + + + +
+

Handles dragging of the overlay. +Uses requestAnimationFrame for smooth animations and GPU-accelerated transforms. +Make sure to use the appropriate CSS selectors.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.8.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
moveMeSelector + + +string + + + + +

The element to be moved

+ +
iMoveThingsSelector + + +string + + + + +

The drag handle element

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

handleMinimization(button)

+ + + + + +
+

Handles the minimization logic for windows spawned by Blue Marble

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.142
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
button + + +HTMLButtonElement + + + + +

The UI button that triggered this minimization event

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

hexToRGB(hex) → {Array.<number, number, number>}

+ + + + + +
+

Converts a hexdecimal color to an RGB color. +Alpha channel not supported.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.31
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hex + + +string + + + + +

Hex color code as string

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Array.<number, number, number> + + +
+
+ + +
+

RGB color as an Array

+
+ + +
+ + + +
+ + +
+ + + +

importJSON(json)

+ + + + + +
+

Imports the JSON object, and appends it to any JSON object already loaded

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
json + + +string + + + + +

The JSON string to parse

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

inject(callback)

+ + + + + +
+

Injects code into the client +This code will execute outside of TamperMonkey's sandbox

+
+ + + + + +
+ + + + +
Since:
+
  • 0.11.15
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + + +* + + + + +

The code to execute

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

localizeDate(date) → {string}

+ + + + + +
+

Returns the localized date format.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.472
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +number + + + + +

The date to localize

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Localized date as a string

+
+ + +
+ + + +
+ + +
+ + + +

localizeDuration(durationTotalMs) → {string}

+ + + + + +
+

Returns the localized duration format.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.472
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
durationTotalMs + + +number + + + + +

The duration to localize, in milliseconds

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Localized duration as a string

+
+ + +
+ + + +
+ + +
+ + + +

localizeNumber(number) → {string}

+ + + + + +
+

Returns the localized number format.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.472
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
number + + +number + + + + +

The number to localize

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Localized number as a string

+
+ + +
+ + + +
+ + +
+ + + +

localizePercent(percent) → {string}

+ + + + + +
+

Returns the localized percentage format.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.472
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
percent + + +number + + + + +

The percentage to localize

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Localized percentage as a string

+
+ + +
+ + + +
+ + +
+ + + +

negativeSafeModulo(a, b) → {number}

+ + + + + +
+

Negative-Safe Modulo. You can pass negative numbers into this.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.55.8
+ + + + + + + + + + + + + + + +
Author:
+
+
    +
  • osuplace
  • +
+
+ + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
a + + +number + + + + +

The first number

+ +
b + + +number + + + + +

The second number

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + +
+

Result

+
+ + +
+ + + +
+ + +
+ + + +

numberToEncoded(number, encoding) → {string}

+ + + + + +
+

Encodes a number into a custom encoded string.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
number + + +number + + + + +

The number to encode

+ +
encoding + + +string + + + + +

The characters to use when encoding

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Encoded string

+
+ + +
+ + + +
+
Example
+ +
const encode = '012abcABC'; // Base 9
+console.log(numberToEncoded(0, encode)); // 0
+console.log(numberToEncoded(5, encode)); // c
+console.log(numberToEncoded(15, encode)); // 1A
+console.log(numberToEncoded(12345, encode)); // 1BCaA
+ +
+ +
+ + +
+ + + +

observe(observer, watchChildList, watchSubtree)

+ + + + + +
+

Observe a MutationObserver

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
observer + + +MutationObserver + + + + + + +

The MutationObserver

+ +
watchChildList + + +boolean + + + + + + false + + +

(Optional) Should childList be watched? False by default

+ +
watchSubtree + + +boolean + + + + + + false + + +

(Optional) Should childList be watched? False by default

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

observeBlack()

+ + + + + +
+

Observe the black color, and add the "Move" button.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.66.3
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

rgbToHex(red, greenopt, blueopt) → {string}

+ + + + + +
+

Converts an RGB color to hexdecimal color. +Octothorpe not included.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.31
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
red + + +number +| + +Array.<number, number, number> + + + + + + + + + + +

The Red channel of the RGB color, or all three channels as an Array

+ +
green + + +number + + + + + + <optional>
+ + + + + +
+

The Green channel of the RGB color

+ +
blue + + +number + + + + + + <optional>
+ + + + + +
+

The Blue channel of the RGB color

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + +
+

Hex color code as string

+
+ + +
+ + + +
+ + +
+ + + +

selectAllCoordinateInputs() → {Array.<Element>}

+ + + + + +
+

Returns the coordinate input fields

+
+ + + + + +
+ + + + +
Since:
+
  • 0.74.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Array.<Element> + + +
+
+ + +
+

The 4 coordinate Inputs

+
+ + +
+ + + +
+ + +
+ + + +

serverTPtoDisplayTP(tile, pixel) → {Array.<number, number>}

+ + + + + +
+

Converts the server tile-pixel coordinate system to the displayed tile-pixel coordinate system.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.42.4
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tile + + +Array.<string, string> + + + + +

The tile to convert

+ +
pixel + + +Array.<string, string> + + + + +

The pixel to convert

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Array.<number, number> + + +
+
+ + +
+

Tile and pixel coordinate pair

+
+ + +
+ + + +
+
Example
+ +
console.log(serverTPtoDisplayTP(['12', '123'], ['34', '567'])); // [34, 3567]
+ +
+ +
+ + +
+ + + +

setApiManager(apiManager)

+ + + + + +
+

Populates the apiManager variable with the apiManager class.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.41.4
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
apiManager + + +ApiManager + + + + +

The apiManager class instance

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

setSettingsManager(settingsManager)

+ + + + + +
+

Populates the settingsManager variable with the settingsManager class.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
settingsManager + + +SettingsManager + + + + +

The settingsManager class instance

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

setSettingsManager(settingsManager)

+ + + + + +
+

Updates the stored instance of the SettingsManager.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.54
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
settingsManager + + +SettingsManager + + + + +

The settings manager instance

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

setTemplatesShouldBeDrawn(value)

+ + + + + +
+

Sets the templatesShouldBeDrawn boolean to a value.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.73.7
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
value + + +boolean + + + + +

The value to set the boolean to

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

setWindowMain(windowMain)

+ + + + + +
+

Updates the stored instance of the main window.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.54
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
windowMain + + +WindowMain + + + + +

The main window instance

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

sleep(time) → {Promise}

+ + + + + +
+

Halts execution of this specific userscript, for the specified time. +This will not block the thread.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.483
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
time + + +number + + + + +

Time to wait in milliseconds

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

Promise of a setTimeout()

+
+ + +
+ + + +
+ + +
+ + + +

spontaneousResponseListener(overlay)

+ + + + + +
+

Determines if the spontaneously received response is something we want. +Otherwise, we can ignore it. +Note: Due to aggressive compression, make your calls like data['jsonData']['name'] instead of data.jsonData.name

+
+ + + + + +
+ + + + +
Since:
+
  • 0.11.1
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
overlay + + +Overlay + + + + +

The Overlay class instance

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

toggleFlag(flagName, stateopt)

+ + + + + +
+

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.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.60
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
flagName + + +string + + + + + + + + + + +

The name of the flag to toggle

+ +
state + + +boolean + + + + + + <optional>
+ + + + + +
+

(Optional) The state to change the flag to

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

uint8ToBase64(uint8) → {Uint8Array}

+ + + + + +
+

Converts a Uint8 array to base64 using the browser's built-in binary to ASCII function

+
+ + + + + +
+ + + + +
Since:
+
  • 0.72.9
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
uint8 + + +Uint8Array + + + + +

The Uint8Array to convert

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Uint8Array + + +
+
+ + +
+

The base64 encoded Uint8Array

+
+ + +
+ + + +
+ + +
+ + + +

updateColorList() → {Object.<number, ColorData>}

+ + + + + +
+

Updates the information inside the colors in the color list. +If the color list does not exist yet, it returns the color information instead. +This assumes the information inside each element is the same between fullscreen and windowed mode.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.60
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Object.<number, ColorData> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

updateInnerHTML(id, html, doSafeopt)

+ + + + + +
+

Updates the inner HTML of the element. +The element is discovered by it's id. +If the element is an input, it will modify the value attribute instead.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.24.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
id + + +string + + + + + + + + + + + + +

The ID of the element to change

+ +
html + + +string + + + + + + + + + + + + +

The HTML/text to update with

+ +
doSafe + + +boolean + + + + + + <optional>
+ + + + + +
+ + false + + +

(Optional) Should textContent be used instead of innerHTML to avoid XSS? False by default

+ +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(async) updateUserStorage()

+ + + + + +
+

Updates the user settings in userscript storage

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.39
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

viewCanvasInNewTab(canvas, lifeDurationopt)

+ + + + + +
+

View the canvas in a new tab.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.484
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
canvas + + +HTMLCanvasElement +| + +OffscreenCanvas + + + + + + + + + + + + +

The canvas to view

+ +
lifeDuration + + +number + + + + + + <optional>
+ + + + + +
+ + 60_000 + + +

(Optional) The lifetime of the URL blob in milliseconds

+ +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+

ColorData

+ + + + +
+

The information about a specific color on the palette.

+
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
colorTotal + + +number +| + +string + + + +
colorTotalLocalized + + +string + + + +
colorCorrect + + +number +| + +string + + + +
colorCorrectLocalized + + +string + + + +
colorPercent + + +string + + + +
colorIncorrect + + +number + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
+ + + + + +
+ +
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..bb085e4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,162 @@ + + + + + + Home - Documentation + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +

+ main.js +

+ + +
+ +
+
+ + +

The main file. Everything in the userscript is executed from here.

+ + + + + +
+ + + + +
Since:
+
  • 0.0.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/main.js.html b/docs/main.js.html new file mode 100644 index 0000000..44ce400 --- /dev/null +++ b/docs/main.js.html @@ -0,0 +1,344 @@ + + + + + + main.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

main.js

+ + + + + + + +
+
+
/** @file The main file. Everything in the userscript is executed from here.
+ * @since 0.0.0
+ */
+
+import Observers from './observers.js';
+import ApiManager from './apiManager.js';
+import TemplateManager from './templateManager.js';
+import { consoleLog, consoleWarn } from './utils.js';
+import WindowMain from './WindowMain.js';
+import WindowTelemetry from './WindowTelemetry.js';
+import SettingsManager from './settingsManager.js';
+
+const name = GM_info.script.name.toString(); // Name of userscript
+const version = GM_info.script.version.toString(); // Version of userscript
+const consoleStyle = 'color: cornflowerblue;'; // The styling for the console logs
+
+/** Injects code into the client
+ * This code will execute outside of TamperMonkey's sandbox
+ * @param {*} callback - The code to execute
+ * @since 0.11.15
+ */
+function inject(callback) {
+    const script = document.createElement('script');
+    script.setAttribute('bm-name', name); // Passes in the name value
+    script.setAttribute('bm-cStyle', consoleStyle); // Passes in the console style value
+    script.textContent = `(${callback})();`;
+    document.documentElement?.appendChild(script);
+    script.remove();
+}
+
+/** What code to execute instantly in the client (webpage) to spy on fetch calls.
+ * This code will execute outside of TamperMonkey's sandbox.
+ * @since 0.11.15
+ */
+inject(() => {
+
+  const script = document.currentScript; // Gets the current script HTML Script Element
+  const name = script?.getAttribute('bm-name') || 'Blue Marble'; // Gets the name value that was passed in. Defaults to "Blue Marble" if nothing was found
+  const consoleStyle = script?.getAttribute('bm-cStyle') || ''; // Gets the console style value that was passed in. Defaults to no styling if nothing was found
+  const fetchedBlobQueue = new Map(); // Blobs being processed
+
+  window.addEventListener('message', (event) => {
+    const { source, endpoint, blobID, blobData, blink } = event.data;
+
+    const elapsed = Date.now() - blink;
+
+    // Since this code does not run in the userscript, we can't use consoleLog().
+    console.groupCollapsed(`%c${name}%c: ${fetchedBlobQueue.size} Recieved IMAGE message about blob "${blobID}"`, consoleStyle, '');
+    console.log(`Blob fetch took %c${String(Math.floor(elapsed/60000)).padStart(2,'0')}:${String(Math.floor(elapsed/1000) % 60).padStart(2,'0')}.${String(elapsed % 1000).padStart(3,'0')}%c MM:SS.mmm`, consoleStyle, '');
+    console.log(fetchedBlobQueue);
+    console.groupEnd();
+
+    // The modified blob won't have an endpoint, so we ignore any message without one.
+    if ((source == 'blue-marble') && !!blobID && !!blobData && !endpoint) {
+
+      const callback = fetchedBlobQueue.get(blobID); // Retrieves the blob based on the UUID
+
+      // If the blobID is a valid function...
+      if (typeof callback === 'function') {
+
+        callback(blobData); // ...Retrieve the blob data from the blobID function
+      } else {
+        // ...else the blobID is unexpected. We don't know what it is, but we know for sure it is not a blob. This means we ignore it.
+
+        consoleWarn(`%c${name}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`, consoleStyle, '', blobID);
+      }
+
+      fetchedBlobQueue.delete(blobID); // Delete the blob from the queue, because we don't need to process it again
+    }
+  });
+
+  // Spys on "spontaneous" fetch requests made by the client
+  const originalFetch = window.fetch; // Saves a copy of the original fetch
+
+  // Overrides fetch
+  window.fetch = async function(...args) {
+
+    const response = await originalFetch.apply(this, args); // Sends a fetch
+    const cloned = response.clone(); // Makes a copy of the response
+
+    // Retrieves the endpoint name. Unknown endpoint = "ignore"
+    const endpointName = ((args[0] instanceof Request) ? args[0]?.url : args[0]) || 'ignore';
+
+    // Check Content-Type to only process JSON
+    const contentType = cloned.headers.get('content-type') || '';
+    if (contentType.includes('application/json')) {
+
+
+      // Since this code does not run in the userscript, we can't use consoleLog().
+      console.log(`%c${name}%c: Sending JSON message about endpoint "${endpointName}"`, consoleStyle, '');
+
+      // Sends a message about the endpoint it spied on
+      cloned.json()
+        .then(jsonData => {
+          window.postMessage({
+            source: 'blue-marble',
+            endpoint: endpointName,
+            jsonData: jsonData
+          }, '*');
+        })
+        .catch(err => {
+          console.error(`%c${name}%c: Failed to parse JSON: `, consoleStyle, '', err);
+        });
+    } else if (contentType.includes('image/') && (!endpointName.includes('openfreemap') && !endpointName.includes('maps'))) {
+      // Fetch custom for all images but opensourcemap
+
+      const blink = Date.now(); // Current time
+
+      const blob = await cloned.blob(); // The original blob
+
+      // Since this code does not run in the userscript, we can't use consoleLog().
+      console.log(`%c${name}%c: ${fetchedBlobQueue.size} Sending IMAGE message about endpoint "${endpointName}"`, consoleStyle, '');
+
+      // Returns the manipulated blob
+      return new Promise((resolve) => {
+        const blobUUID = crypto.randomUUID(); // Generates a random UUID
+
+        // Store the blob while we wait for processing
+        fetchedBlobQueue.set(blobUUID, (blobProcessed) => {
+          // The response that triggers when the blob is finished processing
+
+          // Creates a new response
+          resolve(new Response(blobProcessed, {
+            headers: cloned.headers,
+            status: cloned.status,
+            statusText: cloned.statusText
+          }));
+
+          // Since this code does not run in the userscript, we can't use consoleLog().
+          console.log(`%c${name}%c: ${fetchedBlobQueue.size} Processed blob "${blobUUID}"`, consoleStyle, '');
+        });
+
+        window.postMessage({
+          source: 'blue-marble',
+          endpoint: endpointName,
+          blobID: blobUUID,
+          blobData: blob,
+          blink: blink
+        });
+      }).catch(exception => {
+        const elapsed = Date.now();
+        console.error(`%c${name}%c: Failed to Promise blob!`, consoleStyle, '');
+        console.groupCollapsed(`%c${name}%c: Details of failed blob Promise:`, consoleStyle, '');
+        console.log(`Endpoint: ${endpointName}\nThere are ${fetchedBlobQueue.size} blobs processing...\nBlink: ${blink.toLocaleString()}\nTime Since Blink: ${String(Math.floor(elapsed/60000)).padStart(2,'0')}:${String(Math.floor(elapsed/1000) % 60).padStart(2,'0')}.${String(elapsed % 1000).padStart(3,'0')} MM:SS.mmm`);
+        console.error(`Exception stack:`, exception);
+        console.groupEnd();
+      });
+
+      // cloned.blob().then(blob => {
+      //   window.postMessage({
+      //     source: 'blue-marble',
+      //     endpoint: endpointName,
+      //     blobData: blob
+      //   }, '*');
+      // });
+    }
+
+    return response; // Returns the original response
+  };
+});
+
+// Imports the CSS file from dist folder on github
+const cssOverlay = GM_getResourceText("CSS-BM-File");
+GM_addStyle(cssOverlay);
+
+// Injection point for the Roboto Mono font file (only if this is the Standalone version)
+const robotoMonoInjectionPoint = 'robotoMonoInjectionPoint';
+
+// If the Roboto Mono injection point contains '@font-face'...
+if (!!(robotoMonoInjectionPoint.indexOf('@font-face') + 1)) {
+  // A very hacky way of doing truthy/falsy logic
+  
+  console.log(`Loading Roboto Mono as a file...`);
+  GM_addStyle(robotoMonoInjectionPoint); // Add the Roboto Mono font-faces that were injected.
+} else {
+  // 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);
+}
+
+const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}')); // Loads the user settings
+
+// CONSTRUCTORS
+const observers = new Observers(); // Constructs a new Observers object
+const windowMain = new WindowMain(name, version); // Constructs a new Overlay object for the main overlay
+const templateManager = new TemplateManager(name, version); // Constructs a new TemplateManager object
+const apiManager = new ApiManager(templateManager); // Constructs a new ApiManager object
+const settingsManager = new SettingsManager(name, version, userSettings); // Constructs a new SettingsManager
+
+windowMain.setSettingsManager(settingsManager); // Sets the settings manager
+windowMain.setApiManager(apiManager); // Sets the API manager
+
+templateManager.setWindowMain(windowMain);
+templateManager.setSettingsManager(settingsManager); // Sets the settings manager
+
+const storageTemplates = JSON.parse(GM_getValue('bmTemplates', '{}'));
+console.log(storageTemplates);
+templateManager.importJSON(storageTemplates); // Loads the templates
+
+
+console.log(userSettings);
+console.log(Object.keys(userSettings).length);
+
+// If the user does not have a UUID yet, make a new one.
+if (Object.keys(userSettings).length == 0) {
+  const uuid = crypto.randomUUID(); // Generates a random UUID
+  console.log(uuid);
+  GM.setValue('bmUserSettings', JSON.stringify({
+    'uuid': uuid
+  }));
+}
+
+setInterval(() => apiManager.sendHeartbeat(version), 1000 * 60 * 30); // Sends a heartbeat every 30 minutes
+
+// The current "version" of the data collection agreement
+// Increment by 1 to retrigger the telemetry window
+const currentTelemetryVersion = 1;
+
+// The last "version" of the data collection agreement that the user agreed too
+const previousTelemetryVersion = userSettings?.telemetry;
+console.log(`Telemetry is ${!(previousTelemetryVersion == undefined)}`);
+
+// If the user has not agreed to the current data collection terms, we need to show the Telemetry window.
+if ((previousTelemetryVersion == undefined) || (previousTelemetryVersion > currentTelemetryVersion)) {
+  const windowTelemetry = new WindowTelemetry(name, version, currentTelemetryVersion, userSettings?.uuid);
+  windowTelemetry.setApiManager(apiManager);
+  windowTelemetry.buildWindow(); // Asks the user if they want to enable telemetry
+}
+
+windowMain.buildWindow(); // Builds the main Blue Marble window
+
+apiManager.spontaneousResponseListener(windowMain); // Reads spontaneous fetch responces
+
+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
+ */
+function observeBlack() {
+  const observer = new MutationObserver((mutations, observer) => {
+
+    const black = document.querySelector('#color-1'); // Attempt to retrieve the black color element for anchoring
+
+    if (!black) {return;} // Black color does not exist yet. Kills iteself
+
+    let move = document.querySelector('#bm-button-move'); // Tries to find the move button
+
+    // If the move button does not exist, we make a new one
+    if (!move) {
+      move = document.createElement('button');
+      move.id = 'bm-button-move';
+      move.textContent = 'Move ↑';
+      move.className = 'btn btn-soft';
+      move.onclick = function() {
+        const roundedBox = this.parentNode.parentNode.parentNode.parentNode; // Obtains the rounded box
+        const shouldMoveUp = (this.textContent == 'Move ↑');
+        roundedBox.parentNode.className = roundedBox.parentNode.className.replace(shouldMoveUp ? 'bottom' : 'top', shouldMoveUp ? 'top' : 'bottom'); // Moves the rounded box to the top
+        roundedBox.style.borderTopLeftRadius = shouldMoveUp ? '0px' : 'var(--radius-box)';
+        roundedBox.style.borderTopRightRadius = shouldMoveUp ? '0px' : 'var(--radius-box)';
+        roundedBox.style.borderBottomLeftRadius = shouldMoveUp ? 'var(--radius-box)' : '0px';
+        roundedBox.style.borderBottomRightRadius = shouldMoveUp ? 'var(--radius-box)' : '0px';
+        this.textContent = shouldMoveUp ? 'Move ↓' : 'Move ↑';
+      }
+
+      // Attempts to find the "Paint Pixel" element for anchoring
+      const paintPixel = black.parentNode.parentNode.parentNode.parentNode.querySelector('h2');
+
+      paintPixel.parentNode?.appendChild(move); // Adds the move button
+    }
+  });
+
+  observer.observe(document.body, { childList: true, subtree: true });
+}
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + diff --git a/docs/module.exports_module.exports.html b/docs/module.exports_module.exports.html new file mode 100644 index 0000000..26b7d60 --- /dev/null +++ b/docs/module.exports_module.exports.html @@ -0,0 +1,3189 @@ + + + + + + exports - Documentation + + + + + + + + + + + + + + + + + +
+ +

exports

+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version)

+ + + + + +
+

Constructor for the Overlay class.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.0.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+ +
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(templateManager)

+ + + + + +
+

Constructor for ApiManager class

+
+ + + + + +
+ + + + +
Since:
+
  • 0.11.34
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
templateManager + + +TemplateManager + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports()

+ + + + + +
+

The constructor for the observer class

+
+ + + + + +
+ + + + +
Since:
+
  • 0.43.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version)

+ + + + + +
+

The constructor for the TemplateManager class.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.55.8
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript (SemVer as string)

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(paramsopt)

+ + + + + +
+

The constructor for the Template class with enhanced pixel tracking.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.65.2
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
params + + +Object + + + + + + <optional>
+ + + + + +
+ + {} + + +

Object containing all optional parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
displayName + + +string + + + + + + <optional>
+ + + + + +
+ + 'My template' + + +

The display name of the template

+ +
sortID + + +number + + + + + + <optional>
+ + + + + +
+ + 0 + + +

The sort number of the template for rendering priority

+ +
authorID + + +string + + + + + + <optional>
+ + + + + +
+ + '' + + +

The user ID of the person who exported the template (prevents sort ID collisions)

+ +
url + + +string + + + + + + <optional>
+ + + + + +
+ + '' + + +

The URL to the source image

+ +
file + + +File + + + + + + <optional>
+ + + + + +
+ + null + + +

The template file (pre-processed File or processed bitmap)

+ +
coords + + +Array.<number, number, number, number> + + + + + + <optional>
+ + + + + +
+ + null + + +

The coordinates of the top left corner as (tileX, tileY, pixelX, pixelY)

+ +
chunked + + +Object + + + + + + <optional>
+ + + + + +
+ + null + + +

The affected chunks of the template, and their template for each chunk as a bitmap

+ +
chunked32 + + +Object + + + + + + <optional>
+ + + + + +
+ + {} + + +

The affected chunks of the template, and their template for each chunk as a Uint32Array

+ +
tileSize + + +number + + + + + + <optional>
+ + + + + +
+ + 1000 + + +

The size of a tile in pixels (assumes square tiles)

+ +
pixelCount + + +Object + + + + + + <optional>
+ + + + + +
+ + {total:0, colors:Map} + + +

Total number of pixels in the template (calculated automatically during processing)

+ +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version)

+ + + + + +
+

Constructor for the main Blue Marble window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.326
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(executor)

+ + + + + +
+

Constructor for the color filter window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.329
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
executor + + +* + + + + +

The executing class

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version, currentTelemetryVersion, uuid)

+ + + + + +
+

Constructor for the telemetry window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.339
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
currentTelemetryVersion + + +number + + + + +

The current "version" of the data collection agreement

+ +
uuid + + +string + + + + +

The UUID of the user

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports()

+ + + + + +
+

The constructor for the confetti manager.

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.356
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version, schemaVersionBleedingEdge, templateManageropt)

+ + + + + +
+

Constructor for the Template Wizard window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.88.434
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor for examples
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
name + + +string + + + + + + + + + + +

The name of the userscript

+ +
version + + +string + + + + + + + + + + +

The version of the userscript

+ +
schemaVersionBleedingEdge + + +string + + + + + + + + + + +

The bleeding edge of schema versions for Blue Marble

+ +
templateManager + + +TemplateManager + + + + + + <optional>
+ + + + + +
+

(Optional) The TemplateManager class instance

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version)

+ + + + + +
+

Constructor for the Credits window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.90.9
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor for examples
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version)

+ + + + + +
+

Constructor for the Settings window

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + +
See:
+
+
    +
  • Overlay#constructor for examples
  • +
+
+ + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + +
+ +
+ +

+ exports +

+ + +
+ +
+
+ + +
+ + + +

new exports(name, version, userSettings)

+ + + + + +
+

Constructor for the SettingsManager class

+
+ + + + + +
+ + + + +
Since:
+
  • 0.91.11
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + + +string + + + + +

The name of the userscript

+ +
version + + +string + + + + +

The version of the userscript

+ +
userSettings + + +Object + + + + +

The user settings as an object

+ +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/observers.js.html b/docs/observers.js.html new file mode 100644 index 0000000..c17282e --- /dev/null +++ b/docs/observers.js.html @@ -0,0 +1,121 @@ + + + + + + observers.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

observers.js

+ + + + + + + +
+
+
/** This class contains all MutationObservers used (which is 1 probably).
+ * This is not an object, but rather a "collection" of functions (in a class).
+ * @class Observers
+ * @since 0.43.2
+ */
+export default class Observers {
+
+  /** The constructor for the observer class
+   * @since 0.43.2
+   */
+  constructor() {
+    this.observerBody = null;
+    this.observerBodyTarget = null;
+    this.targetDisplayCoords = '#bm-display-coords';
+  }
+
+  /** Creates the MutationObserver for document.body
+   * @param {HTMLElement} target - Targeted element to watch
+   * @returns {Observers} this (Observers class)
+   * @since 0.43.2
+   */
+  createObserverBody(target) {
+
+    this.observerBodyTarget = target;
+
+    this.observerBody = new MutationObserver((mutations) => {
+      for (const mutation of mutations) {
+        for (const node of mutation.addedNodes) {
+
+          if (!(node instanceof HTMLElement)) {continue;} // Does not track non-HTMLElements
+          
+          if (node.matches?.(this.targetDisplayCoords)) {
+
+          }
+        }
+      }
+    })
+
+    return this;
+  }
+
+  /** Retrieves the MutationObserver that watches document.body
+   * @returns {MutationObserver}
+   * @since 0.43.2
+   */
+  getObserverBody() {
+    return this.observerBody;
+  }
+
+  /** Observe a MutationObserver
+   * @param {MutationObserver} observer - The MutationObserver
+   * @param {boolean} watchChildList - (Optional) Should childList be watched? False by default
+   * @param {boolean} watchSubtree - (Optional) Should childList be watched? False by default
+   * @since 0.43.2
+   */
+  observe(observer, watchChildList=false, watchSubtree=false) {
+    observer.observe(this.observerBodyTarget, {
+      childList: watchChildList,
+      subtree: watchSubtree
+    });
+  }
+}
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + + settingsManager.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

settingsManager.js

+ + + + + + + +
+
+
import { sleep } from "./utils";
+import WindowSettings from "./WindowSettings";
+
+/** SettingsManager class for handling user settings and making them persist between sessions.
+ * Logic for {@link WindowSettings} is managed here.
+ * "Flags" should follow the same styling as `.classList()` and should not contain spaces.
+ * A flag should always be false by default.
+ * When a flag is false, it will not exist in the "flags" Array.
+ * (Therefore, "flags" should be `[]` by default)
+ * If it exists in the "flags" Array, then the flag is `true`.
+ * @class SettingsManager
+ * @since 0.91.11
+ * @example
+ * {
+ *   "uuid": "497dcba3-ecbf-4587-a2dd-5eb0665e6880",
+ *   "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]
+ * }
+ */
+export default class SettingsManager extends WindowSettings {
+
+  /** Constructor for the SettingsManager class
+   * @param {string} name - The name of the userscript
+   * @param {string} version - The version of the userscript
+   * @param {Object} userSettings - The user settings as an object
+   * @since 0.91.11
+   */
+  constructor(name, version, userSettings) {
+    super(name, version); // Executes WindowSettings constructor
+    
+    this.userSettings = userSettings; // User settings as an Object
+    this.userSettings.flags ??= []; // Makes sure the key "flags" always exists
+    this.userSettingsOld = structuredClone(this.userSettings); // Creates a duplicate of the user settings to store the old version of user settings from 5+ seconds ago
+    this.userSettingsSaveLocation = 'bmUserSettings'; // Storage save location
+
+    this.updateFrequency = 5000; // Cooldown between saving to storage (throttle)
+    this.lastUpdateTime = 0; // When this unix timestamp is within the last 5 seconds, we should save this.userSettings to storage
+
+    setInterval(this.updateUserStorage.bind(this), this.updateFrequency); // Runs every X seconds (see updateFrequency)
+  }
+
+  /** Updates the user settings in userscript storage
+   * @since 0.91.39
+   */
+  async updateUserStorage() {
+
+    // 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)) {
+      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
+      console.log(userSettingsCurrent);
+    }
+  }
+
+  /** 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`.
+   * @param {string} flagName - The name of the flag to toggle
+   * @param {boolean} [state=undefined] - (Optional) The state to change the flag to
+   * @since 0.91.60
+   */
+  toggleFlag(flagName, state = undefined) {
+
+    const flagIndex = this.userSettings?.flags?.indexOf(flagName) ?? -1; // Is the flag `true`?
+
+    // If the flag is enabled, AND the user does not want to force the flag to be true...
+    if ((flagIndex != -1) && (state !== true)) {
+
+      this.userSettings?.flags?.splice(flagIndex, 1); // Remove the flag (makes it false)
+    } else if ((flagIndex == -1) && (state !== false)) {
+      // Else if the flag is disabled, AND the user does not want to force the flag to be false...
+      this.userSettings?.flags?.push(flagName); // Add the flag (makes it true)
+    }
+  }
+
+  // This is one of the most insane OOP setups I have ever laid my eyes on
+
+  /** Builds the "highlight" category of the settings window
+   * @since 0.91.18
+   * @see WindowSettings#buildHighlight
+   */
+  buildHighlight() {
+
+    const highlightPresetOff = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0ZM0,1H3M0,2H3M1,0V3M2,0V3" fill="#fff"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
+    const highlightPresetCross = '<svg viewBox="0 0 3 3"><path d="M0,0H3V3H0Z" fill="#fff"/><path d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z" fill="brown"/><path d="M1,1H2V2H1Z" fill="#2f4f4f"/></svg>';
+    
+    // Obtains user settings for highlight from storage, or the default array if nothing was found
+    const storedHighlight = this.userSettings?.highlight ?? [[1, 0, 1], [2, 0, 0], [1, -1, 0], [1, 1, 0], [1, 0, -1]];
+
+    // Constructs the category and adds it to the window
+    this.window = this.addDiv({'class': 'bm-container'})
+      .addHeader(2, {'textContent': 'Pixel Highlight'}).buildElement()
+      .addHr().buildElement()
+      .addDiv({'class': 'bm-container', 'style': 'margin-left: 1.5ch;'})
+        .addCheckbox({'textContent': 'Highlight transparent pixels'}, (instance, label, checkbox) => {
+          checkbox.checked = !this.userSettings?.flags?.includes('hl-noTrans'); // Makes the checkbox match the last stored user setting
+          checkbox.onchange = (event) => this.toggleFlag('hl-noTrans', !event.target.checked); // Forces the flag to be the opposite state as the checkbox. E.g. "Checked" means 'hl-noTrans' is false (does not exist).
+        }).buildElement()
+        .addP({'id': 'bm-highlight-preset-label', 'textContent': 'Choose a preset:', 'style': 'font-weight: 700;'}).buildElement()
+        .addDiv({'class': 'bm-flex-center', 'role': 'group', 'aria-labelledby': 'bm-highlight-preset-label'})
+          .addDiv({'class': 'bm-highlight-preset-container'})
+            .addSpan({'textContent': 'None'}).buildElement()
+            .addButton({'innerHTML': highlightPresetOff, 'aria-label': 'Preset "None"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('None')}).buildElement()
+          .buildElement()
+          .addDiv({'class': 'bm-highlight-preset-container'})
+            .addSpan({'textContent': 'Cross'}).buildElement()
+            .addButton({'innerHTML': highlightPresetCross, 'aria-label': 'Preset "Cross Shape"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('Cross')}).buildElement()
+          .buildElement()
+          .addDiv({'class': 'bm-highlight-preset-container'})
+            .addSpan({'textContent': 'X'}).buildElement()
+            .addButton({'innerHTML': highlightPresetCross.replace('d="M1,0H2V1H3V2H2V3H1V2H0V1H1Z"', 'd="M0,0V1H3V0H2V3H3V2H0V3H1V0Z"'), 'aria-label': 'Preset "X Shape"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('X')}).buildElement()
+          .buildElement()
+          .addDiv({'class': 'bm-highlight-preset-container'})
+            .addSpan({'textContent': 'Full'}).buildElement()
+            .addButton({'innerHTML': highlightPresetOff.replace('#fff', '#2f4f4f'), 'aria-label': 'Preset "Full Template"'}, (instance, button) => {button.onclick = () => this.#updateHighlightToPreset('Full')}).buildElement()
+          .buildElement()
+        .buildElement()
+        .addP({'id': 'bm-highlight-grid-label', 'textContent': 'Create a custom pattern:', 'style': 'font-weight: 700;'}).buildElement()
+        .addDiv({'class': 'bm-highlight-grid', 'role': 'group', 'aria-labelledby': 'bm-highlight-grid-label'});
+          // We leave this open so we can add buttons
+
+          // For each of the 9 buttons...
+          for (let buttonY = -1; buttonY <= 1; buttonY++) {
+            for (let buttonX = -1; buttonX <= 1; buttonX++) {
+              const buttonState = storedHighlight[storedHighlight.findIndex(([, x, y]) => ((x == buttonX) && (y == buttonY)))]?.[0] ?? 0;
+              let buttonStateName = 'Disabled';
+              if (buttonState == 1) {
+                buttonStateName = 'Incorrect';
+              } else if (buttonState == 2) {
+                buttonStateName = 'Template';
+              }
+              this.window = this.addButton({
+                'data-status': buttonStateName,
+                'aria-label': `Sub-pixel ${buttonStateName.toLowerCase()}`
+              }, (instance, button) => {
+                button.onclick = () => this.#updateHighlightSettings(button, [buttonX, buttonY])
+              }).buildElement();
+            }
+          }
+
+          // Resumes from where we left off before we added buttons
+        this.window = this.buildElement()
+      .buildElement()
+    .buildElement();
+  }
+
+  /** Updates the display of the highlight buttons in the settings window.
+   * Additionally, it will update user settings with the new selection.
+   * @param {HTMLButtonElement} button - The button that was pressed
+   * @param {Array<number, number>} coords - The relative coordinates of the button
+   * @since 0.91.46
+   */
+  #updateHighlightSettings(button, coords) {
+
+    button.disabled = true; // Disabled the button until we are done
+
+    const status = button.dataset['status']; // Obtains the current status of the button
+
+    /** Obtains the old highlight storage, or sets it to default. @type {Array<number[]>} */
+    const userStorageOld = this.userSettings?.highlight ?? [[1, 0, 1], [2, 0, 0], [1, -1, 0], [1, 1, 0], [1, 0, -1]];
+
+    let userStorageChange = [2, 0, 0]; // The new change to the user storage
+
+    const userStorageNew = userStorageOld; // The old storage with the new change
+
+    // For each different type of status...
+    switch (status) {
+
+      // If the button was in the "Disabled" state
+      case 'Disabled':
+
+        // Change to "Incorrect"
+        button.dataset['status'] = 'Incorrect';
+        button.ariaLabel = 'Sub-pixel incorrect';
+        userStorageChange = [1, ...coords];
+        break;
+      
+      // If the button was in the "Incorrect" state
+      case 'Incorrect':
+
+        // Change to "Template"
+        button.dataset['status'] = 'Template';
+        button.ariaLabel = 'Sub-pixel template';
+        userStorageChange = [2, ...coords];
+        break;
+      
+      // If the button was in the "Template" state
+      case 'Template':
+
+        // Change to "Disabled"
+        button.dataset['status'] = 'Disabled';
+        button.ariaLabel = 'Sub-pixel disabled';
+        userStorageChange = [0, ...coords];
+        break;
+    }
+
+    // Finds the index of the pixel to change
+    const indexOfChange = userStorageOld.findIndex(([, x, y]) => ((x == userStorageChange[1]) && (y == userStorageChange[2])));
+
+    // If the new sub-pixel state is NOT disabled
+    if (userStorageChange[0] != 0) {
+
+      // If a sub-pixel was found...
+      if (indexOfChange != -1) {
+        userStorageNew[indexOfChange] = userStorageChange;
+      } else {
+        userStorageNew.push(userStorageChange);
+      }
+    } else if (indexOfChange != -1) {
+      // Else, it is disabled. We want to remove it if it exists.
+      userStorageNew.splice(indexOfChange, 1); // Removes 1 index from the array at the index of the pixel change
+    }
+
+    this.userSettings['highlight'] = userStorageNew;
+    // TODO: Add timer update here
+
+    button.disabled = false; // Reenables the button since we are done
+  }
+
+  /** Changes the highlight buttons to the clicked preset.
+   * @param {string} preset - The name of the preset
+   * @since 0.91.49
+   */
+  async #updateHighlightToPreset(preset) {
+
+    // Obtains all preset buttons as a NodeList
+    const presetButtons = document.querySelectorAll('.bm-highlight-preset-container button');
+
+    // For each preset...
+    for (const button of presetButtons) {
+      button.disabled = true; // Disables the button
+    }
+
+    let presetArray = [0,0,0,0,2,0,0,0,0]; // The preset "None"
+
+    // Selects the preset passed in
+    switch (preset) {
+      case 'Cross':
+        presetArray = [0,1,0,1,2,1,0,1,0]; // The preset "Cross"
+        break;
+      case 'X':
+        presetArray = [1,0,1,0,2,0,1,0,1]; // The preset "X"
+        break;
+      case 'Full': 
+        presetArray = [2,2,2,2,2,2,2,2,2]; // The preset "Full"
+        break;
+    }
+
+    // Obtains the buttons to click as a NodeList
+    const buttons = document.querySelector('.bm-highlight-grid')?.childNodes ?? [];
+
+    // For each button...
+    for (let buttonIndex = 0; buttonIndex < buttons.length; buttonIndex++) {
+
+      const button = buttons[buttonIndex]; // Gets the current button to check
+
+      // Gets the state of the button as a number
+      let buttonState = button.dataset['status'];
+      buttonState = (buttonState != 'Disabled') ? ((buttonState != 'Incorrect') ? 2 : 1) : 0;
+
+      // Finds the difference between the preset and the button
+      let buttonStateDelta = presetArray[buttonIndex] - buttonState;
+
+      // Since there is no difference, the button matches, so we skip it
+      if (buttonStateDelta == 0) {continue;}
+
+      // Makes the difference positive
+      buttonStateDelta += (buttonStateDelta < 0) ? 3 : 0;
+
+      /** At this point, these are the possible options:
+       * 1. The preset is zero and the button is two (-2) so we need to click once
+       * 2. The preset is one and the button is two (-1) so we need to click twice
+       * 3. The preset is one ahead of the button (1) so we need to click once
+       * 4. The preset is two ahead of the button (2) so we need to click twice
+       * Due to the addition of three in the line above, options 1 & 3 combine, and options 2 & 4 combine.
+       * Now the only options we have are:
+       * 1. If (1) then click once
+       * 2. If (2) then click twice
+       * Also due to the addition of three in the line above, our two options are POSITIVE numbers
+       */
+
+      button.click(); // Clicks once
+      
+      // Clicks a second time if needed
+      if (buttonStateDelta == 2) {
+
+        // For 0.2 seconds, or when the button is NOT disabled, wait for 10 milliseconds before attempting to continue
+        for (let timeWaited = 0; timeWaited < 200; timeWaited += 10) {
+          if (!button.disabled) {break;} // Breaks early once the button is enabled
+          await sleep(10);
+        }
+
+        button.click(); // Clicks again
+      }
+    }
+
+    // For each preset...
+    for (const button of presetButtons) {
+      button.disabled = false; // Re-enables the button
+    }
+  }
+
+  /** Build the "template" category of settings window
+   * @since 0.91.68
+   * @see WindowSettings#buildTemplate
+   */
+  buildTemplate() {
+
+    this.window = this.addDiv({'class': 'bm-container'})
+      .addHeader(2, {'textContent': 'Pixel Highlight'}).buildElement()
+      .addHr().buildElement()
+      .addDiv({'class': 'bm-container', 'style': 'margin-left: 1.5ch;'})
+        .addCheckbox({'textContent': 'Template creation should skip transparent tiles'}, (instance, label, checkbox) => {
+          checkbox.checked = !this.userSettings?.flags?.includes('hl-noSkip'); // Makes the checkbox match the last stored user setting
+          checkbox.onchange = (event) => this.toggleFlag('hl-noSkip', !event.target.checked); // If the user wants to skip, then the checkbox is NOT checked
+        }).buildElement()
+        .addCheckbox({'innerHTML': 'Experimental: Template creation should <em>aggressively</em> skip transparent tiles'}, (instance, label, checkbox) => {
+          checkbox.checked = this.userSettings?.flags?.includes('hl-agSkip'); // Makes the checkbox match the last stored user setting
+          checkbox.onchange = (event) => this.toggleFlag('hl-agSkip', event.target.checked); // If the user wants to aggressively skip, then the checkbox is checked
+        }).buildElement()
+      .buildElement()
+    .buildElement()
+  }
+}
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + diff --git a/docs/styles/jsdoc-default.css b/docs/styles/jsdoc-default.css new file mode 100644 index 0000000..c14e3b9 --- /dev/null +++ b/docs/styles/jsdoc-default.css @@ -0,0 +1,692 @@ +@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,500i,500,600,600i|Roboto); + +* { + box-sizing: border-box +} + +html, body { + height: 100%; + width: 100%; +} + +body { + color: #4d4e53; + background-color: white; + margin: 0 auto; + padding: 0; + font-family: 'Source Sans Pro', Helvetica, sans-serif; + font-size: 16px; + line-height: 160%; +} + +a, +a:active { + color: #0095dd; + text-decoration: none; +} + +a:hover { + text-decoration: underline +} + +p, ul, ol, blockquote { + margin-bottom: 1em; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Roboto', sans-serif; +} + +h1, h2, h3, h4, h5, h6 { + color: #000; + font-weight: 400; + margin: 0; +} + +h1 { + font-weight: 300; + font-size: 48px; + margin: 1em 0 .5em; +} + +h1.page-title {margin-bottom: 10px;font-size: 34px;font-weight: 300;border-bottom: solid 2px #ddd;padding: .5em 0 .5em;margin-top: 0;} + +h2 { + font-size: 32px; + margin: 1.2em 0 .8em; + font-weight: bold; +} + +h3 { + /* margin-top: 1em; */ + /* margin-bottom: 16px; */ + /* font-weight: bold; */ + padding: 0; + margin: 1em 0 .6em; + font-size: 28px; + /* border-bottom: 1px solid #eee; */ + /* padding-bottom: 15px; */ +} + +h4 { + font-size: 18px; + margin: 1em 0 .2em; + color: #4d4e53; + /* border-bottom: 1px solid #eee; */ + padding-bottom: 8px; +} + +h5, .container-overview .subsection-title { + font-size: 120%; + /* letter-spacing: -0.01em; */ + margin: 20px 0 5px; +} + +h6 { + font-size: 100%; + letter-spacing: -0.01em; + margin: 6px 0 3px 0; + font-style: italic; +} + +tt, code, kbd, samp { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + background: #f4f4f4; + padding: 1px 5px; + border-radius: 5px; + font-size: 14px; +} + +blockquote { + display: block; + border-left: 4px solid #eee; + margin: 0; + padding-left: 1em; + color: #888; +} + +.class-description { + font-size: 130%; + line-height: 140%; + margin-bottom: 1em; + margin-top: 1em; +} + +.class-description:empty { + margin: 0 +} + +/** Container **/ +#main { + float: right; + min-width: 360px; + width: calc(100% - 250px); + padding: 0 30px 20px 30px; +} + +header { + display: block +} + +section { + display: block; + background-color: #fff; + padding: 0; +} + +.variation { + display: none +} + +.signature-attributes { + font-size: 60%; + color: #aaa; + font-style: italic; + font-weight: lighter; +} + +/** Readme **/ + +.readme { + font-size: 16px; +} + +.readme h1, +.readme h2, +.readme h3, +.readme h4, +.readme h5 { + margin-top: 1em; + margin-bottom: 16px; + font-weight: bold; + padding: 0; +} + +.readme h1 { + font-size: 2em; + padding-bottom: 0.3em; +} + +.readme h2 { + font-size: 1.75em; + padding-bottom: 0.3em; +} + +.readme h3 { + font-size: 1.5em; + background-color: transparent; +} + +.readme h4 { + font-size: 1.25em; +} + +.readme h5 { + font-size: 1em; +} + +.readme img { + max-width: 100%; +} + +.readme ul, .readme ol { + padding-left: 2em; +} + +.readme pre > code { + font-size: 0.85em; +} + +.readme table { + margin-bottom: 1em; + border-collapse: collapse; + border-spacing: 0; +} + +.readme table tr { + background-color: #fff; + border-top: 1px solid #ccc; +} + +.readme table th, +.readme table td { + padding: 6px 13px; + border: 1px solid #ddd; +} + +.readme table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +/** Nav **/ +nav { + float: left; + display: block; + width: 250px; + background: #fff; + overflow: auto; + position: fixed; + height: 100%; + padding: 10px; + border-right: 1px solid #eee; + /* box-shadow: 0 0 3px rgba(0,0,0,0.1); */ +} + +nav li { + list-style: none; + padding: 0; + margin: 0; +} + +.nav-heading { + margin-top: 10px; + font-weight: bold; +} + +.nav-heading a { + color: #888; + font-size: 14px; + display: inline-block; +} + +.nav-item-type { + /* margin-left: 5px; */ + width: 18px; + height: 18px; + display: inline-block; + text-align: center; + border-radius: 0.2em; + margin-right: 5px; + font-weight: bold; + line-height: 20px; + font-size: 13px; +} + +.type-function { + background: #B3E5FC; + color: #0288D1; +} + +.type-class { + background: #D1C4E9; + color: #4527A0; +} + +.type-member { + background: #C8E6C9; + color: #388E3C; +} + +.type-module { + background: #E1BEE7; + color: #7B1FA2; +} + + +/** Footer **/ +footer { + color: hsl(0, 0%, 28%); + margin-left: 250px; + display: block; + padding: 30px; + font-style: italic; + font-size: 90%; + border-top: 1px solid #eee; +} + +.ancestors { + color: #999 +} + +.ancestors a { + color: #999 !important; + text-decoration: none; +} + +.clear { + clear: both +} + +.important { + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px +} + +.type-signature { + color: #aaa +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace +} + +.details { + margin-top: 14px; + border-left: 2px solid #DDD; + line-height: 30px; +} + +.details dt { + width: 120px; + float: left; + padding-left: 10px; +} + +.details dd { + margin-left: 70px +} + +.details ul { + margin: 0 +} + +.details ul { + list-style-type: none +} + +.details li { + margin-left: 30px +} + +.details pre.prettyprint { + margin: 0 +} + +.details .object-value { + padding-top: 0 +} + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption { + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint { + font-size: 13px; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 1px 3px hsla(0, 0%, 0%, 0.05); + overflow: auto; +} + +.prettyprint.source { + width: inherit +} + +.prettyprint code { + font-size: 12px; + line-height: 18px; + display: block; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code:empty:before { + content: ''; +} + +.prettyprint > code { + padding: 15px +} + +.prettyprint .linenums code { + padding: 0 15px +} + +.prettyprint .linenums li:first-of-type code { + padding-top: 15px +} + +.prettyprint code span.line { + display: inline-block +} + +.prettyprint.linenums { + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol { + padding-left: 0 +} + +.prettyprint.linenums li { + border-left: 3px #ddd solid +} + +.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { + background-color: lightyellow +} + +.prettyprint.linenums li * { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params, .props { + border-spacing: 0; + border: 1px solid #ddd; + border-collapse: collapse; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + width: 100%; + font-size: 14px; + /* margin-left: 15px; */ +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td, .params th, .props td, .props th { + margin: 0px; + text-align: left; + vertical-align: top; + padding: 10px; + display: table-cell; +} + +.params td { + border-top: 1px solid #eee +} + +.params thead tr, .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params .params thead tr, .props .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params td.description > p:first-child, .props td.description > p:first-child { + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, .props td.description > p:last-child { + margin-bottom: 0; + padding-bottom: 0; +} + +dl.param-type { + /* border-bottom: 1px solid hsl(0, 0%, 87%); */ + margin: 0; + padding: 0; + font-size: 16px; +} + +.param-type dt, .param-type dd { + display: inline-block +} + +.param-type dd { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + display: inline-block; + padding: 0; + margin: 0; + font-size: 14px; +} + +.disabled { + color: #454545 +} + +/* navicon button */ +.navicon-button { + display: none; + position: relative; + padding: 2.0625rem 1.5rem; + transition: 0.25s; + cursor: pointer; + user-select: none; + opacity: .8; +} +.navicon-button .navicon:before, .navicon-button .navicon:after { + transition: 0.25s; +} +.navicon-button:hover { + transition: 0.5s; + opacity: 1; +} +.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { + transition: 0.25s; +} +.navicon-button:hover .navicon:before { + top: .825rem; +} +.navicon-button:hover .navicon:after { + top: -.825rem; +} + +/* navicon */ +.navicon { + position: relative; + width: 2.5em; + height: .3125rem; + background: #000; + transition: 0.3s; + border-radius: 2.5rem; +} +.navicon:before, .navicon:after { + display: block; + content: ""; + height: .3125rem; + width: 2.5rem; + background: #000; + position: absolute; + z-index: -1; + transition: 0.3s 0.25s; + border-radius: 1rem; +} +.navicon:before { + top: .625rem; +} +.navicon:after { + top: -.625rem; +} + +/* open */ +.nav-trigger:checked + label:not(.steps) .navicon:before, +.nav-trigger:checked + label:not(.steps) .navicon:after { + top: 0 !important; +} + +.nav-trigger:checked + label .navicon:before, +.nav-trigger:checked + label .navicon:after { + transition: 0.5s; +} + +/* Minus */ +.nav-trigger:checked + label { + transform: scale(0.75); +} + +/* × and + */ +.nav-trigger:checked + label.plus .navicon, +.nav-trigger:checked + label.x .navicon { + background: transparent; +} + +.nav-trigger:checked + label.plus .navicon:before, +.nav-trigger:checked + label.x .navicon:before { + transform: rotate(-45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus .navicon:after, +.nav-trigger:checked + label.x .navicon:after { + transform: rotate(45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus { + transform: scale(0.75) rotate(45deg); +} + +.nav-trigger:checked ~ nav { + left: 0 !important; +} + +.nav-trigger:checked ~ .overlay { + display: block; +} + +.nav-trigger { + position: fixed; + top: 0; + clip: rect(0, 0, 0, 0); +} + +.overlay { + display: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: hsla(0, 0%, 0%, 0.5); + z-index: 1; +} + +.section-method { + margin-bottom: 30px; + padding-bottom: 30px; + border-bottom: 1px solid #eee; +} + +@media only screen and (min-width: 320px) and (max-width: 680px) { + body { + overflow-x: hidden; + } + + nav { + background: #FFF; + width: 250px; + height: 100%; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: -250px; + z-index: 3; + padding: 0 10px; + transition: left 0.2s; + } + + .navicon-button { + display: inline-block; + position: fixed; + top: 1.5em; + right: 0; + z-index: 2; + } + + #main { + width: 100%; + min-width: 360px; + } + + #main h1.page-title { + margin: 1em 0; + } + + #main section { + padding: 0; + } + + footer { + margin-left: 0; + } +} + +@media only print { + nav { + display: none; + } + + #main { + float: none; + width: 100%; + } +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 0000000..834a866 --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: hsl(104, 100%, 24%); + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 0000000..81e74d1 --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: hsl(104, 100%, 24%); } + + /* a keyword */ + .kwd { + color: hsl(240, 100%, 50%); } + + /* a comment */ + .com { + color: hsl(0, 0%, 60%); } + + /* a type name */ + .typ { + color: hsl(240, 100%, 32%); } + + /* a literal value */ + .lit { + color: hsl(240, 100%, 40%); } + + /* punctuation */ + .pun { + color: #000000; } + + /* lisp open bracket */ + .opn { + color: #000000; } + + /* lisp close bracket */ + .clo { + color: #000000; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/docs/templateManager.js.html b/docs/templateManager.js.html new file mode 100644 index 0000000..dae4472 --- /dev/null +++ b/docs/templateManager.js.html @@ -0,0 +1,1055 @@ + + + + + + templateManager.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

templateManager.js

+ + + + + + + +
+
+
import SettingsManager from "./settingsManager";
+import Template from "./Template";
+import { base64ToUint8, colorpaletteForBlueMarble, consoleError, consoleLog, consoleWarn, localizeNumber, numberToEncoded, sleep, viewCanvasInNewTab } from "./utils";
+import WindowMain from "./WindowMain";
+import WindowWizard from "./WindowWizard";
+
+/** Manages the template system.
+ * This class handles all external requests for template modification, creation, and analysis.
+ * It serves as the central coordinator between template instances and the user interface.
+ * @class TemplateManager
+ * @since 0.55.8
+ * @example
+ * // JSON structure for a template made in schema version 2.0.0.
+ * // Note: The pixel "colors" Object contains more than 2 keys.
+ * // Note: The template tiles are stored as base64 PNG images.
+ * {
+ *   "whoami": "BlueMarble",
+ *   "scriptVersion": "1.13.0",
+ *   "schemaVersion": "2.0.0",
+ *   "templates": {
+ *     "0 $Z": {
+ *       "name": "My Template",
+ *       "enabled": true,
+ *       "pixels": {
+ *         "total": 40399,
+ *         "colors": {
+ *           "-2": 40000,
+ *           "0": 399
+ *         }
+ *       }
+ *       "tiles": {
+ *         "1231,0047,183,593": "iVBORw0KGgoAAAANSUhEUgAA",
+ *         "1231,0048,183,000": "AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+ *       }
+ *     },
+ *     "1 $Z": {
+ *       "name": "My Template",
+ *       "URL": "https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/dist/assets/Favicon.png",
+ *       "URLType": "template",
+ *       "enabled": false,
+ *       "pixels": {
+ *         "total": 40399,
+ *         "colors": {
+ *           "-2": 40000,
+ *           "0": 399
+ *         }
+ *       }
+ *       "tiles": {
+ *         "375,1846,276,188": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA",
+ *         "376,1846,000,188": "data:image/png;AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+ *       }
+ *     }
+ *   }
+ * }
+ * @example
+ * // JSON structure for a template made in schema version 1.0.0.
+ * // Note: The template tiles are stored as base64 PNG images.
+ * {
+ *   "whoami": "BlueMarble",
+ *   "scriptVersion": "1.13.0",
+ *   "schemaVersion": "1.0.0",
+ *   "templates": {
+ *     "0 $Z": {
+ *       "name": "My Template",
+ *       "enabled": true,
+ *       "coords": "2000, 230, 45, 201"
+ *       "palette": {
+ *         "0,0,0": {
+ *            "count": 123,
+ *            "enabled": true
+ *         },
+ *         "255,255,255": {
+ *            "count": 1315,
+ *            "enabled": false
+ *         }
+ *       }
+ *       "tiles": {
+ *         "1231,0047,183,593": "iVBORw0KGgoAAAANSUhEUgAA",
+ *         "1231,0048,183,000": "AAAFCAYAAACNbyblAAAAHElEQVQI12P4"
+ *       }
+ *     }
+ *   }
+ * }
+ */
+export default class TemplateManager {
+
+  /** The constructor for the {@link TemplateManager} class.
+   * @param {string} name - The name of the userscript
+   * @param {string} version - The version of the userscript (SemVer as string)
+   * @since 0.55.8
+   */
+  constructor(name, version) {
+
+    // Meta
+    this.name = name; // Name of userscript
+    this.version = version; // Version of userscript
+    this.windowMain = null; // The main instance of the Overlay class
+    this.settingsManager = null; // The main instance of the SettingsManager class
+    this.schemaVersion = '2.0.0'; // Version of JSON schema
+    this.userID = null; // The ID of the current user
+    this.encodingBase = '!#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'; // Characters to use for encoding/decoding
+    this.tileSize = 1000; // The number of pixels in a tile. Assumes the tile is square
+    this.drawMult = 3; // The enlarged size for each pixel. E.g. when "3", a 1x1 pixel becomes a 1x1 pixel inside a 3x3 area. MUST BE ODD
+    this.paletteTolerance = 3; // Tolerance for how close an RGB value has to be in order to be considered a color. A tolerance of "3" means the sum of the RGB can be up to 3 away from the actual value.
+    this.paletteBM = colorpaletteForBlueMarble(this.paletteTolerance); // Retrieves the color palette BM will use as an Object containing multiple Uint32Arrays
+    
+    // Template
+    this.template = null; // The template image.
+    this.templateState = ''; // The state of the template ('blob', 'proccessing', 'template', etc.)
+    /** An Array of Template classes @type {Array<Template>} */
+    this.templatesArray = []; // All Template instnaces currently loaded (Template)
+    this.templatesJSON = null; // All templates currently loaded (JSON)
+    this.templatesShouldBeDrawn = true; // Should ALL templates be drawn to the canvas?
+    this.templatePixelsCorrect = null; // An object where the keys are the tile coords, and the values are Maps (BM palette color IDs) containing the amount of correctly placed pixels for that tile in this template
+    /** Will contain all color ID's to filter @type {Map<number, boolean>} */
+    this.shouldFilterColor = new Map();
+  }
+
+  /** Updates the stored instance of the main window.
+   * @param {WindowMain} windowMain - The main window instance
+   * @since 0.91.54
+   */
+  setWindowMain(windowMain) {
+    this.windowMain = windowMain;
+  }
+
+  /** Updates the stored instance of the SettingsManager.
+   * @param {SettingsManager} settingsManager - The settings manager instance
+   * @since 0.91.54
+   */
+  setSettingsManager(settingsManager) {
+    this.settingsManager = settingsManager;
+  }
+
+  /** Creates the JSON object to store templates in
+   * @returns {{ whoami: string, scriptVersion: string, schemaVersion: string, templates: Object }} The JSON object
+   * @since 0.65.4
+   */
+  async createJSON() {
+    return {
+      "whoami": this.name.replace(' ', ''), // Name of userscript without spaces
+      "scriptVersion": this.version, // Version of userscript
+      "schemaVersion": this.schemaVersion, // Version of JSON schema
+      "templates": {} // The templates
+    };
+  }
+
+  /** Creates the template from the inputed file blob
+   * @param {File} blob - The file blob to create a template from
+   * @param {string} name - The display name of the template
+   * @param {Array<number, number, number, number>} coords - The coordinates of the top left corner of the template
+   * @since 0.65.77
+   */
+  async createTemplate(blob, name, coords) {
+
+    // Creates the JSON object if it does not already exist
+    if (!this.templatesJSON) {this.templatesJSON = await this.createJSON(); console.log(`Creating JSON...`);}
+
+    this.windowMain.handleDisplayStatus(`Creating template at ${coords.join(', ')}...`);
+
+    // Creates a new template instance
+    const template = new Template({
+      displayName: name,
+      sortID: 0, // Object.keys(this.templatesJSON.templates).length || 0, // Uncomment this to enable multiple templates (1/2)
+      authorID: numberToEncoded(this.userID || 0, this.encodingBase),
+      file: blob,
+      coords: coords
+    });
+
+    // Does the user want to skip transparent tiles while creating templates?
+    const shouldSkipTransTiles = !this.settingsManager?.userSettings?.flags?.includes('hl-noSkip');
+
+    // Does the user want to aggressively skip transparent tiles while creating templates?
+    const shouldAggSkipTransTiles = this.settingsManager?.userSettings?.flags?.includes('hl-agSkip');
+
+    console.log(`Should Skip: ${shouldSkipTransTiles}; Should Agg Skip: ${shouldAggSkipTransTiles}`);
+    
+    const { templateTiles, templateTilesBuffers } = await template.createTemplateTiles(this.tileSize, this.paletteBM, shouldSkipTransTiles, shouldAggSkipTransTiles); // Chunks the tiles
+    
+    template.chunked = templateTiles; // Stores the chunked tile bitmaps
+
+    // Converts total pixel Object/Map variables into JSON-ready format
+    const _pixels = { "total": template.pixelCount.total, "colors": Object.fromEntries(template.pixelCount.colors) }
+
+    // Appends a child into the templates object
+    // The child's name is the number of templates already in the list (sort order) plus the encoded player ID
+    this.templatesJSON.templates[`${template.sortID} ${template.authorID}`] = {
+      "name": template.displayName, // Display name of template
+      "coords": coords.join(', '), // The coords of the template
+      "enabled": true,
+      "pixels": _pixels, // The total pixels in the template
+      "tiles": templateTilesBuffers // Stores the chunked tile buffers
+    };
+
+    this.templatesArray = []; // Remove this to enable multiple templates (2/2)
+    this.templatesArray.push(template); // Pushes the Template object instance to the Template Array
+
+    this.windowMain.handleDisplayStatus(`Template created at ${coords.join(', ')}!`);
+
+    console.log(Object.keys(this.templatesJSON.templates).length);
+    console.log(this.templatesJSON);
+    console.log(this.templatesArray);
+    console.log(JSON.stringify(this.templatesJSON));
+
+    await this.#storeTemplates();
+  }
+
+  /** 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.
+   * @param {Object} template - The template to load
+   * @since 0.88.504
+   */
+  #loadTemplate(templateObject) {
+
+    // Calculates the pixel count
+    const pixelCount = {
+      total: templateObject.pixels?.total,
+      colors: new Map(Object.entries(templateObject.pixels?.colors || {}).map(([key, value]) => [Number(key), value]))
+    };
+
+    // Creates the template
+    const template = new Template({
+      displayName: templateObject.displayName,
+      sortID: Object.keys(this.templatesJSON.templates).length || 0,
+      authorID: numberToEncoded(this.userID || 0, this.encodingBase),
+      pixelCount: pixelCount,
+      chunked: templateObject.tiles
+    });
+
+    template.calculateCoordsFromChunked(); // Updates `Template.coords`
+
+    this.templatesArray.push(template);
+  }
+
+  /** Stores the JSON object of the loaded templates into TamperMonkey (GreaseMonkey) storage.
+   * @since 0.72.7
+   */
+  async #storeTemplates() {
+    GM.setValue('bmTemplates', JSON.stringify(this.templatesJSON));
+  }
+
+  /** Deletes a template from the JSON object.
+   * Also delete's the corrosponding {@link Template} class instance
+   */
+  deleteTemplate() {
+
+  }
+
+  /** Disables the template from view
+   */
+  async disableTemplate() {
+
+    // Creates the JSON object if it does not already exist
+    if (!this.templatesJSON) {this.templatesJSON = await this.createJSON(); console.log(`Creating JSON...`);}
+
+
+  }
+
+  /** Downloads all templates loaded.
+   * @since 0.88.499
+   */
+  async downloadAllTemplates() {
+
+    consoleLog(`Downloading all templates...`);
+
+    console.log(this.templatesArray);
+
+    // For each template loaded...
+    for (const template of this.templatesArray) {
+
+      await this.downloadTemplate(template); // Downloads the template
+
+      await sleep(500); // Avoids download throttling from the browser
+    }
+  }
+
+  /** Downloads all templates from Blue Marble's template storage.
+   * @since 0.88.474
+   */
+  async downloadAllTemplatesFromStorage() {
+
+    // Templates in user storage
+    const templates = JSON.parse(GM_getValue('bmTemplates', '{}'))?.templates;
+
+    console.log(templates);
+
+    // If there is at least one template loaded...
+    if (Object.keys(templates).length > 0) {
+
+      // For each template loaded...
+      for (const [key, template] of Object.entries(templates)) {
+
+        // If the template is a direct child of the templates Object...
+        if (templates.hasOwnProperty(key)) {
+          
+          // Downloads the template using a dummy Template instance
+          await this.downloadTemplate(new Template({
+            displayName: template.name,
+            sortID: key.split(' ')?.[0],
+            authorID: key.split(' ')?.[1],
+            chunked: template.tiles
+          }));
+
+          await sleep(500); // Avoids download throttling from the browser
+        }
+      }
+    }
+  }
+
+  /** Downloads the template passed-in.
+   * @param {Template} template - The template class instance to download
+   * @since 0.88.499
+   */
+  async downloadTemplate(template) {
+
+    template.calculateCoordsFromChunked(); // Updates `Template.coords`
+
+    // Constructs the file name to download as
+    const templateFileName = `${template.coords.join('-')}_${template.displayName.replaceAll(' ', '-')}`;
+
+    // Converts `Template.chunked` to a blob
+    const blob = await this.convertTemplateToBlob(template);
+
+    // Downloads the template
+    await GM.download({
+      url: URL.createObjectURL(blob),
+      name: templateFileName + '.png',
+      conflictAction: 'uniquify',
+      onload: () => {consoleLog(`Download of template '${templateFileName}' complete!`);},
+      onerror: (error, details) => {consoleError(`Download of template '${templateFileName}' failed because ${error}! Details: ${details}`);},
+      ontimeout: () => {consoleWarn(`Download of template '${templateFileName}' has timed out!`);}
+    });
+  }
+
+  /** Converts a Template class instance into a Blob. 
+   * Specifically, this takes `Template.chunked` and converts it to a Blob.
+   * @since 0.88.504
+   * @returns {Promise<Blob>} A Promise of a Blob PNG image of the template
+   */
+  async convertTemplateToBlob(template) {
+
+    console.log(template);
+
+    const templateTiles64 = template.chunked; // Tiles of template image as base 64
+
+    // Sorts the keys of the tiles (Object -> Array)
+    const templateTileKeysSorted = Object.keys(templateTiles64).sort();
+
+    // Turns the base64 tiles into Images
+    const templateTilesImageSorted = await Promise.all(templateTileKeysSorted.map(tileKey => convertBase64ToImage(templateTiles64[tileKey])));
+
+    // Absolute pixel coordinates for smallest (top left) and largest (bottom right) pixel coordinates
+    let absoluteSmallestX = Infinity;
+    let absoluteSmallestY = Infinity;
+    let absoluteLargestX = 0;
+    let absoluteLargestY = 0;
+
+    // Calculates the minimum and maximum (X, Y) absolute coordinates
+    templateTileKeysSorted.forEach((key, index) => {
+
+      // Deconstructs the tile coordinates
+      const [tileX, tileY, pixelX, pixelY] = key.split(',').map(Number);
+
+      const tileImage = templateTilesImageSorted[index]; // Obtains the image for this tile
+
+      // Calculates the absolute pixel coordinates for this tile
+      const absoluteX = (tileX * this.tileSize) + pixelX;
+      const absoluteY = (tileY * this.tileSize) + pixelY;
+
+      // Record the smallest/largest absolute coordinates if and only if this tile is the smallest/largest. Otherwise, use previous best
+      absoluteSmallestX = Math.min(absoluteSmallestX, absoluteX);
+      absoluteSmallestY = Math.min(absoluteSmallestY, absoluteY);
+      absoluteLargestX = Math.max(absoluteLargestX, absoluteX + (tileImage.width / this.drawMult));
+      absoluteLargestY = Math.max(absoluteLargestY, absoluteY + (tileImage.height / this.drawMult));
+    })
+
+    console.log(`Absolute coordinates: (${absoluteSmallestX}, ${absoluteSmallestY}) and (${absoluteLargestX}, ${absoluteLargestY})`);
+
+    // Calculates the template/canvas width and height
+    const templateWidth = absoluteLargestX - absoluteSmallestX;
+    const templateHeight = absoluteLargestY - absoluteSmallestY;
+    const canvasWidth = templateWidth * this.drawMult;
+    const canvasHeight = templateHeight * this.drawMult;
+
+    console.log(`Template Width: ${templateWidth}\nTemplate Height: ${templateHeight}\nCanvas Width: ${canvasWidth}\nCanvas Height: ${canvasHeight}`);
+
+    // Creates a new canvas the size of the template
+    const canvas = new OffscreenCanvas(canvasWidth, canvasHeight);
+    const context = canvas.getContext('2d');
+
+    // For each tile...
+    templateTileKeysSorted.forEach((key, index) => {
+
+      // Deconstructs the tile coordinates
+      const [tileX, tileY, pixelX, pixelY] = key.split(',').map(Number);
+
+      const tileImage = templateTilesImageSorted[index]; // Obtains the image for this tile
+
+      // Calculates the absolute pixel coordinates for this tile
+      const absoluteX = (tileX * this.tileSize) + pixelX;
+      const absoluteY = (tileY * this.tileSize) + pixelY;
+
+      console.log(`Drawing tile (${tileX}, ${tileY}, ${pixelX}, ${pixelY}) (${absoluteX}, ${absoluteY}) at (${absoluteX - absoluteSmallestX}, ${absoluteY - absoluteSmallestY}) on the canvas...`);
+
+      // Draws the tile to the canvas
+      context.drawImage(tileImage, (absoluteX - absoluteSmallestX) * this.drawMult, (absoluteY - absoluteSmallestY) * this.drawMult, tileImage.width, tileImage.height);
+    })
+
+    // The expanded template is now on the canvas
+
+    context.globalCompositeOperation = "destination-over"; // Draw under the canvas (new draws only show in place of transparent pixels)
+
+    // Extends the template vertically to create columns
+    context.drawImage(canvas, 0, -1);
+    context.drawImage(canvas, 0, 1);
+
+    // Extends the columns horizontally to become a solid template
+    context.drawImage(canvas, -1, 0);
+    context.drawImage(canvas, 1, 0);
+
+    const smallCanvas = new OffscreenCanvas(templateWidth, templateHeight);
+    const smallContext = smallCanvas.getContext("2d");
+
+    smallContext.imageSmoothingEnabled = false; // Forces nearest neighbor scaling algorithm
+
+    // Downscale the template
+    smallContext.drawImage(
+      canvas,
+      0, 0, templateWidth * this.drawMult, templateHeight * this.drawMult, // Source image size
+      0, 0, templateWidth, templateHeight // Small canvas size
+    );
+
+    // Returns a blob
+    return smallCanvas.convertToBlob({ type: 'image/png' });
+
+    /** Turns a chunked base 64 string template tile into an Image template tile
+     * @param {string} base64 - Base64 string of image data (without URI header)
+     * @since 0.88.474
+     * @returns {Promise} Promise to load a new Image()
+     */
+    function convertBase64ToImage(base64) {
+      return new Promise((resolve, reject) => {
+        const image = new Image();
+        image.onload = () => resolve(image);
+        image.onerror = reject;
+        image.src = "data:image/png;base64," + base64;
+      });
+    }
+  }
+
+  /** Draws all templates on the specified tile.
+   * This method handles the rendering of template overlays on individual tiles.
+   * @param {File} tileBlob - The pixels that are placed on a tile
+   * @param {Array<number>} tileCoords - The tile coordinates [x, y]
+   * @since 0.65.77
+   */
+  async drawTemplateOnTile(tileBlob, tileCoords) {
+
+    // Returns early if no templates should be drawn
+    if (!this.templatesShouldBeDrawn) {return tileBlob;}
+
+    const drawSize = this.tileSize * this.drawMult; // Calculate draw multiplier for scaling
+
+    // Format tile coordinates with proper padding for consistent lookup
+    tileCoords = tileCoords[0].toString().padStart(4, '0') + ',' + tileCoords[1].toString().padStart(4, '0');
+
+    console.log(`Searching for templates in tile: "${tileCoords}"`);
+
+    const templateArray = this.templatesArray; // Stores a copy for sorting
+    console.log(templateArray);
+
+    // Sorts the array of Template class instances. 0 = first = lowest draw priority
+    templateArray.sort((a, b) => {return a.sortID - b.sortID;});
+
+    console.log(templateArray);
+
+    // Retrieves the relavent template tile blobs
+    const templatesToDraw = templateArray
+      .map(template => {
+        const matchingTiles = Object.keys(template.chunked).filter(tile =>
+          tile.startsWith(tileCoords)
+        );
+
+        if (matchingTiles.length === 0) {return null;} // Return null when nothing is found
+
+        // Retrieves the blobs of the templates for this tile
+        const matchingTileBlobs = matchingTiles.map(tile => {
+
+          const coords = tile.split(','); // [x, y, x, y] Tile/pixel coordinates
+          
+          return {
+            instance: template,
+            bitmap: template.chunked[tile],
+            chunked32: template.chunked32?.[tile],
+            tileCoords: [coords[0], coords[1]],
+            pixelCoords: [coords[2], coords[3]]
+          }
+        });
+
+        return matchingTileBlobs?.[0];
+      })
+    .filter(Boolean);
+
+    console.log(templatesToDraw);
+
+    const templateCount = templatesToDraw?.length || 0; // Number of templates to draw on this tile
+    console.log(`templateCount = ${templateCount}`);
+
+    if (templateCount > 0) {
+      
+      // Calculate total pixel count for templates actively being displayed in this tile
+      const totalPixels = templateArray
+        .filter(template => {
+          // Filter templates to include only those with tiles matching current coordinates
+          // This ensures we count pixels only for templates actually being rendered
+          const matchingTiles = Object.keys(template.chunked).filter(tile =>
+            tile.startsWith(tileCoords)
+          );
+          return matchingTiles.length > 0;
+        })
+        .reduce((sum, template) => sum + (template.pixelCount.total || 0), 0);
+      
+      // Format pixel count with locale-appropriate thousands separators for better readability
+      // Examples: "1,234,567" (US), "1.234.567" (DE), "1 234 567" (FR)
+      const pixelCountFormatted = localizeNumber(totalPixels);
+      
+      // Display status information about the templates being rendered
+      this.windowMain.handleDisplayStatus(
+        `Displaying ${templateCount} template${templateCount == 1 ? '' : 's'}.\nTotal pixels: ${pixelCountFormatted}`
+      );
+    } else {
+      //this.overlay.handleDisplayStatus(`Displaying ${templateCount} templates.`);
+      this.windowMain.handleDisplayStatus(`Sleeping\nVersion: ${this.version}`);
+      return tileBlob; // No templates are on this tile. Return the original tile early
+    }
+    
+    const tileBitmap = await createImageBitmap(tileBlob);
+
+    const canvas = new OffscreenCanvas(drawSize, drawSize);
+    const context = canvas.getContext('2d');
+
+    context.imageSmoothingEnabled = false; // Nearest neighbor
+
+    // Tells the canvas to ignore anything outside of this area
+    context.beginPath();
+    context.rect(0, 0, drawSize, drawSize);
+    context.clip();
+
+    context.clearRect(0, 0, drawSize, drawSize); // Draws transparent background
+    context.drawImage(tileBitmap, 0, 0, drawSize, drawSize); // Draw tile to canvas
+
+    const tileBeforeTemplates = context.getImageData(0, 0, drawSize, drawSize);
+    const tileBeforeTemplates32 = new Uint32Array(tileBeforeTemplates.data.buffer);
+
+    // Obtains the highlight pattern
+    const highlightPattern = this.settingsManager?.userSettings?.highlight || [[2, 0, 0]];
+    // The code demands that a highlight pattern always exists.
+    // Therefore, to disable highlighting, the highlight pattern is `[[2, 0, 0]]`.
+    // `[[2, 0, 0]]` is special, and will skip the highlighting code altogether.
+    // As a side-effect, the template will always display while enabled.
+    // You can't disable all sub-pixels in order to hide the template.
+
+    // Contains the first index of the highlight pattern.
+    const highlightPatternIndexZero = highlightPattern?.[0];
+    // This is so we can later determine if the pattern is the preset "None"
+
+    // Should highlighting be disabled?
+    const highlightDisabled = (
+      (highlightPattern?.length == 1)
+      && (highlightPatternIndexZero?.[0] == 2)
+      && (highlightPatternIndexZero?.[1] == 0)
+      && (highlightPatternIndexZero?.[2] == 0)
+    )
+    
+    // For each template in this tile, draw them.
+    for (const template of templatesToDraw) {
+      console.log(`Template:`);
+      console.log(template);
+
+      const templateHasErased = !!template.instance.pixelCount?.colors?.get(-1); // Does this template have Erased (#deface) pixels?
+
+      // Obtains the template (for only this tile) as a Uint32Array
+      let templateBeforeFilter32 = template.chunked32.slice();
+      // Remove the `.slice()` and colors, once disabled, can never be re-enabled
+
+      const coordXtoDrawAt = Number(template.pixelCoords[0]) * this.drawMult;
+      const coordYtoDrawAt = Number(template.pixelCoords[1]) * this.drawMult;
+
+      // Draws the template to the tile if there are no colors to filter, and there are no Erased pixels
+      if ((this.shouldFilterColor.size == 0) && !templateHasErased) {
+        context.drawImage(template.bitmap, coordXtoDrawAt, coordYtoDrawAt);
+      }
+
+      // If we failed to get the template for this tile, we use a shoddy, buggy, failsafe
+      if (!templateBeforeFilter32) {
+        const templateBeforeFilter = context.getImageData(coordXtoDrawAt, coordYtoDrawAt, template.bitmap.width, template.bitmap.height);
+        templateBeforeFilter32 = new Uint32Array(templateBeforeFilter.data.buffer);
+      }
+
+      // Take the pre-filter template ImageData + the pre-filter tile ImageData, and use that to calculate the correct pixels
+      const timer = Date.now();
+      const {
+        correctPixels: pixelsCorrect,
+        filteredTemplate: templateAfterFilter
+      } = this.#calculateCorrectPixelsOnTile_And_FilterTile({
+        tile: tileBeforeTemplates32,
+        template: templateBeforeFilter32,
+        templateInfo: [coordXtoDrawAt, coordYtoDrawAt, template.bitmap.width, template.bitmap.height],
+        highlightPattern: highlightPattern,
+        highlightDisabled: highlightDisabled
+      });
+
+      let pixelsCorrectTotal = 0;
+      const transparentColorID = 0;
+
+      // For each color with correct pixels placed for this template...
+      for (const [color, total] of pixelsCorrect) {
+
+        if (color == transparentColorID) {continue;} // Skip Transparent color
+
+        pixelsCorrectTotal += total; // Add the current total for this color to the summed total of all correct
+      }
+
+      // If there are colors to filter, then we draw the filtered template on the canvas
+      // Or, if there are Erased (#deface) pixels, then we draw the modified template on the canvas
+      // Or, if the user has enabled highlighting, then we draw the modified template on the canvas
+      if ((this.shouldFilterColor.size != 0) || templateHasErased || !highlightDisabled) {
+        console.log('Colors to filter: ', this.shouldFilterColor);
+        //context.putImageData(new ImageData(new Uint8ClampedArray(templateAfterFilter.buffer), template.bitmap.width, template.bitmap.height), coordXtoDrawAt, coordYtoDrawAt);
+        context.drawImage(await createImageBitmap(new ImageData(new Uint8ClampedArray(templateAfterFilter.buffer), template.bitmap.width, template.bitmap.height)), coordXtoDrawAt, coordYtoDrawAt);
+      }
+
+      console.log(`Finished calculating correct pixels & filtering colors for the tile ${tileCoords} in ${(Date.now() - timer) / 1000} seconds!\nThere are ${pixelsCorrectTotal} correct pixels.`);
+
+      // If "correct" does not exist as a key of the object "pixelCount", we create it
+      if (typeof template.instance.pixelCount['correct'] == 'undefined') {
+        template.instance.pixelCount['correct'] = {};
+      }
+
+      // Adds the correct pixel Map to the template instance
+      template.instance.pixelCount['correct'][tileCoords] = pixelsCorrect;
+    }
+
+    return await canvas.convertToBlob({ type: 'image/png' });
+  }
+
+  /** Imports the JSON object, and appends it to any JSON object already loaded
+   * @param {string} json - The JSON string to parse
+   */
+  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
+    }
+  }
+
+  /** Parses the Blue Marble JSON object
+   * @param {string} json - The JSON string to parse
+   * @since 0.72.13
+   */
+  async #parseBlueMarble(json) {
+
+    console.log(`Parsing BlueMarble...`);
+
+    const templates = json.templates;
+
+    console.log(`BlueMarble length: ${Object.keys(templates).length}`);
+
+    const schemaVersion = json?.schemaVersion;
+    const schemaVersionArray = schemaVersion.split(/[-\.\+]/); // SemVer -> string[]
+    const schemaVersionBleedingEdge = this.schemaVersion.split(/[-\.\+]/); // SemVer -> string[]
+    const scriptVersion = json?.scriptVersion;
+
+    console.log(`BlueMarble Template Schema: ${schemaVersion}; Script Version: ${scriptVersion}`);
+
+    // If MAJOR version is up-to-date...
+    if (schemaVersionArray[0] == schemaVersionBleedingEdge[0]) {
+
+      // If MINOR version is NOT up-to-date...
+      if (schemaVersionArray[1] != schemaVersionBleedingEdge[1]) {
+
+        // Spawns a new Template Wizard
+        const windowWizard = new WindowWizard(this.name, this.version, this.schemaVersion, this);
+        windowWizard.buildWindow();
+      }
+
+      // Load using the latest schema loader. It will be fine, probably...
+      this.templatesArray = await loadSchema({
+        tileSize: this.tileSize,
+        drawMult: this.drawMult,
+        templatesArray: this.templatesArray
+      });
+
+    } else if (schemaVersionArray[0] < schemaVersionBleedingEdge[0]) {
+      // Else if the MAJOR verison is out-of-date
+
+      // Spawns a new Template Wizard
+      const windowWizard = new WindowWizard(this.name, this.version, this.schemaVersion, this);
+      windowWizard.buildWindow();
+    
+    } else {
+      // We don't know what the schema is. Unsupported?
+
+      this.windowMain.handleDisplayError(`Template version ${schemaVersion} is unsupported.\nUse Blue Marble version ${scriptVersion} or load a new template.`);
+    }
+
+    /** Loads schema of Blue Marble template storage
+     * @param {Object} params - Object containing parameters
+     * @param {number} params.tileSize - Size of tile
+     * @param {number} params.drawMult - Tile scale multiplier
+     * @param {Array<Template>} params.templatesArray - Array of Template instances
+     * @since 0.88.434
+     */
+    async function loadSchema({
+      tileSize: tileSize,
+      drawMult: drawMult,
+      templatesArray: templatesArray
+    }) {
+
+      // Run only if there are templates saved
+      if (Object.keys(templates).length > 0) {
+  
+        // For each template...
+        for (const template in templates) {
+  
+          const templateKey = template; // The identification key for the template. E.g., "0 $Z"
+          const templateValue = templates[template]; // The actual content of the template
+          console.log(`Template Key: ${templateKey}`);
+  
+          if (templates.hasOwnProperty(template)) {
+  
+            const templateKeyArray = templateKey.split(' '); // E.g., "0 $Z" -> ["0", "$Z"]
+            const sortID = Number(templateKeyArray?.[0]); // Sort ID of the template
+            const authorID = templateKeyArray?.[1] || '0'; // User ID of the person who exported the template
+            const displayName = templateValue.name || `Template ${sortID || ''}`; // Display name of the template
+            //const coords = templateValue?.coords?.split(',').map(Number); // "1,2,3,4" -> [1, 2, 3, 4]
+  
+            const pixelCount = {
+              total: templateValue.pixels?.total,
+              colors: new Map(Object.entries(templateValue.pixels?.colors || {}).map(([key, value]) => [Number(key), value]))
+            };
+  
+            const tilesbase64 = templateValue.tiles;
+            const templateTiles = {}; // Stores the template bitmap tiles for each tile.
+            const templateTiles32 = {}; // Stores the template Uint32Array tiles for each tile.
+  
+            const actualTileSize = tileSize * drawMult;
+  
+            for (const tile in tilesbase64) {
+              console.log(tile);
+              if (tilesbase64.hasOwnProperty(tile)) {
+                const encodedTemplateBase64 = tilesbase64[tile];
+                const templateUint8Array = base64ToUint8(encodedTemplateBase64); // Base 64 -> Uint8Array
+  
+                const templateBlob = new Blob([templateUint8Array], { type: "image/png" }); // Uint8Array -> Blob
+                const templateBitmap = await createImageBitmap(templateBlob) // Blob -> Bitmap
+                templateTiles[tile] = templateBitmap;
+  
+                // Converts to Uint32Array
+                const canvas = new OffscreenCanvas(actualTileSize, actualTileSize);
+                const context = canvas.getContext('2d');
+                context.drawImage(templateBitmap, 0, 0);
+                const imageData = context.getImageData(0, 0, templateBitmap.width, templateBitmap.height);
+                templateTiles32[tile] = new Uint32Array(imageData.data.buffer);
+              }
+            }
+  
+            // Creates a new Template class instance
+            const template = new Template({
+              displayName: displayName,
+              sortID: sortID || this.templatesArray?.length || 0,
+              authorID: authorID || '',
+              //coords: coords,
+            });
+            template.pixelCount = pixelCount;
+            template.chunked = templateTiles;
+            template.chunked32 = templateTiles32;
+            
+            templatesArray.push(template);
+            console.log(this.templatesArray);
+            console.log(`^^^ This ^^^`);
+          }
+        }
+      }
+
+      return templatesArray
+    }
+  }
+
+  /** Parses the OSU! Place JSON object
+   */
+  #parseOSU() {
+
+  }
+
+  /** Sets the `templatesShouldBeDrawn` boolean to a value.
+   * @param {boolean} value - The value to set the boolean to
+   * @since 0.73.7
+   */
+  setTemplatesShouldBeDrawn(value) {
+    this.templatesShouldBeDrawn = value;
+  }
+
+  /** Calculates the correct pixels on this tile.
+   * In addition, this function filters colors based on user input.
+   * In addition, this function modifies colors to properly display (#deface).
+   * In addition, this function modifies incorrect pixels to display highlighting.
+   * This function has multiple purposes only to reduce iterations of scans over every pixel on the template.
+   * @param {Object} params - Object containing all parameters
+   * @param {Uint32Array} params.tile - The tile without templates as a Uint32Array
+   * @param {Uint32Array} params.template - The template without filtering as a Uint32Array
+   * @param {Array<Number, Number, Number, Number>} params.templateInfo - Information about template location and size
+   * @param {Array<number[]>} params.highlightPattern - The highlight pattern selected by the user
+   * @param {boolean} params.highlightDisabled - Should highlighting be disabled?
+   * @returns {{correctPixels: Map<number, number>, filteredTemplate: Uint32Array}} A Map containing the color IDs (keys) and how many correct pixels there are for that color (values)
+   */
+  #calculateCorrectPixelsOnTile_And_FilterTile({
+    tile: tile32, 
+    template: template32, 
+    templateInfo: templateInformation,
+    highlightPattern: highlightPattern,
+    highlightDisabled: highlightDisabled
+  }) {
+
+    // Size of a pixel in actuality
+    const pixelSize = this.drawMult;
+
+    // Tile information
+    const tileWidth = this.tileSize * pixelSize;
+    const tileHeight = tileWidth;
+    const tilePixelOffsetY = -1; // Shift off of target template pixel to target on tile. E.g. "-1" would be the pixel above the template pixel on the tile
+    const tilePixelOffsetX = 0; // Shift off of target template pixel to target on tile. E.g. "-1" would be the pixel to the left of the template pixel on the tile
+
+    // Template information
+    const templateCoordX = templateInformation[0];
+    const templateCoordY = templateInformation[1];
+    const templateWidth = templateInformation[2];
+    const templateHeight = templateInformation[3];
+    const tolerance = this.paletteTolerance;
+
+    //console.log(`TemplateX: ${templateCoordX}\nTemplateY: ${templateCoordY}\nStarting Row:${templateCoordY+tilePixelOffsetY}\nStarting Column:${templateCoordX+tilePixelOffsetX}`);
+
+    // Obtains if the user wants to highlight tile pixels that are transparent, but the template pixel is not
+    const shouldTransparentTilePixelsBeHighlighted = !this.settingsManager?.userSettings?.flags?.includes('hl-noTrans');
+    // The actual logic of this boolean is "should all pixels be highlighted"
+
+    const { palette: _, LUT: lookupTable } = this.paletteBM; // Obtains the palette and LUT
+
+    // Makes a copy of the color palette Blue Marble uses, turns it into a Map, and adds data to count the amount of each color
+    const _colorpalette = new Map(); // Temp color palette
+
+    // For each center pixel...
+    for (let templateRow = 1; templateRow < templateHeight; templateRow += pixelSize) {
+      for (let templateColumn = 1; templateColumn < templateWidth; templateColumn += pixelSize) {
+        // ROWS ARE VERTICAL. "ROWS" AS IN, LIKE ON A SPREADSHEET
+        // COLUMNS ARE HORIZONTAL. "COLUMNS" AS IN, LIKE ON A SPREADSHEET
+        // THE FIFTH ROW IS FIVE DOWN FROM THE ZEROTH ROW
+        // THE THIRD COLUMN IS TO THE RIGHT OF THE FIRST COLUMN
+
+        // The pixel on the tile to target (1 pixel above the template)
+        const tileRow = (templateCoordY + templateRow) + tilePixelOffsetY; // (Template offset + current row) - 1
+        const tileColumn = (templateCoordX + templateColumn) + tilePixelOffsetX; // Template offset + current column
+        
+        // Retrieves the targeted pixels
+        const tilePixelAbove = tile32[(tileRow * tileWidth) + tileColumn];
+        const templatePixel = template32[(templateRow * templateWidth) + templateColumn];
+
+        // Obtains the alpha channel of the targeted pixels
+        const templatePixelAlpha = (templatePixel >>> 24) & 0xFF;
+        const tilePixelAlpha = (tilePixelAbove >>> 24) & 0xFF;
+
+        // Finds the best matching color ID for the template pixel. If none is found, default to "-2"
+        const bestTemplateColorID = lookupTable.get(templatePixel) ?? -2;
+
+        // Finds the best matching color ID for the tile pixel. If none is found, default to "-2"
+        const bestTileColorID = lookupTable.get(tilePixelAbove) ?? -2;
+
+        // -----     COLOR FILTER      -----
+        // If this pixel on the template is a color the user wants to hide on the canvas...
+        if (this.shouldFilterColor.get(bestTemplateColorID)) {
+
+          // Sets template pixel to match tile background (which removes the template pixel from the user's view)
+          template32[(templateRow * templateWidth) + templateColumn] = tilePixelAbove;
+        }
+        // -----  END OF COLOR FILTER  -----
+
+        // -----        ERASED         -----
+        // If this pixel on the template is the Erased (#deface) color...
+        if (bestTemplateColorID == -1) {
+
+          const blackTrans = 0x20000000; // Black translucent color for Erased pixels
+
+          // If Erased color should be filtered
+          if (this.shouldFilterColor.get(bestTemplateColorID)) {
+            template32[(templateRow * templateWidth) + templateColumn] = 0x00000000; // Center (black, 0% opacity)
+          } else {
+            // Don't filter Erased color
+
+            // If the tile row and tile column are even,
+            // Or the tile row and tile column are odd...
+            if (((tileRow / pixelSize) & 1) == ((tileColumn / pixelSize) & 1)) {
+
+              // Sets the template pixels to be a semi-transparent, black grid
+              template32[(templateRow * templateWidth) + templateColumn] = blackTrans; // Center
+              template32[((templateRow - 1) * templateWidth) + (templateColumn - 1)] = blackTrans; // Top Left
+              template32[((templateRow - 1) * templateWidth) + (templateColumn + 1)] = blackTrans; // Top Right
+              template32[((templateRow + 1) * templateWidth) + (templateColumn - 1)] = blackTrans; // Bottom Left
+              template32[((templateRow + 1) * templateWidth) + (templateColumn + 1)] = blackTrans; // Bottom Right
+            } else {
+              // Else, either the row or column is odd, and the other is even.
+
+              // Sets the template pixels to the the inverse of a semi-transparent, black grid
+              template32[(templateRow * templateWidth) + templateColumn] = 0x00000000; // Center (black, 0% opacity)
+              template32[((templateRow - 1) * templateWidth) + (templateColumn)] = blackTrans; // Top Center
+              template32[((templateRow + 1) * templateWidth) + (templateColumn)] = blackTrans; // Bottom Center
+              template32[((templateRow) * templateWidth) + (templateColumn - 1)] = blackTrans; // Middle Left
+              template32[((templateRow) * templateWidth) + (templateColumn + 1)] = blackTrans; // Middle Right
+            }
+          }
+        }
+        // -----     END OF ERASED     -----
+
+        // -----     HIGHLIGHTING      -----
+
+        // If highlighting is enabled, AND the template pixel is NOT transparent AND the template pixel does NOT match the tile pixel
+        if (!highlightDisabled && (templatePixelAlpha > tolerance) && (bestTileColorID != bestTemplateColorID)) {
+
+          // If the tile pixel is NOT transparent, OR the user wants to highlight transparent pixels
+          if (shouldTransparentTilePixelsBeHighlighted || (tilePixelAlpha > tolerance)) {
+
+            // Obtains the template color of this pixel
+            const templatePixelColor = template32[(templateRow * templateWidth) + templateColumn];
+            // This will retrieve the tile background instead if the color is filtered!
+
+            // For each of the 9 subpixels inside the pixel...
+            for (const subpixelPattern of highlightPattern) {
+
+              // Deconstructs the sub pixel
+              const [subpixelState, subpixelColumnDelta, subpixelRowDelta] = subpixelPattern;
+              // "Delta" because the coordinate of the sub-pixel is relative to the center of the pixel
+
+              // Obtains the subpixel color to use
+              const subpixelColor = (subpixelState != 0) ? ((subpixelState != 1) ? templatePixelColor : 0xFF0000FF) : 0x00000000;
+              // 0 = Transparent (black)
+              // 1 = Red (#FF0000)
+              // 2 = Template (matches template or hides if filtered)
+
+              // Sets the subpixel to match the color on the highlight pattern
+              template32[((templateRow + subpixelRowDelta) * templateWidth) + (templateColumn + subpixelColumnDelta)] = subpixelColor;
+            }
+          }
+        }
+
+        // -----  END OF HIGHLIGHTING  -----
+
+        // If the template pixel is Erased, and the tile pixel is transparent...
+        if ((bestTemplateColorID == -1) && (tilePixelAbove <= tolerance)) {
+
+          // Increments the count by 1 for the Erased (#deface) color.
+          // If the color ID has not been counted yet, default to 1
+          const colorIDcount = _colorpalette.get(bestTemplateColorID);
+          _colorpalette.set(bestTemplateColorID, colorIDcount ? colorIDcount + 1 : 1);
+          continue;
+        }
+        // If the code passes this point, the pixel is not a correct Erased color.
+
+        // If either pixel is transparent...
+        if ((templatePixelAlpha <= tolerance) || (tilePixelAlpha <= tolerance)) {
+          continue; // ...we skip it. We can't match the RGB color of transparent pixels.
+        }
+        // If the code passes this point, both pixels are opaque & not Erased.
+
+        // If the template pixel does not match the tile pixel, then the pixel is skipped after highlighting.
+        if (bestTileColorID != bestTemplateColorID) {
+          continue;
+        }
+        // If the code passes this point, the template pixel matches the tile pixel.
+
+        // Increments the count by 1 for the best matching color ID (which can be negative).
+        // If the color ID has not been counted yet, default to 1
+        const colorIDcount = _colorpalette.get(bestTemplateColorID);
+        _colorpalette.set(bestTemplateColorID, colorIDcount ? colorIDcount + 1 : 1);
+      }
+    }
+
+    console.log(`List of template pixels that match the tile:`);
+    console.log(_colorpalette);
+    return { correctPixels: _colorpalette, filteredTemplate: template32 };
+  }
+}
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + + diff --git a/docs/utils.js.html b/docs/utils.js.html new file mode 100644 index 0000000..e877652 --- /dev/null +++ b/docs/utils.js.html @@ -0,0 +1,546 @@ + + + + + + utils.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

utils.js

+ + + + + + + +
+
+

+/** Returns a Date of when Wplace was last updated.
+ * This is obtained from a certain DOM element which contains the version of Wplace.
+ * @since 0.90.25
+ * @returns {Date | undefined} - The date that Wplace was last updated, as a Date.
+ */
+export function getWplaceVersion() {
+  const wplaceVersionElement = [...document.querySelectorAll(`body > div > .hidden`)].filter(match => /version:/i.test(match.textContent));
+  if (wplaceVersionElement[0]) { // If there is at least one match...
+    const wplaceUpdateTime = wplaceVersionElement[0].textContent?.match(/\d+/); // Obtain the last update time, which is Unix Epoch in milliseconds
+    return wplaceUpdateTime ? new Date(Number(wplaceUpdateTime[0])) : undefined; // Return the time as a Date, or undefined
+  }
+  return undefined;
+}
+
+/** Halts execution of this specific userscript, for the specified time.
+ * This will not block the thread.
+ * @param {number} time - Time to wait in milliseconds
+ * @since 0.88.483
+ * @returns {Promise} Promise of a setTimeout()
+ */
+export function sleep(time) {
+  return new Promise(resolve => setTimeout(resolve, time));
+}
+
+/** View the canvas in a new tab.
+ * @param {HTMLCanvasElement | OffscreenCanvas} canvas - The canvas to view
+ * @param {number} [lifeDuration=60_000] - (Optional) The lifetime of the URL blob in milliseconds
+ * @since 0.88.484
+ */
+export async function viewCanvasInNewTab(canvas, lifeDuration = 60_000) {
+  const final = await canvas.convertToBlob({ type: 'image/png' });
+  const url = URL.createObjectURL(final); // Creates a blob URL
+  window.open(url, '_blank'); // Opens a new tab with blob
+  setTimeout(() => URL.revokeObjectURL(url), lifeDuration); // Destroys the blob after this time (default of 1 minute)
+}
+
+/** Returns the localized number format.
+ * @param {number} number - The number to localize
+ * @since 0.88.472
+ * @returns {string} Localized number as a string
+ */
+export function localizeNumber(number) {
+  const numberFormat = new Intl.NumberFormat();
+  return numberFormat.format(number);
+}
+
+/** Returns the localized percentage format.
+ * @param {number} percent - The percentage to localize
+ * @since 0.88.472
+ * @returns {string} Localized percentage as a string
+ */
+export function localizePercent(percent) {
+  const percentFormat = new Intl.NumberFormat(undefined, {
+    style: 'percent',
+    minimumFractionDigits: 2,
+    maximumFractionDigits: 2
+  });
+  return percentFormat.format(percent);
+}
+
+/** Returns the localized date format.
+ * @param {number} date - The date to localize
+ * @since 0.88.472
+ * @returns {string} Localized date as a string
+ */
+export function localizeDate(date) {
+  const options = {
+    month: 'long', // July
+    day: 'numeric', // 23
+    hour: '2-digit', // 17
+    minute: '2-digit', // 47
+    second: '2-digit' // 00
+  };
+  return date.toLocaleString(undefined, options);
+}
+
+/** Returns the localized duration format.
+ * @param {number} durationTotalMs - The duration to localize, in milliseconds
+ * @since 0.88.472
+ * @returns {string} Localized duration as a string
+ */
+export function localizeDuration(durationTotalMs) {
+
+  // "Total" indicates it is the total time for that unit. E.g. 62 minutes is "62" minutes.
+  const durationTotalSec = Math.floor(durationTotalMs / 1000);
+  const durationTotalHr = Math.floor(durationTotalSec / 3600);
+
+  // "Only" indicates it is formatted in that unit. E.g. 62 minutes is "2" minutes.
+  const durationOnlySec = Math.floor(durationTotalSec % 60);
+  const durationOnlyMin = Math.floor((durationTotalSec % 3600) / 60);
+
+  // Duration Object for localization
+  const duration = {
+    hours: durationTotalHr,
+    minutes: durationOnlyMin,
+    seconds: durationOnlySec
+  };
+
+  // Options Object for localization
+  const options = {
+    style: 'short'
+  };
+
+  return new Intl.DurationFormat(undefined, options).format(duration);
+}
+
+/** Sanitizes HTML to display as plain-text.
+ * This prevents some Cross Site Scripting (XSS).
+ * This is handy when you are displaying user-made data, and you *must* use innerHTML.
+ * @param {string} text - The text to sanitize
+ * @returns {string} HTML escaped string
+ * @since 0.44.2
+ * @example
+ * const paragraph = document.createElement('p');
+ * paragraph.innerHTML = escapeHTML('<u>Foobar.</u>');
+ * // Output:
+ * // (Does not include the paragraph element)
+ * // (Output is not HTML formatted)
+ * <p>
+ *   "<u>Foobar.</u>"
+ * </p>
+ */
+export function escapeHTML(text) {
+  const div = document.createElement('div'); // Creates a div
+  div.textContent = text; // Puts the text in a PLAIN-TEXT property
+  return div.innerHTML; // Returns the HTML property of the div
+}
+
+/** Converts the server tile-pixel coordinate system to the displayed tile-pixel coordinate system.
+ * @param {Array<string, string>} tile - The tile to convert
+ * @param {Array<string, string>} pixel - The pixel to convert
+ * @returns {Array<number, number>} Tile and pixel coordinate pair
+ * @since 0.42.4
+ * @example
+ * console.log(serverTPtoDisplayTP(['12', '123'], ['34', '567'])); // [34, 3567]
+ */
+export function serverTPtoDisplayTP(tile, pixel) {
+  return [((parseInt(tile[0]) % 4) * 1000) + parseInt(pixel[0]), ((parseInt(tile[1]) % 4) * 1000) + parseInt(pixel[1])];
+}
+
+/** Negative-Safe Modulo. You can pass negative numbers into this.
+ * @param {number} a - The first number
+ * @param {number} b - The second number
+ * @returns {number} Result
+ * @author osuplace
+ * @since 0.55.8
+ */
+export function negativeSafeModulo(a, b) {
+  return (a % b + b) % b;
+}
+
+/** Bypasses terser's stripping of console function calls.
+ * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't.
+ * However, the distributed version needs to call the console somehow, so this wrapper function is how.
+ * This is the same as `console.log()`.
+ * @param {...any} args - Arguments to be passed into the `log()` function of the Console
+ * @since 0.58.9
+ */
+export function consoleLog(...args) {((consoleLog) => consoleLog(...args))(console.log);}
+
+/** Bypasses terser's stripping of console function calls.
+ * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't.
+ * However, the distributed version needs to call the console somehow, so this wrapper function is how.
+ * This is the same as `console.error()`.
+ * @param {...any} args - Arguments to be passed into the `error()` function of the Console
+ * @since 0.58.13
+ */
+export function consoleError(...args) {((consoleError) => consoleError(...args))(console.error);}
+
+/** Bypasses terser's stripping of console function calls.
+ * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't.
+ * However, the distributed version needs to call the console somehow, so this wrapper function is how.
+ * This is the same as `console.warn()`.
+ * @param {...any} args - Arguments to be passed into the `warn()` function of the Console
+ * @since 0.58.13
+ */
+export function consoleWarn(...args) {((consoleWarn) => consoleWarn(...args))(console.warn);}
+
+/** Encodes a number into a custom encoded string.
+ * @param {number} number - The number to encode
+ * @param {string} encoding - The characters to use when encoding
+ * @since 0.65.2
+ * @returns {string} Encoded string
+ * @example
+ * const encode = '012abcABC'; // Base 9
+ * console.log(numberToEncoded(0, encode)); // 0
+ * console.log(numberToEncoded(5, encode)); // c
+ * console.log(numberToEncoded(15, encode)); // 1A
+ * console.log(numberToEncoded(12345, encode)); // 1BCaA
+ */
+export function numberToEncoded(number, encoding) {
+
+  if (number === 0) return encoding[0]; // End quickly if number equals 0. No special calculation needed
+
+  let result = ''; // The encoded string
+  const base = encoding.length; // The number of characters used, which determines the base
+
+  // Base conversion algorithm
+  while (number > 0) {
+    result = encoding[number % base] + result; // Find's the character's encoded value determined by the modulo of the base
+    number = Math.floor(number / base); // Divides the number by the base so the next iteration can find the next modulo character
+  }
+
+  return result; // The final encoded string
+}
+
+/** Decodes a number from a custom encoded string.
+ * @param {string} encoded - The encoded string
+ * @param {string} encoding - The characters to use when decoding
+ * @since 0.88.448
+ * @returns {number} Decoded number
+ * @example
+ * const encode = '012abcABC'; // Base 9
+ * console.log(encodedToNumber('0', encode));     // 0
+ * console.log(encodedToNumber('c', encode));     // 5
+ * console.log(encodedToNumber('1A', encode));    // 15
+ * console.log(encodedToNumber('1BCaA', encode)); // 12345
+ */
+export function encodedToNumber(encoded, encoding) {
+
+  let decodedNumber = 0; // The decoded number
+  const base = encoding.length; // The number of characters used, which determins the base
+
+  // For every character in the encoded string...
+  for (const character of encoded) {
+
+    const decodedCharacter = encoding.indexOf(character); // Decodes the character
+
+    // If no matching decode was found for this character...
+    if (decodedCharacter == -1) {
+      consoleError(`Invalid character '${character}' encountered whilst decoding! Is the decode alphabet/base incorrect?`);
+    }
+
+    decodedNumber = (decodedNumber * base) + decodedCharacter; // Adds the decoded character to the final number
+  }
+
+  return decodedNumber; // Returns the decoded number
+}
+
+/** Converts a Uint8 array to base64 using the browser's built-in binary to ASCII function
+ * @param {Uint8Array} uint8 - The Uint8Array to convert
+ * @returns {Uint8Array} The base64 encoded Uint8Array
+ * @since 0.72.9
+ */
+export function uint8ToBase64(uint8) {
+  let binary = '';
+  for (let i = 0; i < uint8.length; i++) {
+    binary += String.fromCharCode(uint8[i]);
+  }
+  return btoa(binary); // Binary to ASCII
+}
+
+/** Decodes a base 64 encoded Uint8 array using the browser's built-in ASCII to binary function
+ * @param {Uint8Array} base64 - The base 64 encoded Uint8Array to convert
+ * @returns {Uint8Array} The decoded Uint8Array
+ * @since 0.72.9
+ */
+export function base64ToUint8(base64) {
+  const binary = atob(base64); // ASCII to Binary
+  const array = new Uint8Array(binary.length);
+  for (let i = 0; i < binary.length; i++) {
+    array[i] = binary.charCodeAt(i);
+  }
+  return array;
+}
+
+/** Handles reading from the clipboard.
+ * Assume this only returns text.
+ * Assume this requires user input.
+ * @param {ClipboardEvent} [event=undefined] - (Optional) The clipboard event that triggered this to run
+ * @since 0.88.426
+ * @returns {string} The clipboard data as a string
+ */
+export async function getClipboardData(event = undefined) {
+
+  let data = ''; // Data from clipboard
+
+  // Try using the event, if any was provided
+  if (event) {
+    data = event.clipboardData.getData('text/plain');
+  }
+
+  if (data.length != 0) {return data;} // Continue only if data is still empty
+  
+  // Try using the navigator clipboard
+  await navigator.clipboard.readText().then(text => {
+    data = text;
+  }).catch(error => {
+    consoleLog(`Failed to retrieve clipboard data using navigator! Using fallback methods...`);
+  });
+
+  if (data.length != 0) {return data;} // Continue only if data is still empty
+
+  // Try using IE clipboard
+  data = window.clipboardData?.getData('Text');
+
+  return data;
+}
+
+/** Calcualtes the relative luminance of an RGB value
+ * @param {Array<Number, Number, Number>} array - The RGB values as an array
+ * @returns {Number} The relative luminance as a Number
+ * @since 0.88.180
+ */
+export function calculateRelativeLuminance(array) {
+
+  // Convert the 0-255 range to 0-1
+  const srgb = array.map(channel => {
+    channel /= 255;
+    return (channel <= 0.03928) ? (channel / 12.92) : Math.pow((channel + 0.055) / 1.055, 2.4);
+  });
+
+  // https://en.wikipedia.org/wiki/Relative_luminance#Relative_luminance_and_%22gamma_encoded%22_colorspaces
+  return (0.2126 * srgb[0]) + (0.7152 * srgb[1]) + (0.0722 * srgb[2]);
+}
+
+/** Converts an RGB color to hexdecimal color.
+ * Octothorpe not included.
+ * @param {number | Array<number, number, number>} red - The Red channel of the RGB color, or all three channels as an Array
+ * @param {number} [green] - The Green channel of the RGB color
+ * @param {number} [blue] - The Blue channel of the RGB color
+ * @returns {string} Hex color code as string
+ * @since 0.90.31
+ */
+export function rgbToHex(red, green, blue) {
+  if (Array.isArray(red)) {[red, green, blue] = red;} // Deconstruct the Array if an Array was passed in
+  return ((1 << 24) | (red << 16) | (green << 8) | blue).toString(16).slice(1); // Packs it into a 24-bit integer, then converts it to base16.
+}
+
+/** Converts a hexdecimal color to an RGB color.
+ * Alpha channel not supported.
+ * @param {string} hex - Hex color code as string
+ * @returns {Array<number, number, number>} RGB color as an Array
+ * @since 0.90.31
+ */
+export function hexToRGB(hex) {
+  hex = (hex[0] == '#') ? hex.slice(1) : hex; // Removes the octothorpe, if any
+  const packedIntRGB = parseInt(hex, 16); // Converts (base16) into an integer
+  return [(packedIntRGB >> 16 & 255), (packedIntRGB >> 8 & 255), (packedIntRGB & 255)]; // Unpacks the integer into the RGB channels
+}
+
+/** Returns the coordinate input fields
+ * @returns {Element[]} The 4 coordinate Inputs
+ * @since 0.74.0
+ */
+export function selectAllCoordinateInputs(document) {
+  coords = [];
+
+  coords.push(document.querySelector('#bm-input-tx'));
+  coords.push(document.querySelector('#bm-input-ty'));
+  coords.push(document.querySelector('#bm-input-px'));
+  coords.push(document.querySelector('#bm-input-py'));
+
+  return coords;
+}
+
+/** Processes the palette used for Blue Marble.
+ * Each ID is sorted from smallest to largest.
+ * Color ID's are integers, which can be negative.
+ * Custom colors have been added for the Blue Marble purposes.
+ * Wplace palette colors have not been modified.
+ * @since 0.88.6
+ */
+export function colorpaletteForBlueMarble(tolerance) {
+
+  const colorpaletteBM = colorpalette; // Makes a copy
+
+  // Adds the Blue Marble color for "erased" and "other" pixels to the palette list
+  colorpaletteBM.unshift({ "id": -1,  "premium": false, "name": "Erased",      "rgb": [222, 250, 206] });
+  colorpaletteBM.unshift({ "id": -2,  "premium": false, "name": "Other",       "rgb": [  0,   0,   0] });
+
+  const lookupTable = new Map();
+
+  // For each color in Blue Marble's palette...
+  for (const color of colorpaletteBM) {
+    if ((color.id == 0) || (color.id == -2)) continue; // skip Transparent or Other colors
+
+    // Target RGB values. These are exactly correct.
+    const targetRed = color.rgb[0];
+    const targetGreen = color.rgb[1];
+    const targetBlue = color.rgb[2];
+
+    // For each RGB value in the range of RGB values centered on the target RGB value for each channel...
+    for (let deltaRedRange = -tolerance; deltaRedRange <= tolerance; deltaRedRange++) {
+      for (let deltaGreenRange = -tolerance; deltaGreenRange <= tolerance; deltaGreenRange++) {
+        for (let deltaBlueRange = -tolerance; deltaBlueRange <= tolerance; deltaBlueRange++) {
+          // Basically, we are making a "cube" around each target value.
+          // Say the tolerance is 3. The size of the cube will be ((3*2)+1)^3 which is 343 total.
+          // This means 343 colors will be Mapped as associated to the target color ID because 343 colors are in the "cube" surrounding and including the target color
+
+          // This specific deviation from the target RGB color values within the cube
+          const derivativeRed = targetRed + deltaRedRange;
+          const derivativeGreen = targetGreen + deltaGreenRange;
+          const derivativeBlue = targetBlue + deltaBlueRange;
+
+          // If it is impossible for the color to exist, then skip the color
+          if (derivativeRed < 0 || derivativeRed > 255 || derivativeGreen < 0 || derivativeGreen > 255 || derivativeBlue < 0 || derivativeBlue > 255) continue;
+
+          // Packed into 32-bit integer like RGBA = 0xAARRGGBB with the alpha channel forced to be 255
+          // Also, it is forced to be an unsigned 32-bit integer
+          const derivativeColor32 = ((255 << 24) | (derivativeBlue << 16) | (derivativeGreen << 8) | derivativeRed) >>> 0;
+          if (!lookupTable.has(derivativeColor32)) {
+            lookupTable.set(derivativeColor32, color.id);
+          }
+        }
+      }
+    }
+  }
+
+  return {palette: colorpaletteBM, LUT: lookupTable}
+}
+
+/** The color palette used by wplace.live
+ * @since 0.78.0
+ * @examples
+ * import utils from 'src/utils.js';
+ * console.log(utils[5]?.name); // "White"
+ * console.log(utils[5]?.rgb); // [255, 255, 255]
+ */
+export const colorpalette = [
+  { "id": 0,  "premium": false, "name": "Transparent",      "rgb": [  0,   0,   0] },
+  { "id": 1,  "premium": false, "name": "Black",            "rgb": [  0,   0,   0] },
+  { "id": 2,  "premium": false, "name": "Dark Gray",        "rgb": [ 60,  60,  60] },
+  { "id": 3,  "premium": false, "name": "Gray",             "rgb": [120, 120, 120] },
+  { "id": 4,  "premium": false, "name": "Light Gray",       "rgb": [210, 210, 210] },
+  { "id": 5,  "premium": false, "name": "White",            "rgb": [255, 255, 255] },
+  { "id": 6,  "premium": false, "name": "Deep Red",         "rgb": [ 96,   0,  24] },
+  { "id": 7,  "premium": false, "name": "Red",              "rgb": [237,  28,  36] },
+  { "id": 8,  "premium": false, "name": "Orange",           "rgb": [255, 127,  39] },
+  { "id": 9,  "premium": false, "name": "Gold",             "rgb": [246, 170,   9] },
+  { "id": 10, "premium": false, "name": "Yellow",           "rgb": [249, 221,  59] },
+  { "id": 11, "premium": false, "name": "Light Yellow",     "rgb": [255, 250, 188] },
+  { "id": 12, "premium": false, "name": "Dark Green",       "rgb": [ 14, 185, 104] },
+  { "id": 13, "premium": false, "name": "Green",            "rgb": [ 19, 230, 123] },
+  { "id": 14, "premium": false, "name": "Light Green",      "rgb": [135, 255,  94] },
+  { "id": 15, "premium": false, "name": "Dark Teal",        "rgb": [ 12, 129, 110] },
+  { "id": 16, "premium": false, "name": "Teal",             "rgb": [ 16, 174, 166] },
+  { "id": 17, "premium": false, "name": "Light Teal",       "rgb": [ 19, 225, 190] },
+  { "id": 18, "premium": false, "name": "Dark Blue",        "rgb": [ 40,  80, 158] },
+  { "id": 19, "premium": false, "name": "Blue",             "rgb": [ 64, 147, 228] },
+  { "id": 20, "premium": false, "name": "Cyan",             "rgb": [ 96, 247, 242] },
+  { "id": 21, "premium": false, "name": "Indigo",           "rgb": [107,  80, 246] },
+  { "id": 22, "premium": false, "name": "Light Indigo",     "rgb": [153, 177, 251] },
+  { "id": 23, "premium": false, "name": "Dark Purple",      "rgb": [120,  12, 153] },
+  { "id": 24, "premium": false, "name": "Purple",           "rgb": [170,  56, 185] },
+  { "id": 25, "premium": false, "name": "Light Purple",     "rgb": [224, 159, 249] },
+  { "id": 26, "premium": false, "name": "Dark Pink",        "rgb": [203,   0, 122] },
+  { "id": 27, "premium": false, "name": "Pink",             "rgb": [236,  31, 128] },
+  { "id": 28, "premium": false, "name": "Light Pink",       "rgb": [243, 141, 169] },
+  { "id": 29, "premium": false, "name": "Dark Brown",       "rgb": [104,  70,  52] },
+  { "id": 30, "premium": false, "name": "Brown",            "rgb": [149, 104,  42] },
+  { "id": 31, "premium": false, "name": "Beige",            "rgb": [248, 178, 119] },
+  { "id": 32, "premium": true,  "name": "Medium Gray",      "rgb": [170, 170, 170] },
+  { "id": 33, "premium": true,  "name": "Dark Red",         "rgb": [165,  14,  30] },
+  { "id": 34, "premium": true,  "name": "Light Red",        "rgb": [250, 128, 114] },
+  { "id": 35, "premium": true,  "name": "Dark Orange",      "rgb": [228,  92,  26] },
+  { "id": 36, "premium": true,  "name": "Light Tan",        "rgb": [214, 181, 148] },
+  { "id": 37, "premium": true,  "name": "Dark Goldenrod",   "rgb": [156, 132,  49] },
+  { "id": 38, "premium": true,  "name": "Goldenrod",        "rgb": [197, 173,  49] },
+  { "id": 39, "premium": true,  "name": "Light Goldenrod",  "rgb": [232, 212,  95] },
+  { "id": 40, "premium": true,  "name": "Dark Olive",       "rgb": [ 74, 107,  58] },
+  { "id": 41, "premium": true,  "name": "Olive",            "rgb": [ 90, 148,  74] },
+  { "id": 42, "premium": true,  "name": "Light Olive",      "rgb": [132, 197, 115] },
+  { "id": 43, "premium": true,  "name": "Dark Cyan",        "rgb": [ 15, 121, 159] },
+  { "id": 44, "premium": true,  "name": "Light Cyan",       "rgb": [187, 250, 242] },
+  { "id": 45, "premium": true,  "name": "Light Blue",       "rgb": [125, 199, 255] },
+  { "id": 46, "premium": true,  "name": "Dark Indigo",      "rgb": [ 77,  49, 184] },
+  { "id": 47, "premium": true,  "name": "Dark Slate Blue",  "rgb": [ 74,  66, 132] },
+  { "id": 48, "premium": true,  "name": "Slate Blue",       "rgb": [122, 113, 196] },
+  { "id": 49, "premium": true,  "name": "Light Slate Blue", "rgb": [181, 174, 241] },
+  { "id": 50, "premium": true,  "name": "Light Brown",      "rgb": [219, 164,  99] },
+  { "id": 51, "premium": true,  "name": "Dark Beige",       "rgb": [209, 128,  81] },
+  { "id": 52, "premium": true,  "name": "Light Beige",      "rgb": [255, 197, 165] },
+  { "id": 53, "premium": true,  "name": "Dark Peach",       "rgb": [155,  82,  73] },
+  { "id": 54, "premium": true,  "name": "Peach",            "rgb": [209, 128, 120] },
+  { "id": 55, "premium": true,  "name": "Light Peach",      "rgb": [250, 182, 164] },
+  { "id": 56, "premium": true,  "name": "Dark Tan",         "rgb": [123,  99,  82] },
+  { "id": 57, "premium": true,  "name": "Tan",              "rgb": [156, 132, 107] },
+  { "id": 58, "premium": true,  "name": "Dark Slate",       "rgb": [ 51,  57,  65] },
+  { "id": 59, "premium": true,  "name": "Slate",            "rgb": [109, 117, 141] },
+  { "id": 60, "premium": true,  "name": "Light Slate",      "rgb": [179, 185, 209] },
+  { "id": 61, "premium": true,  "name": "Dark Stone",       "rgb": [109, 100,  63] },
+  { "id": 62, "premium": true,  "name": "Stone",            "rgb": [148, 140, 107] },
+  { "id": 63, "premium": true,  "name": "Light Stone",      "rgb": [205, 197, 158] }
+];
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 4.0.5 on Tue Mar 17 2026 01:31:43 GMT+0000 (Coordinated Universal Time) using the Minami theme. +
+ + + + +

)CdQ3F!eKyF|9D2V!F-rhUpJ8J+l7g0OBBVM#THTI&O8)>EGR%u6Gd9D9pm5!S}%&6DxW}^f|7=Y zPoO3(pTZY#?(7(|!5}5Nn!D%DotZmlW)?smSMcEE<^aT$6gw#LlwubPI9BYTffL0! zyu-EPCnz{Y#ZR&1d{F!hr_NW!&#~mXis$jseXDo@U)-kR7sMBeUt-T&RQw9By@BF9 z3f?cpmw4m-R{RHncaC**(V--ipJ<~6LkW2fi6RVfh%vcYt9@z>&M0LBSf-Q|Et8wU zCt43_*JB)mHR71wb`K@~5Cizwp{`A2uuJ^_Bcl3k{7ree$8&@l?;^2nagS+NqCDBfkB?pJws=PbK~+A7|2 z{gCDJKI-i%m4LD$n{WIwWR|c+NRy`C1#)1sSBI7FiH6z-QkhY&Q_|%I3exQ zQ`X1M?cZH4^M&BSyr;2z$+^SZUMA*0001Z+HKHROw(}?!13=vX`$@Br+fGR zZ%e`5O6%Txi$Yrz0gF{}p>fY>OnlS0Uevf}oDXW;D{d2gcE<2)oFcV80@g$H)63L{HN*d{8kVzKVW(;E)$9N_%kx5Ku3R9WJbY?JW^G#k0Wdx>E$NBBVtKRLiL?sA*s%w`TdsNz1=+~FRNdB8&+@iBD0 zXFTC4C-8-Cwv(4U=LLQ~^Oa4^rG|OTr5?ItoaPMYxxh`%a*kVU z;HYGAjq6;IY{`*awo0DlOMw(hkrYdb(O28l;MYvSx*ChcQW4f^QL5UdE3HbqvbxB$pfSg`>Cj#;?~00;nMAg}==M6d%RaIhCe zARtS)01i=0um)3FSgr#ump{<1pq_<0a34Kp8x=7I1^|9 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..6bbc3cf58cb011a6b4bf3cb1612ce212608f7274 GIT binary patch literal 19836 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..e231183dce4c7b452afc9e7799586fd285e146f4 GIT binary patch literal 22660 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 ML;wL%uV=LY00kR;TmS$7 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Semibold-webfont.eot b/docs/fonts/OpenSans-Semibold-webfont.eot new file mode 100755 index 0000000000000000000000000000000000000000..d8375dd0ab130207f023358d62ef6ff357108b7f GIT binary patch literal 20028 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 h1{iKYDIA5bqxNz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Semibold-webfont.ttf b/docs/fonts/OpenSans-Semibold-webfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..b3290843a7a3e621867b169c8487de9a8c7a8054 GIT binary patch literal 39476 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 F{{Vm_4C4R* literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-Semibold-webfont.woff b/docs/fonts/OpenSans-Semibold-webfont.woff new file mode 100755 index 0000000000000000000000000000000000000000..28d6adee03b8d2301e06680fce413eb94887b57e GIT binary patch literal 22908 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 G2><}8D24q1 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.eot b/docs/fonts/OpenSans-SemiboldItalic-webfont.eot new file mode 100755 index 0000000000000000000000000000000000000000..0ab1db22e69ec331510563590527277ba5d68cc1 GIT binary patch literal 20962 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.svg b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg new file mode 100755 index 0000000..7166ec1 --- /dev/null +++ b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf b/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf new file mode 100755 index 0000000000000000000000000000000000000000..d2d6318f6640448517db133a40a6a5a36d36ed48 GIT binary patch literal 40252 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 Z0p^q`UCg`8t*mL+fyY_#j+!w<{{z_tFYy2X literal 0 HcmV?d00001 diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.woff b/docs/fonts/OpenSans-SemiboldItalic-webfont.woff new file mode 100755 index 0000000000000000000000000000000000000000..d4dfca402e53e59ec985ff4c6312864da493fa83 GIT binary patch literal 23764 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 + + + + + Global - Documentation + + + + + + + + + + + +