diff --git a/dist/BlueMarble.user.js b/dist/BlueMarble.user.js index a030827..a8ec140 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.66.0 +// @version 0.66.2 // @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,4 +20,4 @@ // Wplace --> https://wplace.live // License --> https://www.mozilla.org/en-US/MPL/2.0/ -(()=>{var t,e,i=t=>{throw TypeError(t)},n=(t,e,n)=>e.has(t)?i("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,n),s=(t,e,n)=>(((t,e)=>{e.has(t)||i("Cannot access private method")})(t,e),n);t=new WeakSet,e=function(t,e={},i={}){const n=document.createElement(t);this.t?(this.i.appendChild(n),this.o.push(this.i),this.i=n):(this.t=n,this.i=n);for(const[t,i]of Object.entries(e))n[t]=i;for(const[t,e]of Object.entries(i))n[t]=e;return n};var o,a=class{constructor({displayName:t="My template",h:e=0,l:i="",url:n="",file:s=null,coords:o=null,u:a=null,m:r=1e3}={}){this.displayName=t,this.h=e,this.l=i,this.url=n,this.file=s,this.coords=o,this.u=a,this.m=r}async p(){const t=await createImageBitmap(this.file),e=t.width,i=t.height,n={},s=new OffscreenCanvas(this.m,this.m),o=s.getContext("2d",{v:!0});for(let a=this.coords[3];a0;)i=e[t%n]+i,t=Math.floor(t/n);return i}o=new WeakSet;var c=GM_info.script.name.toString(),h=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-n",c),e.setAttribute("bm-l","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=(t?.getAttribute("bm-n"),t?.getAttribute("bm-l"),new Map);window.addEventListener("message",t=>{const{source:i,endpoint:n,blobID:s,blobData:o,blink:a}=t.data;Date.now(),"blue-marble"==i&&s&&o&&!n&&e.get(s)(o)});const i=window.fetch;window.fetch=async function(...t){const n=await i.apply(this,t),s=n.clone(),o=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",a=s.headers.get("content-type")||"";if(a.includes("application/json"))s.json().then(t=>{window.postMessage({source:"blue-marble",endpoint:o,jsonData:t},"*")}).catch(t=>{});else if(a.includes("image/")&&!o.includes("openfreemap")){const t=Date.now(),i=await s.blob();return new Promise(n=>{const a=crypto.randomUUID();e.set(a,t=>{n(new Response(t,{headers:s.headers,status:s.status,statusText:s.statusText})),e.delete(a)}),window.postMessage({source:"blue-marble",endpoint:o,blobID:a,blobData:i,blink:t})}).catch(t=>{Date.now()})}return n}});var l=GM_getResourceText("CSS-BM-File");GM_addStyle(l);var u=document.createElement("link");u.href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap",u.rel="preload",u.as="style",u.onload=function(){this.onload=null,this.rel="stylesheet"},document.head.appendChild(u),new class{constructor(){this.$=null,this.M=null,this.C="#bm-5"}D(t){return this.M=t,this.$=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.C)}),this}T(){return this.$}observe(t,e=!1,i=!1){t.observe(this.M,{childList:e,subtree:i})}};var d=new class{constructor(e,i){n(this,t),this.name=e,this.version=i,this.I=null,this.k="bm-a",this.t=null,this.i=null,this.o=[]}N(t){this.I=t}S(){return this.o.length>0&&(this.i=this.o.pop()),this}B(t){t.appendChild(this.t),this.t=null,this.i=null,this.o=[]}O(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"div",{},i)),this}H(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"p",{},i)),this}L(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"small",{},i)),this}P(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"img",{},i)),this}R(i,n={},o=()=>{}){return o(this,s(this,t,e).call(this,"h"+i,{},n)),this}_(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"hr",{},i)),this}j(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"br",{},i)),this}q(i={},n=()=>{}){const o=s(this,t,e).call(this,"label",{textContent:i.textContent??""});delete i.textContent;const a=s(this,t,e).call(this,"input",{type:"checkbox"},i);return o.insertBefore(a,o.firstChild),this.S(),n(this,o,a),this}V(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"button",{},i)),this}Y(i={},n=()=>{}){const o=i.title??i.textContent??"Help: No info";delete i.textContent,i.title=`Help: ${o}`;const a={textContent:"?",className:"bm-o",onclick:()=>{this.F(this.k,o)}};return n(this,s(this,t,e).call(this,"button",a,i)),this}G(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"input",{},i)),this}U(i={},n=()=>{}){const o=i.textContent??"";delete i.textContent;const a=s(this,t,e).call(this,"div"),r=s(this,t,e).call(this,"input",{type:"file",style:"display: none;"},i);this.S();const c=s(this,t,e).call(this,"button",{textContent:o});return this.S(),this.S(),c.addEventListener("click",()=>{r.click()}),r.addEventListener("change",()=>{c.style.maxWidth=`${c.offsetWidth}px`,r.files.length>0?c.textContent=r.files[0].name:c.textContent=o}),n(this,a,r,c),this}W(i={},n=()=>{}){return n(this,s(this,t,e).call(this,"textarea",{},i)),this}F(t,e,i=!1){const n=document.getElementById(t.replace(/^#/,""));n&&(n instanceof HTMLInputElement?n.value=e:i?n.textContent=e:n.innerHTML=e)}X(t,e){let i,n=!1,s=0;t=document.querySelector("#"==t?.[0]?t:"#"+t),e=document.querySelector("#"==e?.[0]?e:"#"+e),t&&e?(e.addEventListener("mousedown",function(o){n=!0,i=o.clientX-t.getBoundingClientRect().left,s=o.clientY-t.getBoundingClientRect().top,document.body.style.userSelect="none",e.classList.add("dragging")}),e.addEventListener("touchstart",function(o){n=!0;const a=o?.touches?.[0];a&&(i=a.clientX-t.getBoundingClientRect().left,s=a.clientY-t.getBoundingClientRect().top,document.body.style.userSelect="none",e.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(e){n&&(t.style.left=e.clientX-i+"px",t.style.top=e.clientY-s+"px",t.style.right="")}),document.addEventListener("touchmove",function(e){if(n){const n=e?.touches?.[0];if(!n)return;t.style.left=n.clientX-i+"px",t.style.top=n.clientY-s+"px",e.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){n=!1,document.body.style.userSelect="",e.classList.remove("dragging")}),document.addEventListener("touchend",function(){n=!1,document.body.style.userSelect="",e.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){n=!1,document.body.style.userSelect="",e.classList.remove("dragging")})):this.J(`Can not drag! ${t?"":"moveMe"} ${t||e?"":"and "}${e?"":"iMoveThings "}was not found!`)}A(t){(0,console.info)(`${this.name}: ${t}`),this.F(this.k,"Status: "+t,!0)}J(t){(0,console.error)(`${this.name}: ${t}`),this.F(this.k,"Error: "+t,!0)}}(c,h),m=new class{constructor(t,e,i){n(this,o),this.name=t,this.version=e,this.t=i,this.Z="1.0.0",this.K=null,this.tt="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.m=1e3,this.et=3,this.it=null,this.nt=null,this.st="bm-m",this.ot="div#map canvas.maplibregl-canvas",this.rt=null,this.ct="",this.ht=[],this.lt=null}ut(){if(document.body.contains(this.it))return this.it;document.getElementById(this.st)?.remove();const t=document.querySelector(this.ot),e=document.createElement("canvas");return e.id=this.st,e.className="maplibregl-canvas",e.style.position="absolute",e.style.top="0",e.style.left="0",e.style.height=t?.clientHeight*(window.devicePixelRatio||1)+"px",e.style.width=t?.clientWidth*(window.devicePixelRatio||1)+"px",e.height=t?.clientHeight*(window.devicePixelRatio||1),e.width=t?.clientWidth*(window.devicePixelRatio||1),e.style.zIndex="8999",e.style.pointerEvents="none",t?.parentElement?.appendChild(e),this.it=e,window.addEventListener("move",this.dt),window.addEventListener("zoom",this.bt),window.addEventListener("resize",this.ft),this.it}async wt(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.Z,templates:{}}}async gt(t,e,i){this.lt||(this.lt=await this.wt()),this.t.A(`Creating template at ${i.join(", ")}...`);const n=new a({displayName:e,h:0,l:r(this.K||0,this.tt),file:t,coords:i});n.u=await n.p(this.m),this.lt.templates[`${n.h} ${n.l}`]={name:n.displayName,enabled:!0,tiles:n.u},this.ht=[],this.ht.push(n),this.t.A(`Template created at ${i.join(", ")}!`)}yt(){}async vt(){this.lt||(this.lt=await this.wt())}async xt(t,e){const i=this.m*this.et;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0");const n=this.ht;n.sort((t,e)=>t.h-e.h);const s=n.map(t=>{const i=Object.keys(t.u).filter(t=>t.startsWith(e));if(0===i.length)return null;const n=i.map(e=>t.u[e]);return n?.[0]}).filter(Boolean);s.length>0&&this.t.A(`Displaying ${s.length} template${1==s.length?"":"s"}.`);const o=await createImageBitmap(t),a=new OffscreenCanvas(i,i),r=a.getContext("2d");r.imageSmoothingEnabled=!1,r.beginPath(),r.rect(0,0,i,i),r.clip(),r.clearRect(0,0,i,i),r.drawImage(o,0,0,i,i);for(const t of s)r.drawImage(t,e[0]*this.et,e[1]*this.et);return await a.convertToBlob({type:"image/png"})}$t(){}}(c,h,d),p=new class{constructor(t){this.Mt=t,this.Ct=!1,this.Dt=[],this.Tt=[]}It(t){window.addEventListener("message",async e=>{const i=e.data,n=i.jsonData;if(!i||"blue-marble"!==i.source)return;if(!i.endpoint)return;const s=i.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(s){case"me":if(n.status&&"2"!=n.status?.toString()[0])return void t.J("You are not logged in!\nCould not fetch userdata.");const e=Math.ceil(Math.pow(Math.floor(n.level)*Math.pow(30,.65),1/.65)-n.pixelsPainted);n.id||n.id,this.Mt.K=n.id,t.F("bm-e",`Username: ${function(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}(n.name)}`),t.F("bm-b",`Droplets: ${(new Intl.NumberFormat).format(n.droplets)}`),t.F("bm-6",`Next level in ${(new Intl.NumberFormat).format(e)} pixel${1==e?"":"s"}`);break;case"pixel":const s=i.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),r=new URLSearchParams(i.endpoint.split("?")[1]),c=[r.get("x"),r.get("y")];if(this.Dt.length&&(!s.length||!c.length))return void t.J("Coordinates are malformed!\nDid you try clicking the canvas first?");this.Dt=[...s,...c];const h=(o=s,a=c,[parseInt(o[0])%4*1e3+parseInt(a[0]),parseInt(o[1])%4*1e3+parseInt(a[1])]),l=document.querySelectorAll("span");for(const t of l)if(t.textContent.trim().includes(`${h[0]}, ${h[1]}`)){let e=document.querySelector("#bm-5");const i=`(Tl X: ${s[0]}, Tl Y: ${s[1]}, Px X: ${c[0]}, Px Y: ${c[1]})`;e?e.textContent=i:(e=document.createElement("span"),e.id="bm-5",e.textContent=i,e.style="margin-left: calc(var(--spacing)*3); font-size: small;",t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",e))}break;case"tiles":let u=i.endpoint.split("/");u=[parseInt(u[u.length-2]),parseInt(u[u.length-1].replace(".png",""))];const d=i.blobID,m=i.blobData,p=await this.Mt.xt(m,u);window.postMessage({source:"blue-marble",blobID:d,blobData:p,blink:i.blink});break;case"robots":this.Ct="false"==n.userscript?.toString().toLowerCase()}var o,a})}}(m);d.N(p),d.O({id:"bm-k",style:"top: 10px; right: 75px;"}).O({id:"bm-7"}).O({id:"bm-f"}).S().P({alt:"Blue Marble Icon",src:"https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png"}).S().R(1,{textContent:c}).S().S()._().S().O({id:"bm-4"}).H({id:"bm-e",textContent:"Username:"}).S().H({id:"bm-b",textContent:"Droplets:"}).S().H({id:"bm-6",textContent:"Next level in..."}).S().S()._().S().O({id:"bm-3"}).O({id:"bm-8"}).V({id:"bm-c",className:"bm-o",style:"margin-top: 0;",innerHTML:''},(t,e)=>{e.onclick=()=>{const e=t.I?.Dt;e?.[0]?(t.F("bm-g",e?.[0]||""),t.F("bm-h",e?.[1]||""),t.F("bm-i",e?.[2]||""),t.F("bm-j",e?.[3]||"")):t.J("Coordinates are malformed! Did you try clicking on the canvas first?")}}).S().G({type:"number",id:"bm-g",placeholder:"Tl X",min:0,max:2047,step:1,required:!0}).S().G({type:"number",id:"bm-h",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0}).S().G({type:"number",id:"bm-i",placeholder:"Px X",min:0,max:2047,step:1,required:!0}).S().G({type:"number",id:"bm-j",placeholder:"Px Y",min:0,max:2047,step:1,required:!0}).S().S().U({id:"bm-2",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).S().O({id:"bm-0"}).V({id:"bm-d",textContent:"Enable"},(t,e)=>{e.onclick=()=>{const e=document.querySelector("#bm-2"),i=document.querySelector("#bm-g");if(!i.checkValidity())return i.reportValidity(),void t.J("Coordinates are malformed! Did you try clicking on the canvas first?");const n=document.querySelector("#bm-h");if(!n.checkValidity())return n.reportValidity(),void t.J("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-i");if(!s.checkValidity())return s.reportValidity(),void t.J("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-j");if(!o.checkValidity())return o.reportValidity(),void t.J("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(m.gt(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(i.value),Number(n.value),Number(s.value),Number(o.value)]),t.A("Drew to canvas!")):t.J("No file selected!")}}).S().S().W({id:d.k,placeholder:`Status: Sleeping...\nVersion: ${h}`,readOnly:!0}).S().O({id:"bm-1"}).O().V({id:"bm-9",className:"bm-o",innerHTML:"🎨",title:"Template Color Converter"},(t,e)=>{e.addEventListener("click",()=>{window.open("https://pepoafonso.github.io/color_converter_wplace/","_blank","noopener noreferrer")})}).S().S().L({textContent:"Made by SwingTheVine",style:"margin-top: auto;"}).S().S().S().B(document.body),d.X("#bm-k","#bm-f"),p.It(d),function(...t){(0,console.log)(...t)}(`%c${c}%c (${h}) userscript has loaded!`,"color: cornflowerblue;","")})(); \ No newline at end of file +(()=>{var t,e,n=t=>{throw TypeError(t)},s=(t,e,s)=>e.has(t)?n("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,s),i=(t,e,s)=>(((t,e)=>{e.has(t)||n("Cannot access private method")})(t,e),s);t=new WeakSet,e=function(t,e={},n={}){const s=document.createElement(t);this.t?(this.i.appendChild(s),this.o.push(this.i),this.i=s):(this.t=s,this.i=s);for(const[t,n]of Object.entries(e))s[t]=n;for(const[t,e]of Object.entries(n))s[t]=e;return s};var o,a=class{constructor({displayName:t="My template",l:e=0,h:n="",url:s="",file:i=null,coords:o=null,u:a=null,m:r=1e3}={}){this.displayName=t,this.l=e,this.h=n,this.url=s,this.file=i,this.coords=o,this.u=a,this.m=r}async p(){console.log(this.coords);const t=await createImageBitmap(this.file),e=t.width,n=t.height,s={},i=new OffscreenCanvas(this.m,this.m),o=i.getContext("2d",{$:!0});for(let a=this.coords[3];a0;)n=e[t%s]+n,t=Math.floor(t/s);return n}o=new WeakSet;var c=GM_info.script.name.toString(),l=GM_info.script.version.toString();!function(t){const e=document.createElement("script");e.setAttribute("bm-n",c),e.setAttribute("bm-l","color: cornflowerblue;"),e.textContent=`(${t})();`,document.documentElement.appendChild(e),e.remove()}(()=>{const t=document.currentScript,e=t?.getAttribute("bm-n")||"Blue Marble",n=t?.getAttribute("bm-l")||"",s=new Map;window.addEventListener("message",t=>{const{source:i,endpoint:o,blobID:a,blobData:r,blink:c}=t.data,l=Date.now()-c;if(console.groupCollapsed(`%c${e}%c: ${s.size} Recieved IMAGE message about blob "${a}"`,n,""),console.log(`Blob fetch took %c${String(Math.floor(l/6e4)).padStart(2,"0")}:${String(Math.floor(l/1e3)%60).padStart(2,"0")}.${String(l%1e3).padStart(3,"0")}%c MM:SS.mmm`,n,""),console.log(s),console.groupEnd(),"blue-marble"==i&&a&&r&&!o){const t=s.get(a);"function"==typeof t?t(r):function(...t){(0,console.warn)(...t)}(`%c${e}%c: Attempted to retrieve a blob (%s) from queue, but the blobID was not a function! Skipping...`,n,"",a),s.delete(a)}});const i=window.fetch;window.fetch=async function(...t){const o=await i.apply(this,t),a=o.clone(),r=(t[0]instanceof Request?t[0]?.url:t[0])||"ignore",c=a.headers.get("content-type")||"";if(c.includes("application/json"))console.log(`%c${e}%c: Sending JSON message about endpoint "${r}"`,n,""),a.json().then(t=>{window.postMessage({source:"blue-marble",endpoint:r,jsonData:t},"*")}).catch(t=>{console.error(`%c${e}%c: Failed to parse JSON: `,n,"",t)});else if(c.includes("image/")&&!r.includes("openfreemap")){const t=Date.now(),i=await a.blob();return console.log(`%c${e}%c: ${s.size} Sending IMAGE message about endpoint "${r}"`,n,""),new Promise(o=>{const c=crypto.randomUUID();s.set(c,t=>{o(new Response(t,{headers:a.headers,status:a.status,statusText:a.statusText})),console.log(`%c${e}%c: ${s.size} Processed blob "${c}"`,n,"")}),window.postMessage({source:"blue-marble",endpoint:r,blobID:c,blobData:i,blink:t})}).catch(i=>{const o=Date.now();console.error(`%c${e}%c: Failed to Promise blob!`,n,""),console.groupCollapsed(`%c${e}%c: Details of failed blob Promise:`,n,""),console.log(`Endpoint: ${r}\nThere are ${s.size} blobs processing...\nBlink: ${t.toLocaleString()}\nTime Since Blink: ${String(Math.floor(o/6e4)).padStart(2,"0")}:${String(Math.floor(o/1e3)%60).padStart(2,"0")}.${String(o%1e3).padStart(3,"0")} MM:SS.mmm`),console.error("Exception stack:",i),console.groupEnd()})}return o}});var h=GM_getResourceText("CSS-BM-File");GM_addStyle(h);var u=document.createElement("link");u.href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap",u.rel="preload",u.as="style",u.onload=function(){this.onload=null,this.rel="stylesheet"},document.head.appendChild(u),new class{constructor(){this.v=null,this.M=null,this.S="#bm-5"}D(t){return this.M=t,this.v=new MutationObserver(t=>{for(const e of t)for(const t of e.addedNodes)t instanceof HTMLElement&&t.matches?.(this.S)}),this}C(){return this.v}observe(t,e=!1,n=!1){t.observe(this.M,{childList:e,subtree:n})}};var d=new class{constructor(e,n){s(this,t),this.name=e,this.version=n,this.T=null,this.k="bm-a",this.t=null,this.i=null,this.o=[]}I(t){this.T=t}N(){return this.o.length>0&&(this.i=this.o.pop()),this}B(t){t.appendChild(this.t),this.t=null,this.i=null,this.o=[]}O(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"div",{},n)),this}P(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"p",{},n)),this}H(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"small",{},n)),this}L(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"img",{},n)),this}R(n,s={},o=()=>{}){return o(this,i(this,t,e).call(this,"h"+n,{},s)),this}Y(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"hr",{},n)),this}G(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"br",{},n)),this}X(n={},s=()=>{}){const o=i(this,t,e).call(this,"label",{textContent:n.textContent??""});delete n.textContent;const a=i(this,t,e).call(this,"input",{type:"checkbox"},n);return o.insertBefore(a,o.firstChild),this.N(),s(this,o,a),this}j(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"button",{},n)),this}q(n={},s=()=>{}){const o=n.title??n.textContent??"Help: No info";delete n.textContent,n.title=`Help: ${o}`;const a={textContent:"?",className:"bm-o",onclick:()=>{this._(this.k,o)}};return s(this,i(this,t,e).call(this,"button",a,n)),this}F(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"input",{},n)),this}J(n={},s=()=>{}){const o=n.textContent??"";delete n.textContent;const a=i(this,t,e).call(this,"div"),r=i(this,t,e).call(this,"input",{type:"file",style:"display: none;"},n);this.N();const c=i(this,t,e).call(this,"button",{textContent:o});return this.N(),this.N(),c.addEventListener("click",()=>{r.click()}),r.addEventListener("change",()=>{c.style.maxWidth=`${c.offsetWidth}px`,r.files.length>0?c.textContent=r.files[0].name:c.textContent=o}),s(this,a,r,c),this}V(n={},s=()=>{}){return s(this,i(this,t,e).call(this,"textarea",{},n)),this}_(t,e,n=!1){const s=document.getElementById(t.replace(/^#/,""));s&&(s instanceof HTMLInputElement?s.value=e:n?s.textContent=e:s.innerHTML=e)}W(t,e){let n,s=!1,i=0;t=document.querySelector("#"==t?.[0]?t:"#"+t),e=document.querySelector("#"==e?.[0]?e:"#"+e),t&&e?(e.addEventListener("mousedown",function(o){s=!0,n=o.clientX-t.getBoundingClientRect().left,i=o.clientY-t.getBoundingClientRect().top,document.body.style.userSelect="none",e.classList.add("dragging")}),e.addEventListener("touchstart",function(o){s=!0;const a=o?.touches?.[0];a&&(n=a.clientX-t.getBoundingClientRect().left,i=a.clientY-t.getBoundingClientRect().top,document.body.style.userSelect="none",e.classList.add("dragging"))},{passive:!1}),document.addEventListener("mousemove",function(e){s&&(t.style.left=e.clientX-n+"px",t.style.top=e.clientY-i+"px",t.style.right="")}),document.addEventListener("touchmove",function(e){if(s){const s=e?.touches?.[0];if(!s)return;t.style.left=s.clientX-n+"px",t.style.top=s.clientY-i+"px",e.preventDefault()}},{passive:!1}),document.addEventListener("mouseup",function(){s=!1,document.body.style.userSelect="",e.classList.remove("dragging")}),document.addEventListener("touchend",function(){s=!1,document.body.style.userSelect="",e.classList.remove("dragging")}),document.addEventListener("touchcancel",function(){s=!1,document.body.style.userSelect="",e.classList.remove("dragging")})):this.A(`Can not drag! ${t?"":"moveMe"} ${t||e?"":"and "}${e?"":"iMoveThings "}was not found!`)}U(t){(0,console.info)(`${this.name}: ${t}`),this._(this.k,"Status: "+t,!0)}A(t){(0,console.error)(`${this.name}: ${t}`),this._(this.k,"Error: "+t,!0)}}(c,l),m=new class{constructor(t,e,n){s(this,o),this.name=t,this.version=e,this.t=n,this.Z="1.0.0",this.K=null,this.tt="!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",this.m=1e3,this.et=3,this.nt=null,this.st=null,this.it="bm-m",this.ot="div#map canvas.maplibregl-canvas",this.rt=null,this.ct="",this.lt=[],this.ht=null}ut(){if(document.body.contains(this.nt))return this.nt;document.getElementById(this.it)?.remove();const t=document.querySelector(this.ot),e=document.createElement("canvas");return e.id=this.it,e.className="maplibregl-canvas",e.style.position="absolute",e.style.top="0",e.style.left="0",e.style.height=t?.clientHeight*(window.devicePixelRatio||1)+"px",e.style.width=t?.clientWidth*(window.devicePixelRatio||1)+"px",e.height=t?.clientHeight*(window.devicePixelRatio||1),e.width=t?.clientWidth*(window.devicePixelRatio||1),e.style.zIndex="8999",e.style.pointerEvents="none",t?.parentElement?.appendChild(e),this.nt=e,window.addEventListener("move",this.dt),window.addEventListener("zoom",this.bt),window.addEventListener("resize",this.ft),this.nt}async gt(){return{whoami:this.name.replace(" ",""),scriptVersion:this.version,schemaVersion:this.Z,templates:{}}}async $t(t,e,n){this.ht||(this.ht=await this.gt(),console.log("Creating JSON...")),this.t.U(`Creating template at ${n.join(", ")}...`);const s=new a({displayName:e,l:0,h:r(this.K||0,this.tt),file:t,coords:n});s.u=await s.p(this.m),this.ht.templates[`${s.l} ${s.h}`]={name:s.displayName,enabled:!0,tiles:s.u},this.lt=[],this.lt.push(s),this.t.U(`Template created at ${n.join(", ")}!`),console.log(Object.keys(this.ht.templates).length),console.log(this.ht),console.log(this.lt)}wt(){}async yt(){this.ht||(this.ht=await this.gt(),console.log("Creating JSON..."))}async vt(t,e){const n=this.m*this.et;e=e[0].toString().padStart(4,"0")+","+e[1].toString().padStart(4,"0"),console.log(`Looking for "${e}"`);const s=this.lt;s.sort((t,e)=>t.l-e.l),console.log(s);const i=s.map(t=>{const n=Object.keys(t.u).filter(t=>t.startsWith(e));if(0===n.length)return null;const s=n.map(e=>t.u[e]);return s?.[0]}).filter(Boolean);console.log(i),i.length>0&&this.t.U(`Displaying ${i.length} template${1==i.length?"":"s"}.`);const o=await createImageBitmap(t),a=new OffscreenCanvas(n,n),r=a.getContext("2d");r.imageSmoothingEnabled=!1,r.beginPath(),r.rect(0,0,n,n),r.clip(),r.clearRect(0,0,n,n),r.drawImage(o,0,0,n,n);for(const t of i)console.log("Template Blob is "+typeof t),console.log(t),r.drawImage(t,e[0]*this.et,e[1]*this.et);return await a.convertToBlob({type:"image/png"})}xt(){}}(c,l,d),p=new class{constructor(t){this.Mt=t,this.St=!1,this.Dt=[],this.Ct=[]}Tt(t){window.addEventListener("message",async e=>{const n=e.data,s=n.jsonData;if(!n||"blue-marble"!==n.source)return;if(!n.endpoint)return;const i=n.endpoint?.split("?")[0].split("/").filter(t=>t&&isNaN(Number(t))).filter(t=>t&&!t.includes(".")).pop();switch(console.log('%cBlue Marble%c: Recieved message about "%s"',"color: cornflowerblue;","",i),i){case"me":if(s.status&&"2"!=s.status?.toString()[0])return void t.A("You are not logged in!\nCould not fetch userdata.");const e=Math.ceil(Math.pow(Math.floor(s.level)*Math.pow(30,.65),1/.65)-s.pixelsPainted);console.log(s.id),(s.id||0===s.id)&&console.log(r(s.id,"!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")),this.Mt.K=s.id,t._("bm-e",`Username: ${function(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML}(s.name)}`),t._("bm-b",`Droplets: ${(new Intl.NumberFormat).format(s.droplets)}`),t._("bm-6",`Next level in ${(new Intl.NumberFormat).format(e)} pixel${1==e?"":"s"}`);break;case"pixel":const i=n.endpoint.split("?")[0].split("/").filter(t=>t&&!isNaN(Number(t))),c=new URLSearchParams(n.endpoint.split("?")[1]),l=[c.get("x"),c.get("y")];if(this.Dt.length&&(!i.length||!l.length))return void t.A("Coordinates are malformed!\nDid you try clicking the canvas first?");this.Dt=[...i,...l];const h=(o=i,a=l,[parseInt(o[0])%4*1e3+parseInt(a[0]),parseInt(o[1])%4*1e3+parseInt(a[1])]),u=document.querySelectorAll("span");for(const t of u)if(t.textContent.trim().includes(`${h[0]}, ${h[1]}`)){let e=document.querySelector("#bm-5");const n=`(Tl X: ${i[0]}, Tl Y: ${i[1]}, Px X: ${l[0]}, Px Y: ${l[1]})`;e?e.textContent=n:(e=document.createElement("span"),e.id="bm-5",e.textContent=n,e.style="margin-left: calc(var(--spacing)*3); font-size: small;",t.parentNode.parentNode.parentNode.insertAdjacentElement("afterend",e))}break;case"tiles":let d=n.endpoint.split("/");d=[parseInt(d[d.length-2]),parseInt(d[d.length-1].replace(".png",""))];const m=n.blobID,p=n.blobData,b=await this.Mt.vt(p,d);window.postMessage({source:"blue-marble",blobID:m,blobData:b,blink:n.blink});break;case"robots":this.St="false"==s.userscript?.toString().toLowerCase();break}var o,a})}}(m);d.I(p),d.O({id:"bm-k",style:"top: 10px; right: 75px;"}).O({id:"bm-7"}).O({id:"bm-f"}).N().L({alt:"Blue Marble Icon",src:"https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/main/dist/assets/Favicon.png"}).N().R(1,{textContent:c}).N().N().Y().N().O({id:"bm-4"}).P({id:"bm-e",textContent:"Username:"}).N().P({id:"bm-b",textContent:"Droplets:"}).N().P({id:"bm-6",textContent:"Next level in..."}).N().N().Y().N().O({id:"bm-3"}).O({id:"bm-8"}).j({id:"bm-c",className:"bm-o",style:"margin-top: 0;",innerHTML:''},(t,e)=>{e.onclick=()=>{const e=t.T?.Dt;e?.[0]?(t._("bm-g",e?.[0]||""),t._("bm-h",e?.[1]||""),t._("bm-i",e?.[2]||""),t._("bm-j",e?.[3]||"")):t.A("Coordinates are malformed! Did you try clicking on the canvas first?")}}).N().F({type:"number",id:"bm-g",placeholder:"Tl X",min:0,max:2047,step:1,required:!0}).N().F({type:"number",id:"bm-h",placeholder:"Tl Y",min:0,max:2047,step:1,required:!0}).N().F({type:"number",id:"bm-i",placeholder:"Px X",min:0,max:2047,step:1,required:!0}).N().F({type:"number",id:"bm-j",placeholder:"Px Y",min:0,max:2047,step:1,required:!0}).N().N().J({id:"bm-2",textContent:"Upload Template",accept:"image/png, image/jpeg, image/webp, image/bmp, image/gif"}).N().O({id:"bm-0"}).j({id:"bm-d",textContent:"Enable"},(t,e)=>{e.onclick=()=>{const e=document.querySelector("#bm-2"),n=document.querySelector("#bm-g");if(!n.checkValidity())return n.reportValidity(),void t.A("Coordinates are malformed! Did you try clicking on the canvas first?");const s=document.querySelector("#bm-h");if(!s.checkValidity())return s.reportValidity(),void t.A("Coordinates are malformed! Did you try clicking on the canvas first?");const i=document.querySelector("#bm-i");if(!i.checkValidity())return i.reportValidity(),void t.A("Coordinates are malformed! Did you try clicking on the canvas first?");const o=document.querySelector("#bm-j");if(!o.checkValidity())return o.reportValidity(),void t.A("Coordinates are malformed! Did you try clicking on the canvas first?");e?.files[0]?(m.$t(e.files[0],e.files[0]?.name.replace(/\.[^/.]+$/,""),[Number(n.value),Number(s.value),Number(i.value),Number(o.value)]),t.U("Drew to canvas!")):t.A("No file selected!")}}).N().N().V({id:d.k,placeholder:`Status: Sleeping...\nVersion: ${l}`,readOnly:!0}).N().O({id:"bm-1"}).O().j({id:"bm-9",className:"bm-o",innerHTML:"🎨",title:"Template Color Converter"},(t,e)=>{e.addEventListener("click",()=>{window.open("https://pepoafonso.github.io/color_converter_wplace/","_blank","noopener noreferrer")})}).N().N().H({textContent:"Made by SwingTheVine",style:"margin-top: auto;"}).N().N().N().B(document.body),d.W("#bm-k","#bm-f"),p.Tt(d),function(...t){(0,console.log)(...t)}(`%c${c}%c (${l}) userscript has loaded!`,"color: cornflowerblue;","")})(); \ No newline at end of file diff --git a/docs/CREDITS.md b/docs/CREDITS.md index fa09127..2c5f875 100644 --- a/docs/CREDITS.md +++ b/docs/CREDITS.md @@ -24,4 +24,6 @@ The favicon "Blue Marble" is owned by NASA Special Thanks: * nof, [darkness](https://github.com/TouchedByDarkness) for creating similar userscripts! * [BullStein](https://github.com/BullStein), [allanf181](https://github.com/allanf181) for being early beta testers! +* guidu_ for the "Minimize" Button code! +* Nomad for the tutorial! * TheBlueCorner for getting me interested in online pixel canvases! \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index bd8c14c..28a93bf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,10 +32,10 @@

