mirror of
https://github.com/SwingTheVine/Wplace-BlueMarble.git
synced 2026-03-11 17:15:38 +00:00
Finished CSS selector obfuscator
This commit is contained in:
parent
3918bf839e
commit
97ba324457
10 changed files with 186 additions and 14 deletions
|
|
@ -4,6 +4,7 @@
|
|||
* 2. Bundle the JS files into one file (esbuild)
|
||||
* 3. Bundle the CSS files into one file (esbuild)
|
||||
* 4. Compress & obfuscate the bundled JS file (terner)
|
||||
* 5. Runs the CSS selector mangler (cssMandler.js)
|
||||
* @since 0.0.6
|
||||
*/
|
||||
|
||||
|
|
@ -11,6 +12,7 @@
|
|||
import esbuild from 'esbuild';
|
||||
import fs from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
import mangleSelectors from './cssMangler.js';
|
||||
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
|
@ -57,7 +59,7 @@ const resultEsbuild = await esbuild.build({
|
|||
write: false, // Should we write the outfile to the disk?
|
||||
}).catch(() => process.exit(1));
|
||||
|
||||
// Retrieves the JS file and map file
|
||||
// Retrieves the JS file
|
||||
const resultEsbuildJS = resultEsbuild.outputFiles.find(file => file.path.endsWith('.js'));
|
||||
|
||||
// Obfuscates the JS file
|
||||
|
|
@ -84,5 +86,15 @@ let resultTerser = await terser.minify(resultEsbuildJS.text, {
|
|||
}
|
||||
});
|
||||
|
||||
// Writes the obfuscated/mangled JS code to a file
|
||||
fs.writeFileSync('dist/BlueMarble.user.js', resultTerser.code, 'utf8');
|
||||
|
||||
// Mangles the CSS selectors
|
||||
mangleSelectors('bm-', 'bm-', 'dist/BlueMarble.user.js', 'dist/BlueMarble.user.css');
|
||||
|
||||
// Adds the banner
|
||||
fs.writeFileSync('dist/BlueMarble.user.js', metaContent + resultTerser.code, 'utf8');
|
||||
fs.writeFileSync(
|
||||
'dist/BlueMarble.user.js',
|
||||
metaContent + fs.readFileSync('dist/BlueMarble.user.js', 'utf8'),
|
||||
'utf8'
|
||||
);
|
||||
|
|
|
|||
141
build/cssMangler.js
Normal file
141
build/cssMangler.js
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/** Mangles all matching CSS selectors provided:
|
||||
* - The CSS selector starts with the correct prefix
|
||||
* - The prefix case matches (case-sensitive)
|
||||
* - There is 1 (bundled) CSS file
|
||||
* - There is 1 (bundled) JS file
|
||||
* The default mangling is base64, as small as possible
|
||||
* @since 0.56.1
|
||||
* @example
|
||||
* // (Assume 'bm-' is the input prefix, and 'b-' is the output prefix)
|
||||
* // Input:
|
||||
* // JS
|
||||
* const element = docuement.createElement('p');
|
||||
* element.id = 'bm-paragraph-id';
|
||||
* element.className = 'bm-paragraph-class';
|
||||
* // CSS
|
||||
* #bm-paragraph-id {color:red;}
|
||||
* .bm-paragraph-class {background-color:blue;}
|
||||
*
|
||||
* // Output:
|
||||
* // JS
|
||||
* const element = docuement.createElement('p');
|
||||
* element.id = 'b-1'; // The longer the selector, the smaller it gets...
|
||||
* element.className = 'b-0'; // ...therefore, the class selector is "0"
|
||||
* // CSS
|
||||
* #b-1 {color:red;}
|
||||
* .b-0 {background-color:blue;}
|
||||
* // Optional returned map Object:
|
||||
* console.log(JSON.stringify(mangleSelectors('bm-', 'b-', 'bundled.js', 'bundled.css', true), null, 2));
|
||||
* {
|
||||
* "bm-paragraph-class": "b-0",
|
||||
* "bm-paragraph-id": "b-1",
|
||||
* }
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
|
||||
/** Mangles the CSS selectors in a JS and CSS file.
|
||||
* Both the JS and CSS file are needed to ensure the names are synced.
|
||||
* A prefix is needed on all selectors to ensure the proper matching.
|
||||
* The prefix is case-sensitive.
|
||||
* The default mangling is all valid single byte characters for CSS selectors (which is, ironically, 64 characters).
|
||||
* You can optionally return the key-value mapping of all selector names.
|
||||
* @param {string} inputPrefix - The prefix to search for.
|
||||
* @param {string} outputPrefix - The prefix to replace with.
|
||||
* @param {string} pathJS - The path to the JS file.
|
||||
* @param {string} pathCSS - The path to the CSS file.
|
||||
* @param {boolean} [returnMap=false] - Should this function return the key-value map Object?
|
||||
* @param {string} [encoding=''] - The characters you want the mangled selectors to consist of.
|
||||
* @returns {Object<string, string>|undefined} A mapping of the mangled CSS selectors as an Object, or `undefined` if `returnMap` is not `true`.
|
||||
* @since 0.56.1
|
||||
* @example
|
||||
* // (Assume 'bm-' is the input prefix, and 'b-' is the output prefix)
|
||||
* // Input:
|
||||
* // JS
|
||||
* const element = docuement.createElement('p');
|
||||
* element.id = 'bm-paragraph-id';
|
||||
* element.className = 'bm-paragraph-class';
|
||||
* // CSS
|
||||
* #bm-paragraph-id {color:red;}
|
||||
* .bm-paragraph-class {background-color:blue;}
|
||||
*
|
||||
* // Output:
|
||||
* // JS
|
||||
* const element = docuement.createElement('p');
|
||||
* element.id = 'b-1'; // The longer the selector, the smaller it gets...
|
||||
* element.className = 'b-0'; // ...therefore, the class selector is "0"
|
||||
* // CSS
|
||||
* #b-1 {color:red;}
|
||||
* .b-0 {background-color:blue;}
|
||||
* // Optional returned map Object:
|
||||
* console.log(JSON.stringify(mangleSelectors('bm-', 'b-', 'bundled.js', 'bundled.css', true), null, 2));
|
||||
* {
|
||||
* "bm-paragraph-class": "b-0",
|
||||
* "bm-paragraph-id": "b-1",
|
||||
* }
|
||||
*/
|
||||
export default function mangleSelectors(inputPrefix, outputPrefix, pathJS, pathCSS, returnMap=false, encoding='') {
|
||||
|
||||
encoding = encoding || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'; // Default encoding
|
||||
|
||||
const fileInputJS = fs.readFileSync(pathJS, 'utf8'); // The JS file
|
||||
const fileInputCSS = fs.readFileSync(pathCSS, 'utf8'); // The CSS file
|
||||
|
||||
// One of each of all matching selectors
|
||||
// File -> RegEx -> Array (Duplicates) -> Set (Unique) -> Array (Unique)
|
||||
let matchedSelectors = [...new Set([...fileInputJS.matchAll(new RegExp(`\\b${escapeRegex(inputPrefix)}[a-zA-Z0-9_-]+`, 'g'))].map(match => match[0]))];
|
||||
|
||||
// Sort keys in selector from longest to shortest
|
||||
// This will avoid partial matches, which could cause bugs
|
||||
// E.g. `foo-foobar` will match before `foo-foo` matches
|
||||
matchedSelectors.sort((a, b) => b.length - a.length);
|
||||
|
||||
// Converts the string[] to an Object (key-value)
|
||||
matchedSelectors = Object.fromEntries(matchedSelectors.map((key, value) => [key, outputPrefix + numberToEncoded(matchedSelectors.indexOf(key), encoding)]));
|
||||
|
||||
// Compile the RegEx from the selector map
|
||||
const regex = new RegExp(Object.keys(matchedSelectors).map(selector => escapeRegex(selector)).join('|'), 'g');
|
||||
|
||||
// Replaces the CSS selectors in both files with encoded versions
|
||||
fs.writeFileSync(pathJS, fileInputJS.replace(regex, match => matchedSelectors[match]), 'utf8');
|
||||
fs.writeFileSync(pathCSS, fileInputCSS.replace(regex, match => matchedSelectors[match]), 'utf8');
|
||||
|
||||
if (!!returnMap) {return matchedSelectors;} // Return the map Object optionally
|
||||
}
|
||||
|
||||
/** Escapes characters in a string that are about to be inserted into a Regular Expression
|
||||
* @param {string} string - The string to pass in
|
||||
* @returns {string} String that has been escaped for RegEx
|
||||
* @since 0.56.2
|
||||
*/
|
||||
function escapeRegex(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
/** 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.56.12
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
}
|
||||
2
dist/BlueMarble.user.css
vendored
2
dist/BlueMarble.user.css
vendored
|
|
@ -1 +1 @@
|
|||
#bm-overlay{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000}div#bm-overlay{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-bar-drag{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-bar-drag.dragging{cursor:grabbing}#bm-contain-header{margin-bottom:.5em}#bm-overlay img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}#bm-overlay h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-contain-automation input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-contain-automation label{margin-right:.5ch}.bm-help{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-button-coords{vertical-align:middle}#bm-button-coords svg{width:50%;margin:0 auto;fill:#111}#bm-contain-coords input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-contain-coords input[type=number]::-webkit-outer-spin-button,#bm-contain-coords input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-contain-buttons-template{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-input-file-template)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-output-status{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-contain-buttons-action{display:flex;justify-content:space-between}#bm-overlay small{font-size:x-small;color:#d3d3d3}#bm-contain-userinfo,#bm-contain-automation,#bm-contain-coords,#bm-contain-buttons-template,div:has(>#bm-input-file-template),#bm-output-status{margin-top:.5em}#bm-overlay button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-overlay button:hover,#bm-overlay button:focus-visible{background-color:#1061e5}#bm-overlay button:active,#bm-overlay button:disabled{background-color:#2e97ff}#bm-overlay button:disabled{text-decoration:line-through}
|
||||
#bm-p{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000}div#bm-p{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-k{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-k.dragging{cursor:grabbing}#bm-b{margin-bottom:.5em}#bm-p img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle}#bm-p h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-3 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-3 label{margin-right:.5ch}.bm-q{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-h{vertical-align:middle}#bm-h svg{width:50%;margin:0 auto;fill:#111}div#bm-7{display:flex;gap:.5ch}#bm-8 svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-c input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-c input[type=number]::-webkit-outer-spin-button,#bm-c input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-0{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-2)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-e{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-1{display:flex;justify-content:space-between}#bm-p small{font-size:x-small;color:#d3d3d3}#bm-4,#bm-3,#bm-c,#bm-0,div:has(>#bm-2),#bm-e{margin-top:.5em}#bm-p button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-p button:hover,#bm-p button:focus-visible{background-color:#1061e5}#bm-p button:active,#bm-p button:disabled{background-color:#2e97ff}#bm-p button:disabled{text-decoration:line-through}
|
||||
|
|
|
|||
4
dist/BlueMarble.user.js
vendored
4
dist/BlueMarble.user.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -35,7 +35,7 @@
|
|||
<a href="https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/LICENSE.txt" target="_blank"><img alt="Software License: MPL-2.0" src="https://img.shields.io/badge/Software_License-MPL--2.0-brightgreen?style=flat"></a>
|
||||
<a href="https://discord.gg/tpeBPy46hf" target="_blank"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
|
||||
<a href="" target="_blank"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-35hrs_30mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
|
||||
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-211-black?style=flat"></a>
|
||||
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-227-black?style=flat"></a>
|
||||
<a href="" target="_blank"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
|
||||
<a href="" target="_blank"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
|
||||
<a href="" target="_blank"><img alt="Compression" src="https://img.shields.io/badge/Compression-68.88%25-blue"></a>
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.55.11",
|
||||
"version": "0.56.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.55.11",
|
||||
"version": "0.56.16",
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"terser": "^5.43.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.56.0",
|
||||
"version": "0.56.16",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node build/build.js",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name Blue Marble
|
||||
// @namespace https://github.com/SwingTheVine/
|
||||
// @version 0.56.0
|
||||
// @version 0.56.16
|
||||
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
|
||||
// @author SwingTheVine
|
||||
// @license MPL-2.0
|
||||
|
|
|
|||
|
|
@ -147,10 +147,11 @@ overlay.addDiv({'id': 'bm-overlay', 'style': 'top: 10px; right: 75px;'})
|
|||
.addTextarea({'id': overlay.outputStatusId, 'placeholder': `Status: Sleeping...\nVersion: ${version}`, 'readOnly': true}).buildElement()
|
||||
.addDiv({'id': 'bm-contain-buttons-action'})
|
||||
.addDiv()
|
||||
.addButton({'id': 'bm-button-teleport', 'className': 'bm-help', 'textContent': '🕴'}).buildElement()
|
||||
.addButton({'id': 'bm-button-favorite', 'className': 'bm-help', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="10,2 12,7.5 18,7.5 13.5,11.5 15.5,18 10,14 4.5,18 6.5,11.5 2,7.5 8,7.5" fill="gold" stroke="black" stroke-width="1.4"></polygon></svg>'}).buildElement()
|
||||
.addButton({'id': 'bm-button-templates', 'className': 'bm-help', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect width="20" height="20" stroke-width="3" stroke="black" fill="none"></rect><circle cx="10" cy="8" r="4"></circle><circle cx="10" cy="18" r="7"></circle></svg>'}).buildElement()
|
||||
.addSmall({'textContent': 'Made by SwingTheVine'}).buildElement()
|
||||
.addButton({'id': 'bm-button-teleport', 'className': 'bm-help', 'textContent': '✈'}).buildElement()
|
||||
.addButton({'id': 'bm-button-favorite', 'className': 'bm-help', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="10,2 12,7.5 18,7.5 13.5,11.5 15.5,18 10,14 4.5,18 6.5,11.5 2,7.5 8,7.5" fill="white"></polygon></svg>'}).buildElement()
|
||||
.addButton({'id': 'bm-button-templates', 'className': 'bm-help', 'innerHTML': '🖌'}).buildElement()
|
||||
.buildElement()
|
||||
.addSmall({'textContent': 'Made by SwingTheVine', 'style': 'margin-top: auto;'}).buildElement()
|
||||
.buildElement()
|
||||
.buildElement()
|
||||
.buildOverlay(document.body);
|
||||
|
|
|
|||
|
|
@ -93,6 +93,24 @@ div#bm-overlay {
|
|||
fill: #111;
|
||||
}
|
||||
|
||||
/* Container for action buttons, that is inside the action button container */
|
||||
div#bm-button-teleport {
|
||||
display: flex;
|
||||
gap: 0.5ch;
|
||||
}
|
||||
|
||||
/* Favorite (Star) button image */
|
||||
/* Templates (Person) button image */
|
||||
#bm-button-favorite svg,
|
||||
#bm-button-template svg {
|
||||
height: 1em;
|
||||
margin: 0 auto;
|
||||
margin-top: 2px;
|
||||
text-align: center;
|
||||
line-height: 1em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/* Tile (x, y) & Pixel (x, y) input fields */
|
||||
#bm-contain-coords input[type="number"] {
|
||||
appearance: auto;
|
||||
|
|
|
|||
Loading…
Reference in a new issue