// ==UserScript==
// @name wplace - simple map browser
// @namespace http://pawing.cv
// @version v1.1
// @description use openstreetmap to navigate the world faster, and press insert above your preferred coordinates to visit them on wplace!
// @author cv
// @match https://wplace.live/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
function openmap() {
let popupcode = `<!DOCTYPE html>
<html lang="en">
<head>
<title>OpenStreetMap</title>
<meta charset="utf-8">
<style>
@import url("https://fonts.googleapis.com/css2?family=Geist:[email protected]&display=swap");
html, body, .map {
height: 100%; line-height: 1;
margin: 0; padding: 0;
font-family: "Geist", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
}
a, span, p, li, input, ul {
font-family: Geist, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
user-select: none;
}
.coords, .small {user-select: all !important}
.info {
display: flex; flex-direction: row; position: fixed;
justify-content: center; align-items: center;
top: 1em; left: 50%; transform: translateX(-50%);
background: #f2f7fe;
padding: 10px; border-radius: 32px;
font-size: 13.5px; font-weight: 700; z-index: 1000;
box-shadow: 0 5px 16px 0 rgba(0,0,0,0.1); color: #394e6a;
pointer-events: none; user-select: none;
}
.info svg {
padding-left: 3px; padding-right: 2px; width: 15px;
}
.coords {
display: flex; flex-direction: column; position: fixed; width: 300px;
bottom: 1em; left: 50%; transform: translateX(-50%);
background: #f2f7fe; display: none;
padding: 20px; border-radius: 32px;
font-size: 18px; z-index: 1000; font-weight: 500;
box-shadow: 0 5px 16px 0 rgba(0,0,0,0.1); color: #394e6a;
}
.small {
font-size: 13px; color: #394e6a;
opacity: 0.5;
}
div.leaflet-control-container > div.leaflet-top.leaflet-left > div {
border: none;
}
div.leaflet-control-container > div.leaflet-top.leaflet-left > div > a {
display: flex; flex-direction: column; background: #f2f7fe;
border-radius: 32px; margin-bottom: 3px; font-size: 13px;
box-shadow: 0 5px 16px 0 rgba(0,0,0,0.1); color: #394e6a;
}
div.leaflet-top.leaflet-right * {
border: none;
}
div.leaflet-top.leaflet-right > div > button,
div.leaflet-top.leaflet-right > div {
display: flex; flex-direction: column; background: #f2f7fe;
border-radius: 32px; margin-bottom: 3px;
color: #394e6a;
}
div.leaflet-top.leaflet-right > div > button {
display: none;
}
div.leaflet-top.leaflet-right > div > div.leaflet-control-geocoder-form {
display: inline-block;
width: 200px; padding: 10px;
box-shadow: 0 5px 16px 0 rgba(0,0,0,0.1);
}
div.leaflet-top.leaflet-right > div > .leaflet-control-geocoder-alternatives {
border-radius: 1px; padding: 10px;
}
div.leaflet-top.leaflet-right > div:has(.leaflet-control-geocoder-alternatives:not([style*="display: none"])) > .leaflet-control-geocoder-form {
box-shadow: none !important;
}
body > div.map.leaflet-container.leaflet-touch.leaflet-fade-anim.leaflet-grab.leaflet-touch-drag.leaflet-touch-zoom > div.leaflet-control-container > div.leaflet-bottom.leaflet-right {
display: none !important;
}
.leaflet-control-geocoder-form-no-error {
text-align: center; color: rgb(179, 97, 97) !important;
padding-bottom: 10px;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
</head>
<body>
<div class="info">Choose a location with <svg alt="Right Click" fill="#394e6a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52"><path d="M38 0h4v6h-4V0Zm9 1 4 2c-2 4-2 4-7 5 1-5 1-5 3-7ZM36 5c8 6 9 12 9 22v6c-2 10-5 14-14 19-10 1-16 1-23-6-4-7-5-11-5-19v-6C5 4 21-3 36 5ZM9 17l-1 9h14V7c-7 0-10 5-13 10ZM8 30c2 9 3 13 11 17 11 0 14-1 20-10l1-7H8Zm38-20h6v4h-6v-4Z"/></svg>!</div>
<div class="coords"></div>
<div class="map" style="width:100%;height:100%;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.Photon.js"></script>
<script>
document.addEventListener("contextmenu", function(e) {e.preventDefault()});
var map = L.map(document.querySelector(".map"), {
worldCopyJump: false,
continuousWorld: false
}).setView([0, 0], 1);
map.setMaxBounds([[-85.05112878, -180], [85.05112878, 180]]);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {noWrap: true}).addTo(map);
map.on('drag', function() {
if (!map.getBounds().intersects(map.options.maxBounds)) {
map.panInsideBounds(map.options.maxBounds, { animate: false });
}
});
L.Control.geocoder({
geocoder: new L.Control.Geocoder.Photon()
}).addTo(map);
var lastlatlng = null;
map.on("mousemove", function(e) {
lastlatlng = e.latlng; var lat = e.latlng.lat; var lng = e.latlng.lng;
var zoom = map.getZoom();
var tile_count = 2048; var tile_size = 1000;
var pixel_count = tile_count * tile_size;
var x = (lng + 180) / 360; var sinlat = Math.sin(lat * Math.PI / 180);
var y = 0.5 - Math.log((1 + sinlat) / (1 - sinlat)) / (4 * Math.PI);
x = Math.min(1, Math.max(0, x)); y = Math.min(1, Math.max(0, y));
var px = x * (pixel_count - 1); var py = y * (pixel_count - 1);
var t_x = Math.floor(px / tile_size); var t_y = Math.floor(py / tile_size);
var p_x = Math.floor(px % tile_size); var p_y = Math.floor(py % tile_size);
document.querySelector(".coords").innerHTML =
lat.toFixed(6) + ", " + lng.toFixed(6) + " [x" + zoom + "] " +
"<br><span class='small'>" + t_x + ", " + t_y + " // " + p_x + ", " + p_y + "</span>";
var coords = document.querySelector(".coords");
if (coords.innerHTML.trim() !== "") {coords.style.display = "flex"}
else {coords.style.display = "none"}
});
document.querySelector(".map").addEventListener("contextmenu", function(ev) {
ev.preventDefault();
if (lastlatlng) {
var osmzoom = map.getZoom(); var wplacezoom = Math.max(1, Math.min(19, osmzoom));
window.opener.postMessage({ lat: lastlatlng.lat, lng: lastlatlng.lng, zoom: wplacezoom, source: "wplace-map-popup" }, "*");
window.close();
}
});
</script>
</body>
</html>`;
const left = (window.screen.width - 800) / 2; const top = (window.screen.height - 600) / 2;
const popup = window.open(
'',
'wplace_map_popup',
`width=800,height=600,left=${left},top=${top},resizable=yes,scrollbars=no,status=no,location=no,toolbar=no,menubar=no`
);
if (popup) {
const writepopup = () => {
if (popup.document && popup.document.readyState === "complete") {
popup.document.open(); popup.document.write(popupcode); popup.document.close();
} else {setTimeout(writepopup, 10)}
}; writepopup();
} else {alert("Popup failed to open! (do you have popups disabled?)")}
}
function cleanlink() {
const cleanlink = new URL(window.location.href);
cleanlink.searchParams.delete('lat');
cleanlink.searchParams.delete('lng');
cleanlink.searchParams.delete('zoom');
window.history.replaceState({}, '', cleanlink.pathname + cleanlink.search + cleanlink.hash);
}
window.addEventListener('message', function(event) {
if (event.data && event.data.source === 'wplace-map-popup' &&
typeof event.data.lat === 'number' && typeof event.data.lng === 'number') {
const currentlink = new URL(window.location.href);
currentlink.searchParams.set('lat', event.data.lat);
currentlink.searchParams.set('lng', event.data.lng);
if (typeof event.data.zoom === 'number') {
currentlink.searchParams.set('zoom', event.data.zoom);
}
window.location.replace(currentlink.toString());
window.addEventListener('pageshow', cleanlink);
}
});
window.addEventListener('DOMContentLoaded', function() {
const url = new URL(window.location.href);
if (url.searchParams.has('lat') || url.searchParams.has('lng') || url.searchParams.has('zoom')) {
cleanlink();
}
});
document.addEventListener('keydown', function(e) {
if (e.key === 'M' || e.key === 'm') {openmap()}
});
})();