Blue Marble

Latest Version Latest Release -Software License: MPL-2.0 +Software License: MPL-2.0 Contact Me -WakaTime -Total Patches +WakaTime +Total Patches Total Lines of Code Total Comments Compression @@ -47,7 +47,8 @@ Welcome to Blue Marble! Blue Marble is a userscript for the website wplace.live. If you like this userscript, please ⭐ the repository!

Installation Instructions

- Supported Browsers + Supported Browsers + Unupported Browsers

Blue Marble has been verified to work on mobile devices. Blue Marble was designed on Chrome, but Blue Marble might work on "unsupported" browsers not listed above.
diff --git a/package-lock.json b/package-lock.json index 1a7d968..4a8b56b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wplace-bluemarble", - "version": "0.65.80", + "version": "0.66.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wplace-bluemarble", - "version": "0.65.80", + "version": "0.66.2", "devDependencies": { "esbuild": "^0.25.0", "terser": "^5.43.1" diff --git a/package.json b/package.json index 10aca8a..18c7a54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wplace-bluemarble", - "version": "0.66.0", + "version": "0.66.2", "type": "module", "scripts": { "build": "node build/build.js", diff --git a/src/BlueMarble.meta.js b/src/BlueMarble.meta.js index 46ef4e1..a52b94e 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.66.0 +// @version 0.66.2 // @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/apiManager.js b/src/apiManager.js index 3e7528b..1dbd9b9 100644 --- a/src/apiManager.js +++ b/src/apiManager.js @@ -45,7 +45,7 @@ export default class ApiManager { // 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 "${endpointText}"`, 'color: cornflowerblue;', ''); + 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 @@ -123,18 +123,7 @@ export default class ApiManager { const blobUUID = data['blobID']; const blobData = data['blobData']; - // let templateBlob = blobData; // By default, apply no template - - // Only run if all coordinates are there - // if (this.templateCoordsTilePixel?.length >= 4) { - - // if ((tileCoordsTile[0] == this.templateCoordsTilePixel[0]) && (tileCoordsTile[1] == this.templateCoordsTilePixel[1])) { - - // console.log(`templateState: ${this.templateManager.templateState || null}`); - // templateBlob = !!this.templateManager.templateState ? await this.templateManager.drawTemplate(blobData, this.templateCoordsTilePixel) : blobData; - - // } - // } + const templateBlob = await this.templateManager.drawTemplateOnTile(blobData, tileCoordsTile); window.postMessage({ diff --git a/src/main.js b/src/main.js index 32f2e00..8a6fe34 100644 --- a/src/main.js +++ b/src/main.js @@ -6,7 +6,7 @@ import Overlay from './Overlay.js'; import Observers from './observers.js'; import ApiManager from './apiManager.js'; import TemplateManager from './templateManager.js'; -import { consoleLog } from './utils.js'; +import { consoleLog, consoleWarn } from './utils.js'; const name = GM_info.script.name.toString(); // Name of userscript const version = GM_info.script.version.toString(); // Version of userscript @@ -50,7 +50,20 @@ inject(() => { // The modified blob won't have an endpoint, so we ignore any message without one. if ((source == 'blue-marble') && !!blobID && !!blobData && !endpoint) { - fetchedBlobQueue.get(blobID)(blobData); + + 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 } }); @@ -111,9 +124,6 @@ inject(() => { statusText: cloned.statusText })); - // Removes the processed blob from the queue - fetchedBlobQueue.delete(blobUUID); - // 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, ''); });