From 73217b9c97f617a5b7400af19f965a9f1083742e Mon Sep 17 00:00:00 2001 From: SwingTheVine Date: Sat, 26 Jul 2025 01:55:47 -0400 Subject: [PATCH] Completed Pin Icon functionality --- dist/BlueMarble.user.css | 2 +- dist/BlueMarble.user.js | 6 +++--- docs/README.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/BlueMarble.meta.js | 2 +- src/apiHandler.js | 28 +++++++++++++++++++++++++++- src/coordsHandler.js | 17 +++++++++++++++++ src/main.js | 4 +++- src/overlay.css | 5 +++-- src/overlay.js | 13 ++++++------- 11 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 src/coordsHandler.js diff --git a/dist/BlueMarble.user.css b/dist/BlueMarble.user.css index b1ed01c..6813d3f 100644 --- a/dist/BlueMarble.user.css +++ b/dist/BlueMarble.user.css @@ -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,') 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.25em;width:1.25em;margin-top:2px;text-align:center;line-height:1.25em;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]{-moz-appearance:textfield;width:4.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{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-input-file)>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-userinfo,#bm-contain-automation,#bm-contain-coords,#bm-contain-buttons,div:has(>#bm-input-file),#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{background-color:#1061e5}#bm-overlay button:active,#bm-overlay button:disabled{background-color:#2e97ff}#bm-overlay button:disabled{text-decoration:line-through} +#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,') 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.25em;width:1.25em;margin-top:2px;text-align:center;line-height:1.25em;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{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-input-file)>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-userinfo,#bm-contain-automation,#bm-contain-coords,#bm-contain-buttons,div:has(>#bm-input-file),#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} diff --git a/dist/BlueMarble.user.js b/dist/BlueMarble.user.js index ae5ed59..05cd6d8 100644 --- a/dist/BlueMarble.user.js +++ b/dist/BlueMarble.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Blue Marble // @namespace https://github.com/SwingTheVine/ -// @version 0.42.0 +// @version 0.42.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 @@ -20,5 +20,5 @@ // Wplace --> https://wplace.live // License --> https://www.mozilla.org/en-US/MPL/2.0/ -(()=>{var g=class{constructor(e,o){this.name=e,this.version=o,this.apiHandler=null,this.outputStatusId="bm-output-status"}setApiHandler(e){this.apiHandler=e}create(){let e=document.createElement("div");e.id="bm-overlay",e.style.top="10px",e.style.right="75px";let o=document.createElement("div");o.id="bm-contain-header";let t=document.createElement("div");t.id="bm-bar-drag",o.appendChild(t);let n=document.createElement("img");n.src="https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/src/assets/Favicon.png",n.alt="Blue Marble Icon",o.appendChild(n);let i=document.createElement("h1");i.textContent=this.name,o.appendChild(i);let s=document.createElement("div");s.id="bm-contain-userinfo";let a=document.createElement("p");a.id="bm-user-name",a.textContent="Username:",s.appendChild(a);let r=document.createElement("p");r.id="bm-user-droplets",r.textContent="Droplets:",s.appendChild(r);let c=document.createElement("p");c.id="bm-user-nextlevel",c.textContent="Next level in...",s.appendChild(c);let l=document.createElement("div");l.id="bm-contain-automation",l.appendChild(this.createInputCheckbox("Stealth","bm-input-stealth",!0)),l.appendChild(this.createButtonQuestion("bm-help-stealth","Help: Waits for the website to make requests, instead of sending requests.",this.outputStatusId)),l.appendChild(document.createElement("br")),l.appendChild(this.createInputCheckbox("Possessed","bm-input-possessed",!0)),l.appendChild(this.createButtonQuestion("bm-help-possessed","Help: Controls the website as if it were possessed.",this.outputStatusId)),l.appendChild(document.createElement("br"));let d=document.createElement("div");d.id="bm-contain-coords";let u=document.createElement("button");u.id="bm-button-coords",u.className="bm-help",u.style="margin-top: 0",u.innerHTML='',u.onclick=()=>{let m=this.apiHandler?.coordsTilePixel;if(!m?.[0]){this.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");return}this.updateInnerHTML("bm-input-tx",m?.[0]||"000"),this.updateInnerHTML("bm-input-ty",m?.[1]||"000"),this.updateInnerHTML("bm-input-px",m?.[2]||"000"),this.updateInnerHTML("bm-input-py",m?.[3]||"000")},d.appendChild(u),d.appendChild(this.createInputNumber("bm-input-tx","Tl X","","3","0","999","1")),d.appendChild(this.createInputNumber("bm-input-ty","Tl Y","","3","0","999","1")),d.appendChild(this.createInputNumber("bm-input-px","Px X","","3","0","999","1")),d.appendChild(this.createInputNumber("bm-input-py","Px Y","","3","0","999","1")),l.appendChild(d),l.appendChild(this.createInputFile("bm-input-file"));let b=document.createElement("div");b.id="bm-contain-buttons",b.appendChild(this.createButton("bm-button-enable","Enable")),b.appendChild(this.createButton("bm-button-disable","Disable")),l.appendChild(b);let f=document.createElement("textarea");f.id=this.outputStatusId,f.readOnly=!0,f.placeholder=`Status: Sleeping... -Version: ${this.version}`,l.appendChild(f),e.appendChild(o),e.appendChild(document.createElement("hr")),e.appendChild(s),e.appendChild(document.createElement("hr")),e.appendChild(l),document.body.appendChild(e),this.handleDrag(e,t)}updateInnerHTML(e,o,t=!1){let n=document.getElementById(e);if(n){if(n instanceof HTMLInputElement){n.value=o;return}t?n.textContent=o:n.innerHTML=o}}createButtonQuestion(e,o,t){let n=this.createButton(e,"?");return n.className="bm-help",n.title=o,n.onclick=()=>{this.updateInnerHTML(t,o)},n}createButton(e,o,t=!0){let n=document.createElement("button");return n.id=e,n.textContent=o,n.disabled=!t,n}createInputText(e,o="",t="",n="",i=!1){let s=document.createElement("input");return s.id=e,s.type="text",s.placeholder=o,s.value=t,s.readOnly=i,s.maxLength=n,s}createInputNumber(e,o="",t="",n="",i="",s="",a="",r=!1){let c=this.createInputText(e,o,t,n,r);return c.type="number",c.min=i,c.max=s,c.step=a,c}createInputCheckbox(e,o,t=!1){let n=document.createElement("label");n.textContent=e;let i=document.createElement("input");return i.type="checkbox",i.id=o,i.checked=t,n.prepend(i),n}createInputFile(e){let o=document.createElement("div"),t=document.createElement("input");t.id=e,t.type="file",t.style="display: none";let n=document.createElement("button");return n.textContent="Upload File",n.addEventListener("click",()=>{t.click()}),t.addEventListener("change",()=>{n.style.maxWidth=`${n.offsetWidth}px`,t.files.length>0?n.textContent=t.files[0].name:n.textContent="Upload File"}),o.appendChild(t),o.appendChild(n),o}handleDrag(e,o){let t=!1,n,i=0;o.addEventListener("mousedown",function(s){t=!0,n=s.clientX-e.getBoundingClientRect().left,i=s.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",o.classList.add("dragging")}),o.addEventListener("touchstart",function(s){t=!0;let a=s?.touches?.[0];a&&(n=a.clientX-e.getBoundingClientRect().left,i=a.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",o.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(s){t&&(e.style.left=s.clientX-n+"px",e.style.top=s.clientY-i+"px",e.style.right="")}),document.addEventListener("touchmove",function(s){if(t){let a=s?.touches?.[0];if(!a)return;e.style.left=a.clientX-n+"px",e.style.top=a.clientY-i+"px",s.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchend",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")})}handleDisplayError(e){console.error(`${this.name}: ${e}`),this.updateInnerHTML(this.outputStatusId,"Error: "+e,!0)}};var x=class{constructor(){this.disableAll=!1,this.coordsTilePixel=[]}spontaneousResponseListener(e){window.addEventListener("message",o=>{let t=o.data;if(!(t&&t.source==="blue-marble"))return;let n=t.endpoint.split("?")[0].split("/").filter(i=>i&&isNaN(Number(i))).pop();switch(console.log(`Recieved message about "${n}"`),n){case"me":let i=Math.ceil(Math.pow(Math.floor(t.jsonData?.level)*Math.pow(30,.65),1.5384615384615383)-t.jsonData?.pixelsPainted);e.updateInnerHTML("bm-user-name",`Username: ${t.jsonData?.name}`),e.updateInnerHTML("bm-user-droplets",`Droplets: ${new Intl.NumberFormat().format(t.jsonData?.droplets)}`),e.updateInnerHTML("bm-user-nextlevel",`Next level in ${new Intl.NumberFormat().format(i)} pixel${i==1?"":"s"}`);break;case"pixel":let s=t.endpoint.split("?")[0].split("/").filter(c=>c&&!isNaN(Number(c))),a=new URLSearchParams(t.endpoint.split("?")[1]),r=[a.get("x"),a.get("y")];this.coordsTilePixel=[...s,...r];break;case"robots":this.disableAll=t.jsonData?.userscript?.toString().toLowerCase()=="false";break}})}};var y=GM_info.script.name.toString(),E=GM_info.script.version.toString();function L(p){let e=document.createElement("script");e.textContent=`(${p})();`,document.documentElement.appendChild(e),e.remove()}L(()=>{let p=window.fetch;window.fetch=async function(...e){let o=await p.apply(this,e),t=o.clone();if((t.headers.get("content-type")||"").includes("application/json")){let i=(e[0]instanceof Request?e[0]?.url:e[0])||"ignore";console.log(`Sending JSON message about endpoint "${i}"`),t.json().then(s=>{window.postMessage({source:"blue-marble",endpoint:i,jsonData:s},"*")}).catch(s=>{console.error("BM - Failed to parse JSON:",s)})}return o}});var I=GM_getResourceText("CSS-BM-File");GM_addStyle(I);var h=document.createElement("link");h.href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap";h.rel="preload";h.as="style";h.onload="this.onload=null;this.rel='stylesheet'";document.head.appendChild(h);var C=new g(y,E),v=new x;C.setApiHandler(v);C.create();v.spontaneousResponseListener(C);console.log(`${y} (${E}) userscript has loaded!`);})(); +(()=>{var x=class{constructor(e,o){this.name=e,this.version=o,this.apiHandler=null,this.outputStatusId="bm-output-status"}setApiHandler(e){this.apiHandler=e}create(){let e=document.createElement("div");e.id="bm-overlay",e.style.top="10px",e.style.right="75px";let o=document.createElement("div");o.id="bm-contain-header";let t=document.createElement("div");t.id="bm-bar-drag",o.appendChild(t);let n=document.createElement("img");n.src="https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/src/assets/Favicon.png",n.alt="Blue Marble Icon",o.appendChild(n);let i=document.createElement("h1");i.textContent=this.name,o.appendChild(i);let s=document.createElement("div");s.id="bm-contain-userinfo";let a=document.createElement("p");a.id="bm-user-name",a.textContent="Username:",s.appendChild(a);let r=document.createElement("p");r.id="bm-user-droplets",r.textContent="Droplets:",s.appendChild(r);let m=document.createElement("p");m.id="bm-user-nextlevel",m.textContent="Next level in...",s.appendChild(m);let l=document.createElement("div");l.id="bm-contain-automation",l.appendChild(this.createInputCheckbox("Stealth","bm-input-stealth",!0)),l.appendChild(this.createButtonQuestion("bm-help-stealth","Help: Waits for the website to make requests, instead of sending requests.",this.outputStatusId)),l.appendChild(document.createElement("br")),l.appendChild(this.createInputCheckbox("Possessed","bm-input-possessed",!0)),l.appendChild(this.createButtonQuestion("bm-help-possessed","Help: Controls the website as if it were possessed.",this.outputStatusId)),l.appendChild(document.createElement("br"));let d=document.createElement("div");d.id="bm-contain-coords";let c=document.createElement("button");c.id="bm-button-coords",c.className="bm-help",c.style="margin-top: 0",c.innerHTML='',c.onclick=()=>{let h=this.apiHandler?.coordsTilePixel;if(!h?.[0]){this.handleDisplayError("Coordinates are malformed! Did you try clicking on the canvas first?");return}this.updateInnerHTML("bm-input-tx",h?.[0]||"0000"),this.updateInnerHTML("bm-input-ty",h?.[1]||"0000"),this.updateInnerHTML("bm-input-px",h?.[2]||"000"),this.updateInnerHTML("bm-input-py",h?.[3]||"000")},d.appendChild(c),d.appendChild(this.createInputNumber("bm-input-tx","Tl X","","4","0","2047","1")),d.appendChild(this.createInputNumber("bm-input-ty","Tl Y","","4","0","2047","1")),d.appendChild(this.createInputNumber("bm-input-px","Px X","","3","0","999","1")),d.appendChild(this.createInputNumber("bm-input-py","Px Y","","3","0","999","1")),l.appendChild(d),l.appendChild(this.createInputFile("bm-input-file"));let p=document.createElement("div");p.id="bm-contain-buttons",p.appendChild(this.createButton("bm-button-enable","Enable")),p.appendChild(this.createButton("bm-button-disable","Disable")),l.appendChild(p);let f=document.createElement("textarea");f.id=this.outputStatusId,f.readOnly=!0,f.placeholder=`Status: Sleeping... +Version: ${this.version}`,l.appendChild(f),e.appendChild(o),e.appendChild(document.createElement("hr")),e.appendChild(s),e.appendChild(document.createElement("hr")),e.appendChild(l),document.body.appendChild(e),this.handleDrag(e,t)}updateInnerHTML(e,o,t=!1){let n=document.getElementById(e);if(n){if(n instanceof HTMLInputElement){n.value=o;return}t?n.textContent=o:n.innerHTML=o}}createButtonQuestion(e,o,t){let n=this.createButton(e,"?");return n.className="bm-help",n.title=o,n.onclick=()=>{this.updateInnerHTML(t,o)},n}createButton(e,o,t=!0){let n=document.createElement("button");return n.id=e,n.textContent=o,n.disabled=!t,n}createInputText(e,o="",t="",n="",i=!1){let s=document.createElement("input");return s.id=e,s.type="text",s.placeholder=o,s.value=t,s.readOnly=i,s.maxLength=n,s}createInputNumber(e,o="",t="",n="",i="",s="",a=!1){let r=this.createInputText(e,o,t,"",a);return r.type="number",r.min=n,r.max=i,r.step=s,r}createInputCheckbox(e,o,t=!1){let n=document.createElement("label");n.textContent=e;let i=document.createElement("input");return i.type="checkbox",i.id=o,i.checked=t,n.prepend(i),n}createInputFile(e){let o=document.createElement("div"),t=document.createElement("input");t.id=e,t.type="file",t.style="display: none";let n=document.createElement("button");return n.textContent="Upload File",n.addEventListener("click",()=>{t.click()}),t.addEventListener("change",()=>{n.style.maxWidth=`${n.offsetWidth}px`,t.files.length>0?n.textContent=t.files[0].name:n.textContent="Upload File"}),o.appendChild(t),o.appendChild(n),o}handleDrag(e,o){let t=!1,n,i=0;o.addEventListener("mousedown",function(s){t=!0,n=s.clientX-e.getBoundingClientRect().left,i=s.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",o.classList.add("dragging")}),o.addEventListener("touchstart",function(s){t=!0;let a=s?.touches?.[0];a&&(n=a.clientX-e.getBoundingClientRect().left,i=a.clientY-e.getBoundingClientRect().top,document.body.style.userSelect="none",o.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(s){t&&(e.style.left=s.clientX-n+"px",e.style.top=s.clientY-i+"px",e.style.right="")}),document.addEventListener("touchmove",function(s){if(t){let a=s?.touches?.[0];if(!a)return;e.style.left=a.clientX-n+"px",e.style.top=a.clientY-i+"px",s.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchend",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){t=!1,document.body.style.userSelect="",o.classList.remove("dragging")})}handleDisplayError(e){console.error(`${this.name}: ${e}`),this.updateInnerHTML(this.outputStatusId,"Error: "+e,!0)}};var g=class{serverTPtoDisplayTP(e,o){return[parseInt(e[0])%4*1e3+parseInt(o[0]),parseInt(e[1])%4*1e3+parseInt(o[1])]}};var C=class{constructor(e){this.coordsHandler=e,this.disableAll=!1,this.coordsTilePixel=[]}spontaneousResponseListener(e){window.addEventListener("message",o=>{let t=o.data;if(!(t&&t.source==="blue-marble"))return;let n=t.endpoint.split("?")[0].split("/").filter(i=>i&&isNaN(Number(i))).pop();switch(console.log(`Recieved message about "${n}"`),n){case"me":let i=Math.ceil(Math.pow(Math.floor(t.jsonData?.level)*Math.pow(30,.65),1.5384615384615383)-t.jsonData?.pixelsPainted);e.updateInnerHTML("bm-user-name",`Username: ${t.jsonData?.name}`),e.updateInnerHTML("bm-user-droplets",`Droplets: ${new Intl.NumberFormat().format(t.jsonData?.droplets)}`),e.updateInnerHTML("bm-user-nextlevel",`Next level in ${new Intl.NumberFormat().format(i)} pixel${i==1?"":"s"}`);break;case"pixel":let s=t.endpoint.split("?")[0].split("/").filter(d=>d&&!isNaN(Number(d))),a=new URLSearchParams(t.endpoint.split("?")[1]),r=[a.get("x"),a.get("y")];this.coordsTilePixel=[...s,...r];let m=this.coordsHandler.serverTPtoDisplayTP(s,r),l=document.querySelectorAll("span");for(let d of l)if(d.textContent.trim().includes(`${m[0]}, ${m[1]}`)){let c=document.querySelector("#bm-display-coords"),p=`(Tl X: ${s[0]}, Tl Y: ${s[1]}, Px X: ${r[0]}, Px Y: ${r[1]})`;c?c.textContent=p:(c=document.createElement("span"),c.id="bm-display-coords",c.textContent=p,c.style="margin-left: calc(var(--spacing)*3);",d.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",c))}break;case"robots":this.disableAll=t.jsonData?.userscript?.toString().toLowerCase()=="false";break}})}};var E=GM_info.script.name.toString(),v=GM_info.script.version.toString();function L(u){let e=document.createElement("script");e.textContent=`(${u})();`,document.documentElement.appendChild(e),e.remove()}L(()=>{let u=window.fetch;window.fetch=async function(...e){let o=await u.apply(this,e),t=o.clone();if((t.headers.get("content-type")||"").includes("application/json")){let i=(e[0]instanceof Request?e[0]?.url:e[0])||"ignore";console.log(`Sending JSON message about endpoint "${i}"`),t.json().then(s=>{window.postMessage({source:"blue-marble",endpoint:i,jsonData:s},"*")}).catch(s=>{console.error("BM - Failed to parse JSON:",s)})}return o}});var w=GM_getResourceText("CSS-BM-File");GM_addStyle(w);var b=document.createElement("link");b.href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap";b.rel="preload";b.as="style";b.onload="this.onload=null;this.rel='stylesheet'";document.head.appendChild(b);var y=new x(E,v),T=new g,I=new C(T);y.setApiHandler(I);y.create();I.spontaneousResponseListener(y);console.log(`${E} (${v}) userscript has loaded!`);})(); diff --git a/docs/README.md b/docs/README.md index d450acc..e8ae66e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -35,7 +35,7 @@ Software License: MPL-2.0 Contact Me WakaTime -Total Patches +Total Patches Total Lines of Code Total Comments Build diff --git a/package-lock.json b/package-lock.json index b9e5c24..24cd166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "devDependencies": { "esbuild": "^0.25.0" }, - "version": "0.41.11" + "version": "0.42.16" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.8", @@ -467,5 +467,5 @@ } } }, - "version": "0.41.11" + "version": "0.42.16" } diff --git a/package.json b/package.json index f57dba3..9901d36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wplace-bluemarble", - "version": "0.42.0", + "version": "0.42.16", "type": "module", "scripts": { "build": "node build/build.js", diff --git a/src/BlueMarble.meta.js b/src/BlueMarble.meta.js index 3f17717..84cd422 100644 --- a/src/BlueMarble.meta.js +++ b/src/BlueMarble.meta.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Blue Marble // @namespace https://github.com/SwingTheVine/ -// @version 0.42.0 +// @version 0.42.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 diff --git a/src/apiHandler.js b/src/apiHandler.js index 270c5ec..c8cd98b 100644 --- a/src/apiHandler.js +++ b/src/apiHandler.js @@ -5,9 +5,11 @@ export class ApiHandler { /** Constructor for ApiHandler class + * @param {CoordsHandler} coordsHandler - The CoordsHandler instance * @since 0.11.34 */ - constructor() { + constructor(coordsHandler) { + this.coordsHandler = coordsHandler; this.disableAll = false; // Should the entire userscript be disabled? this.coordsTilePixel = []; // Contains the last detected tile/pixel coordinate pair requested } @@ -50,6 +52,30 @@ export class ApiHandler { 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 this.coordsTilePixel = [...coordsTile, ...coordsPixel]; // Combines the two arrays such that [x, y, x, y] + const displayTP = this.coordsHandler.serverTPtoDisplayTP(coordsTile, coordsPixel); + + 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) { + if (element.textContent.trim().includes(`${displayTP[0]}, ${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]})`; + + // 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.textContent = text; + displayCoords.style = 'margin-left: calc(var(--spacing)*3);'; + element.parentNode.parentNode.parentNode.insertAdjacentElement('afterend', displayCoords); + } else { + displayCoords.textContent = text; + } + } + } break; case 'robots': // Request to retrieve what script types are allowed diff --git a/src/coordsHandler.js b/src/coordsHandler.js new file mode 100644 index 0000000..48d3ae8 --- /dev/null +++ b/src/coordsHandler.js @@ -0,0 +1,17 @@ +/** Handles translation of coordinate systems. + * @since 0.42.4 + */ +export class CoordsHandler { + + /** Converts the server tile-pixel coordinate system to the displayed tile-pixel coordinate system. + * @param {string[]} tile - The tile to convert (as an array like ["12", "124"]) + * @param {string[]} pixel - The pixel to convert (as an array like ["12", "124"]) + * @returns {number[]} [tile, pixel] + * @since 0.42.4 + * @example + * console.log(serverTPtoDisplayTP(['12', '123'], ['34', '567'])); // [34, 3567] + */ + serverTPtoDisplayTP(tile, pixel) { + return [((parseInt(tile[0]) % 4) * 1000) + parseInt(pixel[0]), ((parseInt(tile[1]) % 4) * 1000) + parseInt(pixel[1])]; + } +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 2ff752f..42139e0 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,5 @@ import { Overlay } from './overlay.js'; +import { CoordsHandler } from './coordsHandler.js'; import { ApiHandler } from './apiHandler.js'; const name = GM_info.script.name.toString(); @@ -72,7 +73,8 @@ stylesheetLink.onload = "this.onload=null;this.rel='stylesheet'"; document.head.appendChild(stylesheetLink); const overlay = new Overlay(name, version); // Constructs a new Overlay object -const apiHandler = new ApiHandler(); // Constructs a new ApiHandler object +const coordsHandler = new CoordsHandler(); // Constructs a new CoordsHandler object +const apiHandler = new ApiHandler(coordsHandler); // Constructs a new ApiHandler object overlay.setApiHandler(apiHandler); // Sets the API handler diff --git a/src/overlay.css b/src/overlay.css index 6c210db..7348a21 100644 --- a/src/overlay.css +++ b/src/overlay.css @@ -95,8 +95,9 @@ div#bm-overlay { /* Tile (x, y) & Pixel (x, y) input fields */ #bm-contain-coords input[type="number"] { + appearance: auto; -moz-appearance: textfield; - width: 4.5ch; + width: 5.5ch; margin-left: 1ch; background-color: rgba(0, 0, 0, 0.2); padding: 0 0.5ch; @@ -156,7 +157,7 @@ div:has(> #bm-input-file), } /* All overlay buttons when hovered/focused */ -#bm-overlay button:hover, #bm-overlay button:focus { +#bm-overlay button:hover, #bm-overlay button:focus-visible { background-color: #1061e5; } diff --git a/src/overlay.js b/src/overlay.js index 6be63f4..a54870f 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -116,16 +116,16 @@ export class Overlay { this.handleDisplayError('Coordinates are malformed! Did you try clicking on the canvas first?'); return; } - this.updateInnerHTML('bm-input-tx', coords?.[0] || '000'); - this.updateInnerHTML('bm-input-ty', coords?.[1] || '000'); + this.updateInnerHTML('bm-input-tx', coords?.[0] || '0000'); + this.updateInnerHTML('bm-input-ty', coords?.[1] || '0000'); this.updateInnerHTML('bm-input-px', coords?.[2] || '000'); this.updateInnerHTML('bm-input-py', coords?.[3] || '000'); } containerAutomationCoords.appendChild(buttonCoords); // Adds the coordinate button to the automation container // Tile (x,y) and Pixel (x,y) input fields - containerAutomationCoords.appendChild(this.createInputNumber('bm-input-tx', 'Tl X', '', '3', '0', '999', '1')); - containerAutomationCoords.appendChild(this.createInputNumber('bm-input-ty', 'Tl Y', '', '3', '0', '999', '1')); + containerAutomationCoords.appendChild(this.createInputNumber('bm-input-tx', 'Tl X', '', '4', '0', '2047', '1')); + containerAutomationCoords.appendChild(this.createInputNumber('bm-input-ty', 'Tl Y', '', '4', '0', '2047', '1')); containerAutomationCoords.appendChild(this.createInputNumber('bm-input-px', 'Px X', '', '3', '0', '999', '1')); containerAutomationCoords.appendChild(this.createInputNumber('bm-input-py', 'Px Y', '', '3', '0', '999', '1')); @@ -243,7 +243,6 @@ export class Overlay { * @param {string} inputId - The ID for the number input * @param {string} [placeholder] - (Optional) The placeholder text of the input * @param {string} [text] - (Optional) The text content of the input - * @param {string} [maxLength] - (Optional) The maximum character amount of the input * @param {string} [numMin] - (Optional) The minimum possible number the user can input * @param {string} [numMax] - (Optional) The maximum possible number the user can input * @param {string} [numStep] - (Optional) The increment that numbers are considered valid inputs. E.g. a step of "1" will only allow integers @@ -251,8 +250,8 @@ export class Overlay { * @returns {HTMLInputElement} HTML Input Element * @since 0.41.9 */ - createInputNumber(inputId, placeholder='', text='', maxLength='', numMin='', numMax='', numStep='', isReadOnly=false) { - const input = this.createInputText(inputId, placeholder, text, maxLength, isReadOnly); // A shortcut :P + createInputNumber(inputId, placeholder='', text='', numMin='', numMax='', numStep='', isReadOnly=false) { + const input = this.createInputText(inputId, placeholder, text, '', isReadOnly); // A shortcut :P input.type = 'number'; input.min = numMin; input.max = numMax;