Changed: Squaremap configs support LiveAtlas & Squaremap Markers
1
main/.gitignore
vendored
@ -1,4 +1,3 @@
|
||||
/data
|
||||
/squaremap/data/
|
||||
/squaremap/locale/
|
||||
/squaremap/web/
|
||||
|
@ -37,6 +37,7 @@ https://cdn.modrinth.com/data/1u6JkXh5/versions/vBzkrSYP/worldedit-mod-7.3.6.jar
|
||||
|
||||
# Squaremap
|
||||
https://cdn.modrinth.com/data/PFb7ZqK6/versions/RerxbGKf/squaremap-fabric-mc1.21.1-1.3.2.jar
|
||||
https://github.com/SentixDev/squaremarker/releases/download/1.21.1-v1.0.6/squaremarker-fabric-mc1.21.1-1.0.6.jar
|
||||
|
||||
# World Guard
|
||||
https://cdn.modrinth.com/data/py6EMmAJ/versions/xpvSS4oW/yawp-0.0.2.10-alpha2.jar
|
||||
|
@ -13,7 +13,7 @@ settings:
|
||||
web-address: http://localhost:8080
|
||||
web-directory:
|
||||
path: web
|
||||
auto-update: true
|
||||
auto-update: false
|
||||
image-quality:
|
||||
compress-images:
|
||||
enabled: true
|
||||
|
BIN
main/squaremap/images/blank.png
Normal file
After Width: | Height: | Size: 167 B |
172
main/squaremap/web/css/styles.css
Normal file
@ -0,0 +1,172 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #000000;
|
||||
overflow: hidden;
|
||||
}
|
||||
ul, li {
|
||||
list-style-type: none;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#map {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: black;
|
||||
}
|
||||
img.leaflet-tile {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: -moz-crisp-edges
|
||||
}
|
||||
div.leaflet-nameplate-pane div {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
padding: 2px 5px 1px;
|
||||
margin: 0;
|
||||
border-color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
div.leaflet-nameplate-pane div:before {
|
||||
border-color: transparent;
|
||||
}
|
||||
div.leaflet-nameplate-pane div img.head {
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
}
|
||||
div.leaflet-nameplate-pane div img.armor,
|
||||
div.leaflet-nameplate-pane div img.health {
|
||||
display: block;
|
||||
}
|
||||
div.leaflet-nameplate-pane div,
|
||||
div.leaflet-marker-pane img {
|
||||
transition: all 0.25s;
|
||||
}
|
||||
.leaflet-bottom.leaflet-left .link,
|
||||
.leaflet-bottom.leaflet-left .coordinates {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
}
|
||||
div.leaflet-control-layers.link img {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
vertical-align: bottom;
|
||||
bottom: 0;
|
||||
background-image: url("../images/link.png");
|
||||
background-size: 20px 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
div.leaflet-control-layers.coordinates {
|
||||
vertical-align: bottom;
|
||||
padding: 2px 5px;
|
||||
line-height: 14px;
|
||||
height: 30px;
|
||||
}
|
||||
#sidebar {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
height: 100vh;
|
||||
padding-left: 10px;
|
||||
z-index: 10000;
|
||||
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
border-left: 1px solid #000000;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
color: #ffffff;
|
||||
text-align: right;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
#sidebar.show {
|
||||
width: 200px;
|
||||
padding-left: 0;
|
||||
}
|
||||
#sidebar fieldset {
|
||||
clear: right;
|
||||
margin: 30px 0;
|
||||
border: none;
|
||||
border-top: 1px solid rgba(196, 196, 196, 0.4);
|
||||
text-align: left;
|
||||
transition: all 0.25s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#sidebar fieldset#worlds {
|
||||
margin: -15px 0 0;
|
||||
}
|
||||
#sidebar fieldset#players {
|
||||
margin: 10px 0 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
#sidebar fieldset#players::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
#sidebar fieldset#players::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 0;
|
||||
}
|
||||
#sidebar fieldset#players::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border-right: 2px solid rgba(112, 128, 144, 0.75);
|
||||
border-radius: 1px;
|
||||
}
|
||||
#sidebar fieldset#players::-webkit-scrollbar-thumb:hover {
|
||||
border-radius: 3px;
|
||||
border: 0;
|
||||
background-color: rgba(112, 128, 144, 0.75);
|
||||
}
|
||||
#sidebar legend {
|
||||
display: block;
|
||||
margin: 10px 5px;
|
||||
}
|
||||
#sidebar fieldset a,
|
||||
#sidebar fieldset a:visited {
|
||||
display: block;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
padding: 5px 10px;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
#sidebar fieldset a.following {
|
||||
background-color: rgba(128, 128, 255, 0.25);
|
||||
}
|
||||
#sidebar fieldset a:hover,
|
||||
#sidebar fieldset a.following:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
cursor: pointer;
|
||||
}
|
||||
#sidebar fieldset a .other-world {
|
||||
filter: brightness(50%) !important;
|
||||
font-style: italic;
|
||||
}
|
||||
#sidebar fieldset a img {
|
||||
vertical-align: middle;
|
||||
padding-right: 10px;
|
||||
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
|
||||
-webkit-filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
|
||||
}
|
||||
#sidebar fieldset#players a {
|
||||
padding: 10px;
|
||||
}
|
||||
#sidebar #pin {
|
||||
position: relative;
|
||||
right: -160px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
margin: 5px 5px 10px;
|
||||
padding: 2px;
|
||||
border: 1px solid rgba(255, 255, 255, 0);
|
||||
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
|
||||
-webkit-filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
|
||||
cursor: pointer;
|
||||
}
|
||||
#sidebar #pin.pinned:hover {
|
||||
background: rgba(0, 128, 0, 0.5);
|
||||
border: 1px solid rgba(0, 128, 0, 0.75);
|
||||
}
|
||||
#sidebar #pin.unpinned:hover {
|
||||
background: rgba(128, 0, 0, 0.5);
|
||||
border: 1px solid rgba(128, 0, 0, 0.75);
|
||||
}
|
BIN
main/squaremap/web/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
main/squaremap/web/images/armor/0.png
Normal file
After Width: | Height: | Size: 603 B |
BIN
main/squaremap/web/images/armor/1.png
Normal file
After Width: | Height: | Size: 667 B |
BIN
main/squaremap/web/images/armor/10.png
Normal file
After Width: | Height: | Size: 662 B |
BIN
main/squaremap/web/images/armor/11.png
Normal file
After Width: | Height: | Size: 675 B |
BIN
main/squaremap/web/images/armor/12.png
Normal file
After Width: | Height: | Size: 664 B |
BIN
main/squaremap/web/images/armor/13.png
Normal file
After Width: | Height: | Size: 684 B |
BIN
main/squaremap/web/images/armor/14.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
main/squaremap/web/images/armor/15.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
main/squaremap/web/images/armor/16.png
Normal file
After Width: | Height: | Size: 669 B |
BIN
main/squaremap/web/images/armor/17.png
Normal file
After Width: | Height: | Size: 681 B |
BIN
main/squaremap/web/images/armor/18.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
main/squaremap/web/images/armor/19.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
main/squaremap/web/images/armor/2.png
Normal file
After Width: | Height: | Size: 665 B |
BIN
main/squaremap/web/images/armor/20.png
Normal file
After Width: | Height: | Size: 636 B |
BIN
main/squaremap/web/images/armor/3.png
Normal file
After Width: | Height: | Size: 690 B |
BIN
main/squaremap/web/images/armor/4.png
Normal file
After Width: | Height: | Size: 675 B |
BIN
main/squaremap/web/images/armor/5.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
main/squaremap/web/images/armor/6.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
main/squaremap/web/images/armor/7.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
main/squaremap/web/images/armor/8.png
Normal file
After Width: | Height: | Size: 664 B |
BIN
main/squaremap/web/images/armor/9.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
main/squaremap/web/images/blank.png
Normal file
After Width: | Height: | Size: 167 B |
BIN
main/squaremap/web/images/clear.png
Normal file
After Width: | Height: | Size: 527 B |
BIN
main/squaremap/web/images/end_sky.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
main/squaremap/web/images/foliage.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
main/squaremap/web/images/grass.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
main/squaremap/web/images/health/0.png
Normal file
After Width: | Height: | Size: 617 B |
BIN
main/squaremap/web/images/health/1.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
main/squaremap/web/images/health/10.png
Normal file
After Width: | Height: | Size: 678 B |
BIN
main/squaremap/web/images/health/11.png
Normal file
After Width: | Height: | Size: 684 B |
BIN
main/squaremap/web/images/health/12.png
Normal file
After Width: | Height: | Size: 681 B |
BIN
main/squaremap/web/images/health/13.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
main/squaremap/web/images/health/14.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
main/squaremap/web/images/health/15.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
main/squaremap/web/images/health/16.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
main/squaremap/web/images/health/17.png
Normal file
After Width: | Height: | Size: 686 B |
BIN
main/squaremap/web/images/health/18.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
main/squaremap/web/images/health/19.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
main/squaremap/web/images/health/2.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
main/squaremap/web/images/health/20.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
main/squaremap/web/images/health/3.png
Normal file
After Width: | Height: | Size: 675 B |
BIN
main/squaremap/web/images/health/4.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
main/squaremap/web/images/health/5.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
main/squaremap/web/images/health/6.png
Normal file
After Width: | Height: | Size: 680 B |
BIN
main/squaremap/web/images/health/7.png
Normal file
After Width: | Height: | Size: 684 B |
BIN
main/squaremap/web/images/health/8.png
Normal file
After Width: | Height: | Size: 680 B |
BIN
main/squaremap/web/images/health/9.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
main/squaremap/web/images/icon/blue-cube-smol.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
main/squaremap/web/images/icon/green-cube-smol.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
main/squaremap/web/images/icon/player.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
main/squaremap/web/images/icon/purple-cube-smol.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
main/squaremap/web/images/icon/red-cube-smol.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 160 B |
BIN
main/squaremap/web/images/icon/spawn.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
main/squaremap/web/images/link.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
main/squaremap/web/images/nether_sky.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
main/squaremap/web/images/og.png
Normal file
After Width: | Height: | Size: 309 KiB |
BIN
main/squaremap/web/images/overworld_sky.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
main/squaremap/web/images/pinned.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
main/squaremap/web/images/unpinned.png
Normal file
After Width: | Height: | Size: 11 KiB |
404
main/squaremap/web/index.html
Normal file
@ -0,0 +1,404 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="preload" href="https://fonts.gstatic.com/s/raleway/v22/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVvaorCIPrE.woff2" as="font" crossorigin="anonymous">
|
||||
<link rel="preload" href="https://fonts.gstatic.com/s/raleway/v22/1Ptxg8zYS_SKggPN4iEgvnHyvveLxVsEpbCIPrE.woff2" as="font" crossorigin="anonymous">
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
||||
<meta name="theme-color" content="#222222">
|
||||
<link rel="manifest" href="./live-atlas/favicons/site.webmanifest">
|
||||
<link rel="icon" href="./live-atlas/favicons/favicon.svg">
|
||||
<link rel=”mask-icon” href="./live-atlas/favicons/mask.svg" color="#cccccc">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="./live-atlas/favicons/apple-touch-icon.png">
|
||||
|
||||
<meta name="keywords" content="minecraft, map, dynamic, liveatlas" />
|
||||
<meta name="description" content="Minecraft Dynamic Map" />
|
||||
|
||||
<title>LiveAtlas</title>
|
||||
|
||||
<script>
|
||||
window.liveAtlasConfig = {
|
||||
// By default LiveAtlas looks for a dynmap standalone/config.js file
|
||||
// This configuration can be used instead to support Pl3xmap and Squaremap installations as well as multiple servers (external webserver required)
|
||||
// To configure multiple servers, see https://github.com/JLyne/LiveAtlas/wiki/Configuring-Multiple-Servers.
|
||||
|
||||
// Example Squaremap internal webserver configuration
|
||||
servers: {
|
||||
squaremap: {
|
||||
squaremap: window.location.pathname
|
||||
},
|
||||
},
|
||||
|
||||
// Example Pl3xmap internal webserver configuration
|
||||
// servers: {
|
||||
// pl3xmap: {
|
||||
// pl3xmap: window.location.pathname
|
||||
// },
|
||||
// },
|
||||
|
||||
// Example Overviewer configuration
|
||||
// servers: {
|
||||
// overviewer: {
|
||||
// overviewer: window.location.pathname
|
||||
// },
|
||||
// },
|
||||
|
||||
// Example Dynmap internal webserver configuration without using standalone/config.js
|
||||
// servers: {
|
||||
// dynmap: {
|
||||
// dynmap: {
|
||||
// configuration: 'standalone/dynmap_config.json?_={timestamp}',
|
||||
// update: 'standalone/dynmap_{world}.json?_={timestamp}',
|
||||
// sendmessage: 'standalone/sendmessage.php',
|
||||
// login: 'standalone/login.php',
|
||||
// register: 'standalone/register.php',
|
||||
// tiles: 'tiles/',
|
||||
// markers: 'tiles/'
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
|
||||
// Example multiple Dynmap servers on external webserver configuration
|
||||
// servers: {
|
||||
// creative: {
|
||||
// label: 'Creative',
|
||||
// dynmap: {
|
||||
// configuration: 'http://dynmap.local/standalone/creative/MySQL_configuration.php',
|
||||
// update: 'http://dynmap.local/standalone/creative/MySQL_update.php?world={world}&ts={timestamp}',
|
||||
// sendmessage: 'http://dynmap.local/standalone/creative/MySQL_sendmessage.php',
|
||||
// login: 'http://dynmap.local/standalone/creative/MySQL_login.php',
|
||||
// register: 'http://dynmap.local/standalone/creative/MySQL_register.php',
|
||||
// tiles: 'http://dynmap.local/standalone/creative/MySQL_tiles.php?tile=',
|
||||
// markers: 'http://dynmap.local/standalone/creative/MySQL_markers.php?marker='
|
||||
// }
|
||||
// },
|
||||
// survival: {
|
||||
// label: 'Survival',
|
||||
// dynmap: {
|
||||
// configuration: 'http://dynmap.local/standalone/survival/MySQL_configuration.php',
|
||||
// update: 'http://dynmap.local/standalone/survival/MySQL_update.php?world={world}&ts={timestamp}',
|
||||
// sendmessage: 'http://dynmap.local/standalone/survival/MySQL_sendmessage.php',
|
||||
// login: 'http://dynmap.local/standalone/survival/MySQL_login.php',
|
||||
// register: 'http://dynmap.local/standalone/survival/MySQL_register.php',
|
||||
// tiles: 'http://dynmap.local/standalone/survival/MySQL_tiles.php?tile=',
|
||||
// markers: 'http://dynmap.local/standalone/survival/MySQL_markers.php?marker='
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
|
||||
// These messages are used throughout LiveAtlas and can be translated here
|
||||
// If a message you want to translate isn't here, it is likely controlled by dynmap itself
|
||||
// see https://github.com/webbukkit/dynmap/wiki/Configuration.txt
|
||||
messages: {
|
||||
chatNoMessages: 'No chat messages yet...',
|
||||
chatTitle: 'Chat',
|
||||
chatLogin: 'Please login to send chat messages',
|
||||
chatSend: 'Send',
|
||||
chatPlaceholder: 'Type your chat message here...',
|
||||
chatErrorUnknown: 'Unexpected error while sending chat message',
|
||||
chatErrorDisabled: 'Chat is not enabled',
|
||||
serversHeading: 'Servers',
|
||||
markersHeading: 'Markers',
|
||||
markersSearchPlaceholder: 'Search markers...',
|
||||
markersSkeleton: 'No markers exist for the current world',
|
||||
markersSetSkeleton: 'This marker set is empty',
|
||||
markersSearchSkeleton: 'No matching markers found',
|
||||
markersUnnamed: '(Unnamed marker)',
|
||||
worldsSkeleton: 'No maps have been configured',
|
||||
playersSkeleton: 'No players are currently online',
|
||||
playersTitle: 'Click to center on player\nDouble-click to follow player',
|
||||
playersTitleHidden: 'This player is currently hidden from the map\nDouble-click to follow player when they become visible',
|
||||
playersTitleOtherWorld: 'This player is in another world.\nClick to center on player\nDouble-click to follow player',
|
||||
playersSearchPlaceholder: 'Search players...',
|
||||
playersSearchSkeleton: 'No matching players found',
|
||||
followingHeading: 'Following',
|
||||
followingUnfollow: 'Unfollow',
|
||||
followingTitleUnfollow: 'Stop following this player',
|
||||
followingHidden: 'Currently hidden',
|
||||
linkTitle: 'Copy link to current location',
|
||||
loadingTitle: 'Loading...',
|
||||
locationRegion: 'Region',
|
||||
locationChunk: 'Chunk',
|
||||
contextMenuCopyLink: 'Copy link to here',
|
||||
contextMenuCenterHere: 'Center here',
|
||||
toggleTitle: 'Click to toggle this section',
|
||||
mapTitle: 'Map - Use the arrow keys to pan the map',
|
||||
layersTitle: 'Layers',
|
||||
copyToClipboardSuccess: 'Copied to clipboard',
|
||||
copyToClipboardError: 'Unable to copy to clipboard',
|
||||
|
||||
loginTitle: 'Login/Register',
|
||||
loginHeading: 'Existing User',
|
||||
loginUsernameLabel: 'Username',
|
||||
loginPasswordLabel: 'Password',
|
||||
loginSubmit: 'Login',
|
||||
loginErrorUnknown: 'Unexpected error while logging in',
|
||||
loginErrorDisabled: 'Logging in is disabled on this server',
|
||||
loginErrorIncorrect: 'Incorrect username or password',
|
||||
loginSuccess: 'Logged in successfully',
|
||||
|
||||
registerHeading: 'New User',
|
||||
registerDescription: `Enter your username and password, along with your registration code.
|
||||
|
||||
You can get a registration code by running /dynmap webregister in-game.`,
|
||||
registerConfirmPasswordLabel: 'Confirm Password',
|
||||
registerCodeLabel: 'Registration Code',
|
||||
registerSubmit: 'Register',
|
||||
registerErrorUnknown: 'Unexpected error during registration',
|
||||
registerErrorDisabled: 'Registration is disabled on this server',
|
||||
registerErrorVerifyFailed: 'The entered passwords do not match',
|
||||
registerErrorIncorrect: 'Registration failed, please check the entered details are correct',
|
||||
|
||||
logoutTitle: 'Logout',
|
||||
logoutErrorUnknown: 'Unexpected error while logging out',
|
||||
logoutSuccess: 'Logged out successfully',
|
||||
|
||||
closeTitle: 'Close',
|
||||
showMore: 'Show more'
|
||||
},
|
||||
|
||||
ui: {
|
||||
// If true, player markers will always be displayed in front of other marker types
|
||||
playersAboveMarkers: true,
|
||||
|
||||
// Whether to enable the player list search box
|
||||
playersSearch: true,
|
||||
|
||||
// Use more compact pre-2.0 player marker style
|
||||
compactPlayerMarkers: false,
|
||||
|
||||
// Disable the map right click menu
|
||||
disableContextMenu: false,
|
||||
|
||||
// Disable the markers button and list
|
||||
disableMarkerUI: false,
|
||||
|
||||
// Custom URL to redirect to when logging in is required
|
||||
// This URL will need to handle the login process and redirect users back to LiveAtlas
|
||||
customLoginUrl: null
|
||||
},
|
||||
|
||||
// Config version. Do not modify.
|
||||
version: 1
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Theme colours */
|
||||
:root {
|
||||
--background-base: #222222; /* Foreground UI elements */
|
||||
--background-dark: #121212; /* Body/Splash screen/Shadows */
|
||||
--background-light: #363636; /* Scrollbars/inputs */
|
||||
--background-error: #771616; /* Errors */
|
||||
--background-marker: var(--background-dark); /* Map markers */
|
||||
|
||||
--background-disabled: #555555; /* Disabled controls */
|
||||
--background-hover: #363636; /* :hovered buttons/menu items */
|
||||
--background-active: #6d6d6d; /* Button :active */
|
||||
--background-selected: #BDBDBD; /* Selected buttons/menu items */
|
||||
|
||||
--outline-focus: #eeeeee; /* :focus outline */
|
||||
|
||||
--border-radius: 0.5rem;
|
||||
--border-color: #333333; /* Control borders */
|
||||
--border-error: #cc0e0e;
|
||||
--box-shadow: 2px 2px 0px #111111;
|
||||
|
||||
--text-base: rgba(255, 255, 255, 0.7); /* Normal text */
|
||||
--text-emphasis: rgba(255, 255, 255, 0.87); /* Chat messages/:focus inputs */
|
||||
--text-subtle: rgba(255, 255, 255, 0.5); /* Skeletons/secondary text */
|
||||
--text-disabled: var(--text-subtle); /* Disabled controls */
|
||||
--text-marker: var(--text-base); /* Map markers */
|
||||
|
||||
--text-hover: var(--text-base); /* Text in :hover buttons */
|
||||
--text-active: var(--text-base); /* Text in :active buttons */
|
||||
--text-selected: var(--background-base); /* Text in selected buttons */
|
||||
--text-shadow: 0.1rem 0.1rem #000000; /* Text in selected buttons */
|
||||
|
||||
--text-night: #ddffff; /* Clock time at night */
|
||||
--text-day: #ffdd33; /* Clock time in day */
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--background-light) transparent;
|
||||
}
|
||||
|
||||
*:hover, *:focus-within {
|
||||
scrollbar-color: var(--background-hover) transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 0.7rem;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: var(--background-light);
|
||||
border: 2px solid #000000;
|
||||
border-radius: 2rem;
|
||||
transition: background 1s ease-in;
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
|
||||
*:hover::-webkit-scrollbar-thumb, *:focus-within::-webkit-scrollbar-thumb {
|
||||
background-color: var(--background-hover);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background-color: var(--background-dark);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Raleway,
|
||||
system-ui,
|
||||
-apple-system,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif,
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji';
|
||||
color: var(--text-base);
|
||||
text-shadow: var(--text-shadow);
|
||||
letter-spacing: 0.02rem;
|
||||
}
|
||||
|
||||
noscript {
|
||||
color: var(--text-base);
|
||||
font-size: 1rem;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
#splash, noscript {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transition: 0.3s opacity linear;
|
||||
z-index: 100;
|
||||
background-color: var(--background-dark);
|
||||
cursor: wait;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.6rem;
|
||||
padding: 4rem;
|
||||
}
|
||||
|
||||
#splash[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#splash__spinner {
|
||||
margin-top: 4rem;
|
||||
animation: fade 0.5s linear 1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
#splash__error {
|
||||
margin-top: 2rem;
|
||||
transition: opacity 0.5s ease-in;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
max-width: 60rem;
|
||||
}
|
||||
|
||||
#splash__error-message {
|
||||
font-family: monospace;
|
||||
background-color: var(--background-error);
|
||||
padding: 1rem 1.5rem;
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#splash__error[aria-hidden=true] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-size: 1.6rem;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<script type="module" crossorigin src="./live-atlas/assets/index.a82d2095.js"></script>
|
||||
<link rel="modulepreload" href="./live-atlas/assets/vendor.d0ab50b1.js">
|
||||
<link rel="stylesheet" href="./live-atlas/assets/index.1de34b3a.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="splash">
|
||||
<svg id="splash__logo" width="200" height="200" viewBox="0 0 268.83 266.53" aria-hidden="true" fill="rgba(255, 255, 255, 0.7)">
|
||||
<path d="M5.235.662C2.153.606 0 2.945 0 6.912v216.577c0 5.288 3.828 11.41 8.582 13.725l57.835 28.165c4.755 2.316 8.582-.078 8.582-5.367V43.434c0-5.288-3.827-11.41-8.582-13.727L8.582 1.544C7.394.965 6.262.681 5.235.662zm32.283 135.96c14.394 0 26.062 11.669 26.062 26.063 0 14.394-24.167 59.55-26.062 57.654-1.854 1.854-26.063-43.26-26.063-57.654 0-14.394 11.669-26.063 26.063-26.063zM202.388 1.013l57.833 28.165c4.755 2.315 8.583 8.437 8.583 13.726v216.58c0 5.29-3.828 7.683-8.583 5.367l-57.833-28.164c-4.755-2.316-8.583-8.438-8.583-13.727V6.38c0-5.289 3.828-7.682 8.583-5.367zM172.012.39c-1.051-.035-2.209.191-3.426.709l-68.342 29.053c-4.867 2.07-8.786 7.993-8.786 13.282V260.01c0 5.29 3.919 7.88 8.786 5.811l68.342-29.053c4.867-2.07 8.786-7.991 8.786-13.28V6.91c0-3.967-2.204-6.417-5.36-6.521zm-36.949 41.216c14.394 0 26.063 11.668 26.063 26.062 0 14.394-24.168 59.55-26.063 57.655C133.209 127.177 109 82.063 109 67.668c0-14.394 11.669-26.062 26.063-26.062z"/>
|
||||
<path d="M48.573 162.689a11.056 11.056 0 0 1-11.056 11.056 11.056 11.056 0 0 1-11.056-11.056 11.056 11.056 0 0 1 11.056-11.056 11.056 11.056 0 0 1 11.056 11.056zM146.12 67.669a11.056 11.056 0 0 1-11.057 11.056 11.056 11.056 0 0 1-11.056-11.056 11.056 11.056 0 0 1 11.056-11.056 11.056 11.056 0 0 1 11.056 11.056z"/>
|
||||
</svg>
|
||||
|
||||
<svg id="splash__spinner" width="38" height="38" viewBox="0 0 38 38" stroke="#fff" aria-label="LiveAtlas is loading">
|
||||
<g transform="translate(1 1)" stroke-width="2" fill="none">
|
||||
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
|
||||
<path d="M36 18c0-9.94-8.06-18-18-18">
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"/>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div id="splash__error" aria-hidden="true">
|
||||
<span id="splash__error-message" role="alert"></span>
|
||||
<span id="splash__error-retry" aria-live="polite"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<noscript>
|
||||
<strong>LiveAtlas requires JavaScript to work.<br />Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
|
||||
<main id="app" aria-hidden="true"></main>
|
||||
|
||||
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
if(!window.liveAtlasLoaded) {
|
||||
document.getElementById('splash__error').setAttribute('aria-hidden', 'false');
|
||||
document.getElementById('splash__error-message').innerText = 'Required LiveAtlas files are missing or failed to load.\nPlease reinstall LiveAtlas.';
|
||||
document.getElementById('splash__spinner').style.visibility = 'hidden';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
243
main/squaremap/web/js/addons/Ellipse.js
Normal file
@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Copyright 2014 JD Fergason
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// https://github.com/jdfergason/Leaflet.Ellipse
|
||||
|
||||
L.SVG.include ({
|
||||
_updateEllipse: function (layer) {
|
||||
var rx = layer._radiusX,
|
||||
ry = layer._radiusY,
|
||||
phi = layer._tiltDeg,
|
||||
endPoint = layer._endPointParams;
|
||||
|
||||
var d = 'M' + endPoint.x0 + ',' + endPoint.y0 +
|
||||
'A' + rx + ',' + ry + ',' + phi + ',' +
|
||||
endPoint.largeArc + ',' + endPoint.sweep + ',' +
|
||||
endPoint.x1 + ',' + endPoint.y1 + ' z';
|
||||
this._setPath(layer, d);
|
||||
}
|
||||
});
|
||||
|
||||
L.Canvas.include ({
|
||||
_updateEllipse: function (layer) {
|
||||
if (layer._empty()) { return; }
|
||||
|
||||
var p = layer._point,
|
||||
ctx = this._ctx,
|
||||
r = layer._radiusX,
|
||||
s = (layer._radiusY || r) / r;
|
||||
|
||||
// this breaks "preferCanvas: true"
|
||||
//this._drawnLayers[layer._leaflet_id] = layer;
|
||||
|
||||
ctx.save();
|
||||
|
||||
ctx.translate(p.x, p.y);
|
||||
if (layer._tilt !== 0) {
|
||||
ctx.rotate( layer._tilt );
|
||||
}
|
||||
if (s !== 1) {
|
||||
ctx.scale(1, s);
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, r, 0, Math.PI * 2);
|
||||
ctx.restore();
|
||||
|
||||
this._fillStroke(ctx, layer);
|
||||
},
|
||||
});
|
||||
|
||||
L.Ellipse = L.Path.extend({
|
||||
|
||||
options: {
|
||||
fill: true,
|
||||
startAngle: 0,
|
||||
endAngle: 359.9
|
||||
},
|
||||
|
||||
initialize: function (latlng, radii, tilt, options) {
|
||||
|
||||
L.setOptions(this, options);
|
||||
this._latlng = L.latLng(latlng);
|
||||
|
||||
if (tilt) {
|
||||
this._tiltDeg = tilt;
|
||||
} else {
|
||||
this._tiltDeg = 0;
|
||||
}
|
||||
|
||||
if (radii) {
|
||||
this._mRadiusX = radii[0];
|
||||
this._mRadiusY = radii[1];
|
||||
}
|
||||
},
|
||||
|
||||
setRadius: function (radii) {
|
||||
this._mRadiusX = radii[0];
|
||||
this._mRadiusY = radii[1];
|
||||
return this.redraw();
|
||||
},
|
||||
|
||||
getRadius: function () {
|
||||
return new L.point(this._mRadiusX, this._mRadiusY);
|
||||
},
|
||||
|
||||
setTilt: function (tilt) {
|
||||
this._tiltDeg = tilt;
|
||||
return this.redraw();
|
||||
},
|
||||
|
||||
getBounds: function () {
|
||||
// TODO respect tilt (bounds are too big)
|
||||
var lngRadius = this._getLngRadius(),
|
||||
latRadius = this._getLatRadius(),
|
||||
latlng = this._latlng;
|
||||
|
||||
return new L.LatLngBounds(
|
||||
[latlng.lat - latRadius, latlng.lng - lngRadius],
|
||||
[latlng.lat + latRadius, latlng.lng + lngRadius]);
|
||||
},
|
||||
|
||||
// @method setLatLng(latLng: LatLng): this
|
||||
// Sets the position of a circle marker to a new location.
|
||||
setLatLng: function (latlng) {
|
||||
this._latlng = L.latLng(latlng);
|
||||
this.redraw();
|
||||
return this.fire('move', {latlng: this._latlng});
|
||||
},
|
||||
|
||||
// @method getLatLng(): LatLng
|
||||
// Returns the current geographical position of the circle marker
|
||||
getLatLng: function () {
|
||||
return this._latlng;
|
||||
},
|
||||
|
||||
setStyle: L.Path.prototype.setStyle,
|
||||
|
||||
_project: function () {
|
||||
var lngRadius = this._getLngRadius(),
|
||||
latRadius = this._getLatRadius(),
|
||||
latlng = this._latlng,
|
||||
pointLeft = this._map.latLngToLayerPoint([latlng.lat, latlng.lng - lngRadius]),
|
||||
pointBelow = this._map.latLngToLayerPoint([latlng.lat - latRadius, latlng.lng]);
|
||||
|
||||
this._point = this._map.latLngToLayerPoint(latlng);
|
||||
this._radiusX = Math.max(this._point.x - pointLeft.x, 1) * this._map.options.scale;
|
||||
this._radiusY = Math.max(pointBelow.y - this._point.y, 1) * this._map.options.scale;
|
||||
this._tilt = Math.PI * this._tiltDeg / 180;
|
||||
this._endPointParams = this._centerPointToEndPoint();
|
||||
this._updateBounds();
|
||||
},
|
||||
|
||||
_updateBounds: function () {
|
||||
// http://math.stackexchange.com/questions/91132/how-to-get-the-limits-of-rotated-ellipse
|
||||
var sin = Math.sin(this._tilt);
|
||||
var cos = Math.cos(this._tilt);
|
||||
var sinSquare = sin * sin;
|
||||
var cosSquare = cos * cos;
|
||||
var aSquare = this._radiusX * this._radiusX;
|
||||
var bSquare = this._radiusY * this._radiusY;
|
||||
var halfWidth = Math.sqrt(aSquare*cosSquare+bSquare*sinSquare);
|
||||
var halfHeight = Math.sqrt(aSquare*sinSquare+bSquare*cosSquare);
|
||||
var w = this._clickTolerance();
|
||||
var p = [halfWidth + w, halfHeight + w];
|
||||
this._pxBounds = new L.Bounds(this._point.subtract(p), this._point.add(p));
|
||||
},
|
||||
|
||||
_update: function () {
|
||||
if (this._map) {
|
||||
this._updatePath();
|
||||
}
|
||||
},
|
||||
|
||||
_updatePath: function () {
|
||||
this._renderer._updateEllipse(this);
|
||||
},
|
||||
|
||||
_getLatRadius: function () {
|
||||
var simpleCrs = !!this._map.options.crs.infinite;
|
||||
if(simpleCrs)
|
||||
return this._mRadiusY;
|
||||
else
|
||||
return (this._mRadiusY / 40075017) * 360;
|
||||
},
|
||||
|
||||
_getLngRadius: function () {
|
||||
var simpleCrs = !!this._map.options.crs.infinite;
|
||||
if(simpleCrs)
|
||||
return this._mRadiusX;
|
||||
else
|
||||
return ((this._mRadiusX / 40075017) * 360) / Math.cos((Math.PI / 180) * this._latlng.lat);
|
||||
},
|
||||
|
||||
_centerPointToEndPoint: function () {
|
||||
// Convert between center point parameterization of an ellipse
|
||||
// too SVG's end-point and sweep parameters. This is an
|
||||
// adaptation of the perl code found here:
|
||||
// http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Paths
|
||||
var c = this._point,
|
||||
rx = this._radiusX,
|
||||
ry = this._radiusY,
|
||||
theta2 = (this.options.startAngle + this.options.endAngle) * (Math.PI / 180),
|
||||
theta1 = this.options.startAngle * (Math.PI / 180),
|
||||
delta = this.options.endAngle,
|
||||
phi = this._tiltDeg * (Math.PI / 180);
|
||||
|
||||
// Determine start and end-point coordinates
|
||||
var x0 = c.x + Math.cos(phi) * rx * Math.cos(theta1) +
|
||||
Math.sin(-phi) * ry * Math.sin(theta1);
|
||||
var y0 = c.y + Math.sin(phi) * rx * Math.cos(theta1) +
|
||||
Math.cos(phi) * ry * Math.sin(theta1);
|
||||
|
||||
var x1 = c.x + Math.cos(phi) * rx * Math.cos(theta2) +
|
||||
Math.sin(-phi) * ry * Math.sin(theta2);
|
||||
var y1 = c.y + Math.sin(phi) * rx * Math.cos(theta2) +
|
||||
Math.cos(phi) * ry * Math.sin(theta2);
|
||||
|
||||
var largeArc = (delta > 180) ? 1 : 0;
|
||||
var sweep = (delta > 0) ? 1 : 0;
|
||||
|
||||
return {'x0': x0, 'y0': y0, 'tilt': phi, 'largeArc': largeArc,
|
||||
'sweep': sweep, 'x1': x1, 'y1': y1};
|
||||
},
|
||||
|
||||
_empty: function () {
|
||||
return this._radiusX && this._radiusY && !this._renderer._bounds.intersects(this._pxBounds);
|
||||
},
|
||||
|
||||
_containsPoint : function (p) {
|
||||
// http://stackoverflow.com/questions/7946187/point-and-ellipse-rotated-position-test-algorithm
|
||||
var sin = Math.sin(this._tilt);
|
||||
var cos = Math.cos(this._tilt);
|
||||
var dx = p.x - this._point.x;
|
||||
var dy = p.y - this._point.y;
|
||||
var sumA = cos * dx + sin * dy;
|
||||
var sumB = sin * dx - cos * dy;
|
||||
if (this.options.fill === false) {
|
||||
var x = this._radiusX - this.options.weight;
|
||||
var y = this._radiusY - this.options.weight;
|
||||
if (sumA * sumA / (x * x) + sumB * sumB / (y * y) <= 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return sumA * sumA / (this._radiusX * this._radiusX) + sumB * sumB / (this._radiusY * this._radiusY) <= 1;
|
||||
}
|
||||
});
|
||||
|
||||
L.ellipse = function (latlng, radii, tilt, options) {
|
||||
return new L.Ellipse(latlng, radii, tilt, options);
|
||||
};
|
82
main/squaremap/web/js/addons/RotateMarker.js
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Benjamin Becquet
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* https://github.com/bbecquet/Leaflet.RotatedMarker
|
||||
*/
|
||||
(function() {
|
||||
// save these original methods before they are overwritten
|
||||
var proto_initIcon = L.Marker.prototype._initIcon;
|
||||
var proto_setPos = L.Marker.prototype._setPos;
|
||||
|
||||
var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
|
||||
|
||||
L.Marker.addInitHook(function () {
|
||||
var iconOptions = this.options.icon && this.options.icon.options;
|
||||
var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
|
||||
if (iconAnchor) {
|
||||
iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
|
||||
}
|
||||
this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;
|
||||
this.options.rotationAngle = this.options.rotationAngle || 0;
|
||||
|
||||
// Ensure marker keeps rotated during dragging
|
||||
this.on('drag', function(e) { e.target._applyRotation(); });
|
||||
});
|
||||
|
||||
L.Marker.include({
|
||||
_initIcon: function() {
|
||||
proto_initIcon.call(this);
|
||||
},
|
||||
|
||||
_setPos: function (pos) {
|
||||
proto_setPos.call(this, pos);
|
||||
this._applyRotation();
|
||||
},
|
||||
|
||||
_applyRotation: function () {
|
||||
if(this.options.rotationAngle) {
|
||||
this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;
|
||||
|
||||
if(oldIE) {
|
||||
// for IE 9, use the 2D rotation
|
||||
this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
|
||||
} else {
|
||||
// for modern browsers, prefer the 3D accelerated version
|
||||
this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setRotationAngle: function(angle) {
|
||||
this.options.rotationAngle = angle;
|
||||
this.update();
|
||||
return this;
|
||||
},
|
||||
|
||||
setRotationOrigin: function(origin) {
|
||||
this.options.rotationOrigin = origin;
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
})();
|
106
main/squaremap/web/js/modules/LayerControl.js
Normal file
@ -0,0 +1,106 @@
|
||||
import { P } from './Squaremap.js';
|
||||
import { SquaremapTileLayer } from './SquaremapTileLayer.js';
|
||||
|
||||
class LayerControl {
|
||||
constructor() {
|
||||
this.layers = new Map();
|
||||
}
|
||||
init() {
|
||||
this.currentLayer = 0;
|
||||
this.updateInterval = 60;
|
||||
|
||||
this.playersLayer = new L.LayerGroup();
|
||||
this.playersLayer.id = "players_layer";
|
||||
|
||||
this.controls = L.control.layers({}, {}, {
|
||||
position: 'topleft',
|
||||
sortLayers: true,
|
||||
sortFunction: (a, b) => {
|
||||
return a.order - b.order;
|
||||
}
|
||||
})
|
||||
.addTo(P.map);
|
||||
}
|
||||
addOverlay(name, layer, hide) {
|
||||
this.controls.addOverlay(layer, name);
|
||||
if (this.shouldHide(layer, hide) !== true) {
|
||||
layer.addTo(P.map);
|
||||
}
|
||||
}
|
||||
removeOverlay(layer) {
|
||||
this.ignoreLayer = layer;
|
||||
this.controls.removeLayer(layer);
|
||||
layer.remove();
|
||||
this.ignoreLayer = null;
|
||||
}
|
||||
shouldHide(layer, def) {
|
||||
const value = window.localStorage.getItem(`hide_${layer.id}`);
|
||||
return value == null ? def : value === 'true';
|
||||
}
|
||||
hideLayer(layer) {
|
||||
if (layer != this.ignoreLayer) {
|
||||
window.localStorage.setItem(`hide_${layer.id}`, 'true');
|
||||
}
|
||||
}
|
||||
showLayer(layer) {
|
||||
if (layer != this.ignoreLayer) {
|
||||
window.localStorage.setItem(`hide_${layer.id}`, 'false');
|
||||
}
|
||||
}
|
||||
setupTileLayers(world) {
|
||||
// setup the map tile layers
|
||||
// we need 2 layers to swap between for seamless refreshing
|
||||
if (this.tileLayer1 != null) {
|
||||
P.map.removeLayer(this.tileLayer1);
|
||||
}
|
||||
if (this.tileLayer2 != null) {
|
||||
P.map.removeLayer(this.tileLayer2);
|
||||
}
|
||||
this.tileLayer1 = this.createTileLayer(world);
|
||||
this.tileLayer2 = this.createTileLayer(world);
|
||||
|
||||
// refresh player's control
|
||||
this.removeOverlay(this.playersLayer);
|
||||
if (world.player_tracker.show_controls) {
|
||||
this.addOverlay(world.player_tracker.label,
|
||||
this.playersLayer,
|
||||
world.player_tracker.default_hidden);
|
||||
}
|
||||
this.playersLayer.order = world.player_tracker.priority;
|
||||
this.playersLayer.setZIndex(world.player_tracker.z_index);
|
||||
}
|
||||
createTileLayer(world) {
|
||||
return new SquaremapTileLayer(`tiles/${world.name}/{z}/{x}_{y}.png`, {
|
||||
tileSize: 512,
|
||||
minNativeZoom: 0,
|
||||
maxNativeZoom: world.zoom.max,
|
||||
errorTileUrl: 'images/clear.png'
|
||||
}).addTo(P.map)
|
||||
.addEventListener("load", () => {
|
||||
// when all tiles are loaded, switch to this layer
|
||||
this.switchTileLayer();
|
||||
});
|
||||
}
|
||||
updateTileLayer() {
|
||||
// redraw background tile layer
|
||||
if (this.currentLayer == 1) {
|
||||
this.tileLayer2.redraw();
|
||||
} else {
|
||||
this.tileLayer1.redraw();
|
||||
}
|
||||
}
|
||||
switchTileLayer() {
|
||||
// swap current tile layer
|
||||
if (this.currentLayer == 1) {
|
||||
this.tileLayer1.setZIndex(0);
|
||||
this.tileLayer2.setZIndex(1);
|
||||
this.currentLayer = 2;
|
||||
} else {
|
||||
this.tileLayer1.setZIndex(1);
|
||||
this.tileLayer2.setZIndex(0);
|
||||
this.currentLayer = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { LayerControl };
|
161
main/squaremap/web/js/modules/PlayerList.js
Normal file
@ -0,0 +1,161 @@
|
||||
import { Player } from "./util/Player.js";
|
||||
import { P } from './Squaremap.js';
|
||||
|
||||
class PlayerList {
|
||||
constructor(json) {
|
||||
this.players = new Map();
|
||||
this.markers = new Map();
|
||||
this.following = null;
|
||||
this.firstTick = true;
|
||||
this.label = json.player_list_label;
|
||||
P.map.createPane("nameplate").style.zIndex = 1000;
|
||||
}
|
||||
tick() {
|
||||
if (P.tick_count % P.worldList.curWorld.player_tracker.update_interval == 0) {
|
||||
P.getJSON("tiles/players.json", (json) => {
|
||||
this.updatePlayerList(json.players);
|
||||
const title = `${this.label}`
|
||||
.replace(/{cur}/g, json.players.length)
|
||||
.replace(/{max}/g, json.max == null ? "???" : json.max)
|
||||
if (P.sidebar.players.legend.innerHTML !== title) {
|
||||
P.sidebar.players.legend.innerHTML = title;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
showPlayer(uuid) {
|
||||
const player = this.players.get(uuid);
|
||||
if (!P.worldList.worlds.has(player.world)) {
|
||||
return false;
|
||||
}
|
||||
P.worldList.showWorld(player.world, () => {
|
||||
P.map.panTo(P.toLatLng(player.x, player.z));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
addToList(player) {
|
||||
const head = document.createElement("img");
|
||||
head.src = player.getHeadUrl();
|
||||
|
||||
const span = document.createElement("span");
|
||||
span.innerHTML = player.displayName
|
||||
|
||||
const link = P.createElement("a", player.uuid, this);
|
||||
link.onclick = function (e) {
|
||||
if (this.parent.showPlayer(this.id)) {
|
||||
this.parent.followPlayerMarker(this.id);
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
link.appendChild(head);
|
||||
link.appendChild(span);
|
||||
const fieldset = P.sidebar.players.element;
|
||||
fieldset.appendChild(link);
|
||||
Array.from(fieldset.getElementsByTagName("a"))
|
||||
.sort((a, b) => {
|
||||
return plain(a.getElementsByTagName("span")[0])
|
||||
.localeCompare(plain(b.getElementsByTagName("span")[0]));
|
||||
})
|
||||
.forEach(link => fieldset.appendChild(link));
|
||||
}
|
||||
removeFromList(player) {
|
||||
const link = document.getElementById(player.uuid);
|
||||
if (link != null) {
|
||||
link.remove();
|
||||
}
|
||||
this.players.delete(player.uuid);
|
||||
player.removeMarker();
|
||||
}
|
||||
updatePlayerList(players) {
|
||||
const playersToRemove = Array.from(this.players.keys());
|
||||
|
||||
let needsSort = false;
|
||||
|
||||
// update players from json
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
let player = this.players.get(players[i].uuid);
|
||||
if (player == null) {
|
||||
// new player
|
||||
player = new Player(players[i]);
|
||||
this.players.set(player.uuid, player);
|
||||
this.addToList(player);
|
||||
} else {
|
||||
const oldDisplayName = player.displayName;
|
||||
player.update(players[i]);
|
||||
if (oldDisplayName !== player.displayName) {
|
||||
needsSort = true;
|
||||
document.getElementById(player.uuid)
|
||||
.getElementsByTagName("span")[0]
|
||||
.innerHTML = player.displayName;
|
||||
}
|
||||
}
|
||||
playersToRemove.remove(players[i].uuid);
|
||||
}
|
||||
|
||||
// remove players not in json
|
||||
for (let i = 0; i < playersToRemove.length; i++) {
|
||||
const player = this.players.get(playersToRemove[i]);
|
||||
this.removeFromList(player);
|
||||
}
|
||||
|
||||
if (needsSort) {
|
||||
const fieldset = P.sidebar.players.element;
|
||||
Array.from(fieldset.getElementsByTagName("a"))
|
||||
.sort((a, b) => {
|
||||
return plain(a.getElementsByTagName("span")[0])
|
||||
.localeCompare(plain(b.getElementsByTagName("span")[0]));
|
||||
})
|
||||
.forEach(link => fieldset.appendChild(link));
|
||||
}
|
||||
|
||||
// first tick only
|
||||
if (this.firstTick) {
|
||||
this.firstTick = false;
|
||||
|
||||
// follow uuid from url
|
||||
const follow = P.getUrlParam("uuid", null);
|
||||
if (follow != null && this.players.get(follow) != null) {
|
||||
this.followPlayerMarker(follow);
|
||||
}
|
||||
}
|
||||
|
||||
// follow highlighted player
|
||||
if (this.following != null) {
|
||||
const player = this.players.get(this.following);
|
||||
if (player != null && P.worldList.curWorld != null) {
|
||||
if (player.world !== P.worldList.curWorld.name) {
|
||||
P.worldList.showWorld(player.world, () => {
|
||||
P.map.panTo(P.toLatLng(player.x, player.z));
|
||||
});
|
||||
} else {
|
||||
P.map.panTo(P.toLatLng(player.x, player.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clearPlayerMarkers() {
|
||||
const playersToRemove = Array.from(this.players.keys());
|
||||
for (let i = 0; i < playersToRemove.length; i++) {
|
||||
const player = this.players.get(playersToRemove[i]);
|
||||
player.removeMarker();
|
||||
}
|
||||
this.markers.clear();
|
||||
//P.layerControl.playersLayer.clearLayers();
|
||||
}
|
||||
followPlayerMarker(uuid) {
|
||||
if (this.following != null) {
|
||||
document.getElementById(this.following).classList.remove("following");
|
||||
this.following = null;
|
||||
}
|
||||
if (uuid != null) {
|
||||
this.following = uuid;
|
||||
document.getElementById(this.following).classList.add("following");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function plain(element) {
|
||||
return element.textContent || element.innerText || "";
|
||||
}
|
||||
|
||||
export { PlayerList };
|
47
main/squaremap/web/js/modules/Sidebar.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { Pin } from "./util/Pin.js";
|
||||
import { Fieldset } from "./util/Fieldset.js";
|
||||
import { P } from './Squaremap.js';
|
||||
|
||||
class Sidebar {
|
||||
constructor(json, show) {
|
||||
this.sidebar = P.createElement("div", "sidebar", this);
|
||||
this.showSidebar = show;
|
||||
if (!show) {
|
||||
this.sidebar.style.display = "none";
|
||||
}
|
||||
this.sidebar.addEventListener("click", (e) => {
|
||||
P.playerList.followPlayerMarker(null);
|
||||
});
|
||||
document.body.appendChild(this.sidebar);
|
||||
|
||||
this.pin = new Pin(json.pinned == "pinned");
|
||||
this.show(this.pin.pinned);
|
||||
if (json.pinned != "hide") {
|
||||
this.sidebar.appendChild(this.pin.element);
|
||||
}
|
||||
|
||||
this.worlds = new Fieldset("worlds", json.world_list_label);
|
||||
this.sidebar.appendChild(this.worlds.element);
|
||||
|
||||
this.players = new Fieldset("players", json.player_list_label
|
||||
.replace(/{cur}/g, 0)
|
||||
.replace(/{max}/g, 0));
|
||||
this.sidebar.appendChild(this.players.element);
|
||||
|
||||
this.sidebar.onmouseleave = () => {
|
||||
if (!this.pin.pinned) {
|
||||
this.show(false);
|
||||
}
|
||||
};
|
||||
this.sidebar.onmouseenter = () => {
|
||||
if (!this.pin.pinned) {
|
||||
this.show(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
show(show) {
|
||||
this.sidebar.className = show ? "show" : "";
|
||||
}
|
||||
}
|
||||
|
||||
export { Sidebar };
|
171
main/squaremap/web/js/modules/Squaremap.js
Normal file
@ -0,0 +1,171 @@
|
||||
import { Sidebar } from "./Sidebar.js";
|
||||
import { PlayerList } from "./PlayerList.js";
|
||||
import { WorldList } from "./WorldList.js";
|
||||
import { UICoordinates } from "./UICoordinates.js";
|
||||
import { UILink } from "./UILink.js";
|
||||
import { LayerControl } from "./LayerControl.js";
|
||||
|
||||
class SquaremapMap {
|
||||
constructor() {
|
||||
this.map = L.map("map", {
|
||||
crs: L.CRS.Simple,
|
||||
center: [0, 0],
|
||||
attributionControl: false,
|
||||
preferCanvas: true,
|
||||
noWrap: true
|
||||
})
|
||||
.on('overlayadd', (e) => {
|
||||
this.layerControl.showLayer(e.layer);
|
||||
})
|
||||
.on('overlayremove', (e) => {
|
||||
this.layerControl.hideLayer(e.layer);
|
||||
})
|
||||
.on('click', (e) => {
|
||||
this.playerList.followPlayerMarker(null);
|
||||
})
|
||||
.on('dblclick', (e) => {
|
||||
this.playerList.followPlayerMarker(null);
|
||||
});
|
||||
|
||||
this.tick_count = 1;
|
||||
|
||||
this.layerControl = new LayerControl();
|
||||
|
||||
this.init();
|
||||
}
|
||||
loop() {
|
||||
if (document.visibilityState === 'visible') {
|
||||
this.tick();
|
||||
this.tick_count++;
|
||||
}
|
||||
setTimeout(() => this.loop(), 1000);
|
||||
}
|
||||
tick() {
|
||||
// tick player tracker
|
||||
this.playerList.tick();
|
||||
// tick world
|
||||
this.worldList.curWorld.tick();
|
||||
}
|
||||
init() {
|
||||
this.getJSON("tiles/settings.json", (json) => {
|
||||
this.layerControl.init();
|
||||
|
||||
this.title = json.ui.title;
|
||||
this.sidebar = new Sidebar(json.ui.sidebar, this.getUrlParam("show_sidebar", "true") === "true");
|
||||
this.playerList = new PlayerList(json.ui.sidebar);
|
||||
this.worldList = new WorldList(json.worlds);
|
||||
this.coordinates = new UICoordinates(json.ui.coordinates, this.getUrlParam("show_coordinates", "true") === "true");
|
||||
this.uiLink = new UILink(json.ui.link, this.getUrlParam("show_link_button", "true") === "true");
|
||||
|
||||
this.showControls = this.getUrlParam("show_controls", "true") === "true"
|
||||
if (!this.showControls) {
|
||||
let controlLayers = document.getElementsByClassName('leaflet-top leaflet-left');
|
||||
controlLayers[0].style.display = "none";
|
||||
}
|
||||
|
||||
this.worldList.loadInitialWorld(json, (world) => {
|
||||
this.loop();
|
||||
this.centerOn(
|
||||
this.getUrlParam("x", world.spawn.x),
|
||||
this.getUrlParam("z", world.spawn.z),
|
||||
this.getUrlParam("zoom", world.zoom.def));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
centerOn(x, z, zoom) {
|
||||
this.map.setView(this.toLatLng(x, z), zoom);
|
||||
this.uiLink.update();
|
||||
return this.map;
|
||||
}
|
||||
toLatLng(x, z) {
|
||||
return L.latLng(this.pixelsToMeters(-z), this.pixelsToMeters(x));
|
||||
//return this.map.unproject([x, z], this.worldList.curWorld.zoom.max);
|
||||
}
|
||||
toPoint(latlng) {
|
||||
return L.point(this.metersToPixels(latlng.lng), this.metersToPixels(-latlng.lat));
|
||||
//return this.map.project(latlng, this.worldList.curWorld.zoom.max);
|
||||
}
|
||||
pixelsToMeters(num) {
|
||||
return num * this.scale;
|
||||
}
|
||||
metersToPixels(num) {
|
||||
return num / this.scale;
|
||||
}
|
||||
setScale(zoom) {
|
||||
this.scale = (1 / Math.pow(2, zoom));
|
||||
// store this on map for ellipse
|
||||
this.map.options.scale = this.scale;
|
||||
}
|
||||
createElement(tag, id, parent) {
|
||||
const element = document.createElement(tag);
|
||||
element.id = id;
|
||||
element.parent = parent;
|
||||
return element;
|
||||
}
|
||||
createTextElement(tag, text) {
|
||||
const element = document.createElement(tag);
|
||||
element.appendChild(document.createTextNode(text));
|
||||
return element;
|
||||
}
|
||||
getJSON(url, fn) {
|
||||
fetch(url, {cache: "no-store"})
|
||||
.then(async res => {
|
||||
if (res.ok) {
|
||||
fn(await res.json());
|
||||
}
|
||||
});
|
||||
}
|
||||
getUrlParam(query, def) {
|
||||
const url = window.location.search.substring(1);
|
||||
const vars = url.split('&');
|
||||
for (let i = 0; i < vars.length; i++) {
|
||||
const param = vars[i].split('=');
|
||||
if (param[0] === query) {
|
||||
const value = param[1] === undefined ? '' : decodeURIComponent(param[1]);
|
||||
return value === '' ? def : value;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
getUrlFromView() {
|
||||
const center = this.toPoint(this.map.getCenter());
|
||||
const zoom = this.map.getZoom();
|
||||
const x = Math.floor(center.x);
|
||||
const z = Math.floor(center.y);
|
||||
const following = this.playerList.following ? `&uuid=${this.playerList.following}` : '';
|
||||
let link = `?world=${this.worldList.curWorld.name}&zoom=${zoom}&x=${x}&z=${z}${following}`;
|
||||
if (!this.showControls) {
|
||||
link += "&show_controls=false"
|
||||
}
|
||||
if (!this.uiLink.showLinkButton) {
|
||||
link += "&show_link_button=false"
|
||||
}
|
||||
if (!this.coordinates.showCoordinates) {
|
||||
link += "&show_coordinates=false"
|
||||
}
|
||||
if (!this.sidebar.showSidebar) {
|
||||
link += "&show_sidebar=false"
|
||||
}
|
||||
return link
|
||||
}
|
||||
updateBrowserUrl(url) {
|
||||
window.history.replaceState(null, "", url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const P = new SquaremapMap();
|
||||
|
||||
|
||||
// https://stackoverflow.com/a/3955096
|
||||
Array.prototype.remove = function() {
|
||||
var what, a = arguments, L = a.length, ax;
|
||||
while (L && this.length) {
|
||||
what = a[--L];
|
||||
while ((ax = this.indexOf(what)) !== -1) {
|
||||
this.splice(ax, 1);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
43
main/squaremap/web/js/modules/SquaremapTileLayer.js
Normal file
@ -0,0 +1,43 @@
|
||||
export var SquaremapTileLayer = L.TileLayer.extend({
|
||||
|
||||
// @method createTile(coords: Object, done?: Function): HTMLElement
|
||||
// Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
|
||||
// to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
|
||||
// callback is called when the tile has been loaded.
|
||||
createTile: function (coords, done) {
|
||||
var tile = document.createElement('img');
|
||||
|
||||
L.DomEvent.on(tile, 'load', () => {
|
||||
//Once image has loaded revoke the object URL as we don't need it anymore
|
||||
URL.revokeObjectURL(tile.src);
|
||||
this._tileOnLoad(done, tile)
|
||||
});
|
||||
L.DomEvent.on(tile, 'error', L.Util.bind(this._tileOnError, this, done, tile));
|
||||
|
||||
if (this.options.crossOrigin || this.options.crossOrigin === '') {
|
||||
tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
|
||||
}
|
||||
|
||||
tile.alt = '';
|
||||
tile.setAttribute('role', 'presentation');
|
||||
|
||||
//Retrieve image via a fetch instead of just setting the src
|
||||
//This works around the fact that browsers usually don't make a request for an image that was previously loaded,
|
||||
//without resorting to changing the URL (which would break caching).
|
||||
fetch(this.getTileUrl(coords))
|
||||
.then(res => {
|
||||
//Call leaflet's error handler if request fails for some reason
|
||||
if (!res.ok) {
|
||||
this._tileOnError(done, tile, null);
|
||||
return;
|
||||
}
|
||||
|
||||
//Get image data and convert into object URL so it can be used as a src
|
||||
//Leaflet's onload listener will take it from here
|
||||
res.blob().then(blob => tile.src = URL.createObjectURL(blob));
|
||||
})
|
||||
.catch(() => this._tileOnError(done, tile, null));
|
||||
|
||||
return tile;
|
||||
}
|
||||
});
|
44
main/squaremap/web/js/modules/UICoordinates.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { P } from './Squaremap.js';
|
||||
|
||||
class UICoordinates {
|
||||
constructor(json, show) {
|
||||
const Coords = L.Control.extend({
|
||||
_container: null,
|
||||
options: {
|
||||
position: 'bottomleft'
|
||||
},
|
||||
onAdd: function () {
|
||||
const coords = L.DomUtil.create('div', 'leaflet-control-layers coordinates');
|
||||
this._coords = coords;
|
||||
if (!show) {
|
||||
this._coords.style.display = "none";
|
||||
}
|
||||
return coords;
|
||||
},
|
||||
update: function (html, point) {
|
||||
this.x = point == null ? "---" : Math.floor(point.x);
|
||||
this.z = point == null ? "---" : Math.floor(point.y);
|
||||
if (html != null) {
|
||||
this._coords.innerHTML = html
|
||||
.replace(/{x}/g, this.x)
|
||||
.replace(/{z}/g, this.z);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.showCoordinates = show;
|
||||
this.html = json.html == null ? "undefined" : json.html;
|
||||
this.coords = new Coords();
|
||||
P.map.addControl(this.coords)
|
||||
.addEventListener('mousemove', (event) => {
|
||||
if (P.worldList.curWorld != null) {
|
||||
this.coords.update(this.html, P.toPoint(event.latlng));
|
||||
}
|
||||
});
|
||||
if (!json.enabled) {
|
||||
this.coords._coords.style.display = "none";
|
||||
}
|
||||
this.coords.update(this.html);
|
||||
}
|
||||
}
|
||||
|
||||
export { UICoordinates };
|
39
main/squaremap/web/js/modules/UILink.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { P } from './Squaremap.js';
|
||||
|
||||
class UILink {
|
||||
constructor(json, show) {
|
||||
const Link = L.Control.extend({
|
||||
_container: null,
|
||||
options: {
|
||||
position: 'bottomleft'
|
||||
},
|
||||
onAdd: function () {
|
||||
const link = L.DomUtil.create('div', 'leaflet-control-layers link');
|
||||
this._link = link;
|
||||
if (!show) {
|
||||
this._link.style.display = "none";
|
||||
}
|
||||
this.update();
|
||||
return link;
|
||||
},
|
||||
update: function() {
|
||||
const url = P.worldList.curWorld == null ? "" : P.getUrlFromView();
|
||||
//P.updateBrowserUrl(url); // this spams browser history
|
||||
this._link.innerHTML = `<a href='${url}'><img src='images/clear.png'/></a>`;
|
||||
}
|
||||
});
|
||||
this.showLinkButton = show;
|
||||
this.link = new Link();
|
||||
P.map.addControl(this.link)
|
||||
.addEventListener('move', () => this.update())
|
||||
.addEventListener('zoom', () => this.update());
|
||||
if (!json.enabled) {
|
||||
this.link._link.style.display = "none";
|
||||
}
|
||||
}
|
||||
update() {
|
||||
this.link.update();
|
||||
}
|
||||
}
|
||||
|
||||
export { UILink };
|
97
main/squaremap/web/js/modules/WorldList.js
Normal file
@ -0,0 +1,97 @@
|
||||
import { World } from "./util/World.js";
|
||||
import { P } from './Squaremap.js';
|
||||
|
||||
class WorldList {
|
||||
constructor(json) {
|
||||
// get worlds from json
|
||||
const unorderedMap = new Map();
|
||||
for (let i = 0; i < json.length; i++) {
|
||||
const world = new World(json[i]);
|
||||
unorderedMap.set(world.name, world);
|
||||
}
|
||||
|
||||
// sort worlds by order
|
||||
this.worlds = new Map([...unorderedMap].sort((a, b) => a[1].order - b[1].order));
|
||||
|
||||
// set up world list link elements
|
||||
for (const [name, world] of this.worlds) {
|
||||
const link = P.createElement("a", name, this);
|
||||
link.onclick = function () {
|
||||
const curWorld = this.parent.curWorld;
|
||||
if (curWorld.name == name) {
|
||||
P.centerOn(world.spawn.x, world.spawn.z, world.zoom.def)
|
||||
return;
|
||||
}
|
||||
P.playerList.clearPlayerMarkers();
|
||||
this.parent.loadWorld(name, (world) => {
|
||||
P.centerOn(world.spawn.x, world.spawn.z, world.zoom.def)
|
||||
});
|
||||
};
|
||||
|
||||
const img = document.createElement("img");
|
||||
img.src = this.getIcon(world);
|
||||
|
||||
link.appendChild(img);
|
||||
link.appendChild(P.createTextElement("span", world.display_name));
|
||||
|
||||
P.sidebar.worlds.element.appendChild(link);
|
||||
}
|
||||
}
|
||||
getIcon(world) {
|
||||
if (world.icon != null && world.icon != "") {
|
||||
return `images/icon/${world.icon}.png`;
|
||||
}
|
||||
switch (world.type) {
|
||||
case "nether":
|
||||
return "images/icon/red-cube-smol.png";
|
||||
case "the_end":
|
||||
return "images/icon/purple-cube-smol.png";
|
||||
case "normal":
|
||||
default:
|
||||
return "images/icon/green-cube-smol.png";
|
||||
}
|
||||
}
|
||||
loadInitialWorld(json, callback) {
|
||||
let updateUrl = false
|
||||
let name = P.getUrlParam("world", null)
|
||||
if (name != null) {
|
||||
const world = this.worlds.get(name);
|
||||
if (world == null) {
|
||||
updateUrl = true
|
||||
name = null;
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
name = json.worlds.sort((a, b) => a.order - b.order)[0].name
|
||||
}
|
||||
this.loadWorld(name, (a) => {
|
||||
callback(a)
|
||||
if (updateUrl) {
|
||||
P.updateBrowserUrl(`?world=${this.curWorld.name}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
loadWorld(name, callback) {
|
||||
// unload current world
|
||||
if (this.curWorld != null) {
|
||||
this.curWorld.unload();
|
||||
}
|
||||
|
||||
// load new world
|
||||
const world = this.worlds.get(name);
|
||||
this.curWorld = world;
|
||||
world.load(callback);
|
||||
}
|
||||
showWorld(world, callback) {
|
||||
if (this.curWorld.name == world) {
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.loadWorld(world, callback);
|
||||
P.updateBrowserUrl(P.getUrlFromView());
|
||||
}
|
||||
}
|
||||
|
||||
export { WorldList };
|
11
main/squaremap/web/js/modules/util/Fieldset.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { P } from '../Squaremap.js';
|
||||
|
||||
class Fieldset {
|
||||
constructor(id, title) {
|
||||
this.element = P.createElement("fieldset", id);
|
||||
this.legend = P.createTextElement("legend", title);
|
||||
this.element.appendChild(this.legend);
|
||||
}
|
||||
}
|
||||
|
||||
export { Fieldset };
|
163
main/squaremap/web/js/modules/util/Markers.js
Normal file
@ -0,0 +1,163 @@
|
||||
import { P } from '../Squaremap.js';
|
||||
|
||||
class Marker {
|
||||
constructor(opts) {
|
||||
this.opts = opts;
|
||||
this.id = this.opts.pop("id");
|
||||
this.popup = this.opts.pop("popup");
|
||||
this.popup_sticky = true;
|
||||
this.tooltip = this.opts.pop("tooltip");
|
||||
this.tooltip_sticky = true;
|
||||
}
|
||||
init() {
|
||||
if (this.popup != null) {
|
||||
if (this.popup_sticky) {
|
||||
this.marker.on('click', (e) => {
|
||||
L.popup({
|
||||
direction: this.opts.pop("tooltip_direction", "top")
|
||||
})
|
||||
.setLatLng(P.toLatLng(P.coordinates.coords.x, P.coordinates.coords.z))
|
||||
.setContent(this.popup)
|
||||
.openOn(P.map);
|
||||
});
|
||||
} else {
|
||||
this.marker.bindPopup(() => this.popup, {
|
||||
direction: this.opts.pop("tooltip_direction", "top")
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.tooltip != null) {
|
||||
this.marker.bindTooltip(() => this.tooltip, {
|
||||
direction: this.opts.pop("tooltip_direction", "top"),
|
||||
sticky: this.tooltip_sticky
|
||||
});
|
||||
}
|
||||
for (const key in this.opts) {
|
||||
this.marker.options[key] = this.opts[key];
|
||||
}
|
||||
}
|
||||
addTo(layer) {
|
||||
this.marker.remove();
|
||||
this.marker.addTo(layer);
|
||||
}
|
||||
}
|
||||
|
||||
class Options {
|
||||
constructor(json) {
|
||||
for (const prop in json) {
|
||||
this[prop] = json[prop];
|
||||
}
|
||||
}
|
||||
pop(key, def) {
|
||||
const val = this[key];
|
||||
delete this[key];
|
||||
return val == null ? def : val;
|
||||
}
|
||||
}
|
||||
|
||||
class Rectangle extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const points = this.opts.pop("points");
|
||||
this.marker = L.rectangle([P.toLatLng(points[0].x, points[0].z), P.toLatLng(points[1].x, points[1].z)]);
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
class PolyLine extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const points = this.opts.pop("points");
|
||||
const outer = [];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (Symbol.iterator in Object(points[i])) {
|
||||
const inner = [];
|
||||
for (let j = 0; j < points[i].length; j++) {
|
||||
inner.push(P.toLatLng(points[i][j].x, points[i][j].z));
|
||||
}
|
||||
outer.push(inner);
|
||||
} else {
|
||||
outer.push(P.toLatLng(points[i].x, points[i].z));
|
||||
}
|
||||
}
|
||||
this.marker = L.polyline(outer);
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
class Polygon extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const points = this.opts.pop("points");
|
||||
const outer = [];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
if (Symbol.iterator in Object(points[i])) {
|
||||
const inner = [];
|
||||
for (let j = 0; j < points[i].length; j++) {
|
||||
if (Symbol.iterator in Object(points[i][j])) {
|
||||
const inner2 = [];
|
||||
for (let k = 0; k < points[i][j].length; k++) {
|
||||
inner2.push(P.toLatLng(points[i][j][k].x, points[i][j][k].z));
|
||||
}
|
||||
inner.push(inner2);
|
||||
} else {
|
||||
inner.push(P.toLatLng(points[i][j].x, points[i][j].z));
|
||||
}
|
||||
}
|
||||
outer.push(inner);
|
||||
} else {
|
||||
outer.push(P.toLatLng(points[i].x, points[i].z));
|
||||
}
|
||||
}
|
||||
this.marker = L.polygon(outer);
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
class Circle extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const center = this.opts.pop("center");
|
||||
const radius = this.opts.pop("radius");
|
||||
this.marker = L.circle(P.toLatLng(center.x, center.z), {
|
||||
radius: P.pixelsToMeters(radius)
|
||||
});
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
class Ellipse extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const center = this.opts.pop("center");
|
||||
const radiusX = this.opts.pop("radiusX");
|
||||
const radiusZ = this.opts.pop("radiusZ");
|
||||
const tilt = 0;
|
||||
this.marker = L.ellipse(P.toLatLng(center.x, center.z), [radiusX, radiusZ], tilt);
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
class Icon extends Marker {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
const point = this.opts.pop("point");
|
||||
const size = this.opts.pop("size");
|
||||
const anchor = this.opts.pop("anchor");
|
||||
const tooltipAnchor = this.opts.pop("tooltip_anchor", L.point(0, -size.z / 2));
|
||||
this.marker = L.marker(P.toLatLng(point.x, point.z), {
|
||||
icon: L.icon({
|
||||
iconUrl: `images/icon/registered/${opts.pop("icon")}.png`,
|
||||
iconSize: [size.x, size.z],
|
||||
iconAnchor: [anchor.x, anchor.z],
|
||||
popupAnchor: [tooltipAnchor.x, tooltipAnchor.z],
|
||||
tooltipAnchor: [tooltipAnchor.x, tooltipAnchor.z]
|
||||
})
|
||||
});
|
||||
this.popup_sticky = false;
|
||||
this.tooltip_sticky = false;
|
||||
super.init();
|
||||
}
|
||||
}
|
||||
|
||||
export { Marker, Options, Rectangle, PolyLine, Polygon, Circle, Ellipse, Icon };
|
23
main/squaremap/web/js/modules/util/Pin.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { P } from '../Squaremap.js';
|
||||
|
||||
class Pin {
|
||||
constructor(def) {
|
||||
this.pinned = def;
|
||||
|
||||
this.element = P.createElement("img", "pin", this);
|
||||
|
||||
this.element.onclick = () => this.toggle();
|
||||
|
||||
this.pin(this.pinned);
|
||||
}
|
||||
toggle() {
|
||||
this.pin(!this.pinned);
|
||||
}
|
||||
pin(pin) {
|
||||
this.pinned = pin;
|
||||
this.element.className = pin ? "pinned" : "unpinned";
|
||||
this.element.src = `images/${this.element.className}.png`;
|
||||
}
|
||||
}
|
||||
|
||||
export { Pin };
|
98
main/squaremap/web/js/modules/util/Player.js
Normal file
@ -0,0 +1,98 @@
|
||||
import { P } from '../Squaremap.js';
|
||||
|
||||
class Player {
|
||||
constructor(json) {
|
||||
this.name = json.name;
|
||||
this.uuid = json.uuid;
|
||||
this.world = json.world;
|
||||
this.displayName = json.display_name !== undefined ? json.display_name : json.name;
|
||||
this.x = 0;
|
||||
this.z = 0;
|
||||
this.armor = 0;
|
||||
this.health = 20;
|
||||
this.tooltip = L.tooltip({
|
||||
permanent: true,
|
||||
direction: "right",
|
||||
offset: [10, 0],
|
||||
pane: "nameplate"
|
||||
});
|
||||
this.marker = L.marker(P.toLatLng(json.x, json.z), {
|
||||
icon: L.icon({
|
||||
iconUrl: 'images/icon/player.png',
|
||||
iconSize: [17, 16],
|
||||
iconAnchor: [8, 9],
|
||||
tooltipAnchor: [0, 0]
|
||||
}),
|
||||
rotationAngle: (180 + json.yaw)
|
||||
});
|
||||
if (P.worldList.curWorld.player_tracker.nameplates.enabled) {
|
||||
this.updateNameplate(json);
|
||||
this.marker.bindTooltip(this.tooltip);
|
||||
}
|
||||
}
|
||||
getHeadUrl() {
|
||||
return P.worldList.curWorld.player_tracker.nameplates.heads_url
|
||||
.replace(/{uuid}/g, this.uuid)
|
||||
.replace(/{name}/g, this.name);
|
||||
}
|
||||
updateNameplate(player) {
|
||||
let headImg = "";
|
||||
let armorImg = "";
|
||||
let healthImg = "";
|
||||
if (P.worldList.curWorld.player_tracker.nameplates.show_heads) {
|
||||
headImg = `<img src='${this.getHeadUrl()}' class="head" />`;
|
||||
}
|
||||
if (P.worldList.curWorld.player_tracker.nameplates.show_armor && player.armor != null) {
|
||||
armorImg = `<img src="images/armor/${Math.min(Math.max(player.armor, 0), 20)}.png" class="armor" />`;
|
||||
}
|
||||
if (P.worldList.curWorld.player_tracker.nameplates.show_health && player.health != null) {
|
||||
healthImg = `<img src="images/health/${Math.min(Math.max(player.health, 0), 20)}.png" class="health" />`;
|
||||
}
|
||||
this.tooltip.setContent(`<ul><li>${headImg}</li><li>${this.displayName}${healthImg}${armorImg}</li>`);
|
||||
}
|
||||
update(player) {
|
||||
this.x = player.x;
|
||||
this.z = player.z;
|
||||
this.world = player.world;
|
||||
this.armor = player.armor;
|
||||
this.health = player.health;
|
||||
this.displayName = player.display_name !== undefined ? player.display_name : player.name;
|
||||
const link = document.getElementById(player.uuid);
|
||||
const img = link.getElementsByTagName("img")[0];
|
||||
const span = link.getElementsByTagName("span")[0];
|
||||
if (P.worldList.curWorld.name == player.world) {
|
||||
if (P.worldList.curWorld.player_tracker.enabled) {
|
||||
this.addMarker();
|
||||
}
|
||||
const latlng = P.toLatLng(player.x, player.z);
|
||||
if (!this.marker.getLatLng().equals(latlng)) {
|
||||
this.marker.setLatLng(latlng);
|
||||
}
|
||||
const angle = 180 + player.yaw;
|
||||
if (this.marker.options.rotationAngle != angle) {
|
||||
this.marker.setRotationAngle(angle);
|
||||
}
|
||||
img.classList.remove("other-world");
|
||||
span.classList.remove("other-world");
|
||||
} else {
|
||||
this.removeMarker();
|
||||
img.classList.add("other-world");
|
||||
span.classList.add("other-world");
|
||||
}
|
||||
this.updateNameplate(player);
|
||||
}
|
||||
removeMarker() {
|
||||
this.marker.remove();
|
||||
P.playerList.markers.delete(this.uuid);
|
||||
P.map.removeLayer(this.marker);
|
||||
P.layerControl.playersLayer.removeLayer(this.marker);
|
||||
}
|
||||
addMarker() {
|
||||
if (!P.playerList.markers.has(this.uuid)) {
|
||||
this.marker.addTo(P.layerControl.playersLayer);
|
||||
P.playerList.markers.set(this.uuid, this.marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Player };
|
145
main/squaremap/web/js/modules/util/World.js
Normal file
@ -0,0 +1,145 @@
|
||||
import { Options, Rectangle, PolyLine, Polygon, Circle, Ellipse, Icon } from "./Markers.js";
|
||||
import { P } from '../Squaremap.js';
|
||||
|
||||
class World {
|
||||
constructor(json) {
|
||||
this.name = json.name;
|
||||
this.order = json.order;
|
||||
this.icon = json.icon;
|
||||
this.type = json.type;
|
||||
this.display_name = json.display_name;
|
||||
this.markerLayers = new Map();
|
||||
this.player_tracker = {};
|
||||
this.marker_update_interval = 5;
|
||||
this.tiles_update_interval = 15;
|
||||
}
|
||||
tick() {
|
||||
// refresh map tile layer
|
||||
if (P.tick_count % this.tiles_update_interval == 0) {
|
||||
P.layerControl.updateTileLayer();
|
||||
}
|
||||
// load and draw markers
|
||||
if (P.tick_count % this.marker_update_interval == 0) {
|
||||
P.getJSON(`tiles/${this.name}/markers.json`, (json) => {
|
||||
if (this === P.worldList.curWorld) {
|
||||
this.markers(json);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
unload() {
|
||||
P.playerList.clearPlayerMarkers();
|
||||
const keys = Array.from(this.markerLayers.keys());
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const layer = this.markerLayers.get(keys[i]);
|
||||
P.layerControl.controls.removeLayer(layer);
|
||||
layer.remove();
|
||||
this.markerLayers.delete(keys[i]);
|
||||
}
|
||||
}
|
||||
load(callback) {
|
||||
P.getJSON(`tiles/${this.name}/settings.json`, (json) => {
|
||||
this.player_tracker = json.player_tracker;
|
||||
this.zoom = json.zoom;
|
||||
this.spawn = json.spawn;
|
||||
this.marker_update_interval = json.marker_update_interval;
|
||||
this.tiles_update_interval = json.tiles_update_interval;
|
||||
|
||||
// set the scale for our projection calculations
|
||||
P.setScale(this.zoom.max);
|
||||
|
||||
// set center and zoom
|
||||
P.centerOn(this.spawn.x, this.spawn.z, this.zoom.def)
|
||||
.setMinZoom(0) // extra zoom out doesn't work :(
|
||||
.setMaxZoom(this.zoom.max + this.zoom.extra);
|
||||
|
||||
// update page title
|
||||
document.title = P.title
|
||||
.replace(/{world}/g, this.display_name);
|
||||
|
||||
// setup background
|
||||
document.getElementById("map").style.background = this.getBackground();
|
||||
|
||||
// setup tile layers
|
||||
P.layerControl.setupTileLayers(this);
|
||||
|
||||
// force clear player markers
|
||||
P.playerList.clearPlayerMarkers();
|
||||
|
||||
// tick now, reset counter
|
||||
P.tick_count = 0;
|
||||
P.tick();
|
||||
|
||||
// force clear player markers
|
||||
P.playerList.clearPlayerMarkers();
|
||||
|
||||
if (callback != null) {
|
||||
callback(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
getBackground() {
|
||||
switch (this.type) {
|
||||
case "nether":
|
||||
return "url('images/nether_sky.png')";
|
||||
case "the_end":
|
||||
return "url('images/end_sky.png')";
|
||||
case "normal":
|
||||
default:
|
||||
return "url('images/overworld_sky.png')";
|
||||
}
|
||||
}
|
||||
markers(json) {
|
||||
// check if json is iterable
|
||||
if (json == null || !(Symbol.iterator in Object(json))) {
|
||||
return;
|
||||
}
|
||||
// iterate layers
|
||||
for (const entry of json) {
|
||||
// check if layer exists and needs updating
|
||||
let layer = this.markerLayers.get(entry.id);
|
||||
if (layer != null) {
|
||||
if (layer.timestamp === entry.timestamp) {
|
||||
continue; // skip
|
||||
}
|
||||
// clear existing layer to rebuild
|
||||
P.layerControl.removeOverlay(layer);
|
||||
// TODO
|
||||
// implement marker tracker instead of clearing
|
||||
// to reduce possible client side lag
|
||||
}
|
||||
|
||||
// setup the layer
|
||||
layer = new L.LayerGroup();
|
||||
layer.order = entry.order;
|
||||
layer.id = entry.id;
|
||||
layer.timestamp = entry.timestamp;
|
||||
layer.setZIndex(entry.z_index);
|
||||
this.markerLayers.set(layer.id, layer);
|
||||
|
||||
// setup the layer control
|
||||
if (entry.control === true) {
|
||||
P.layerControl.addOverlay(entry.name, layer, entry.hide);
|
||||
}
|
||||
|
||||
// setup the markers
|
||||
for (const shape in entry.markers) {
|
||||
let marker;
|
||||
const opts = new Options(entry.markers[shape]);
|
||||
switch(opts.pop("type")) {
|
||||
case "rectangle": marker = new Rectangle(opts); break;
|
||||
case "polyline": marker = new PolyLine(opts); break;
|
||||
case "polygon": marker = new Polygon(opts); break;
|
||||
case "circle": marker = new Circle(opts); break;
|
||||
case "ellipse": marker = new Ellipse(opts); break;
|
||||
case "icon": marker = new Icon(opts); break;
|
||||
}
|
||||
if (marker != null) {
|
||||
marker.addTo(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { World };
|
111
main/squaremap/web/live-atlas/assets/index.1de34b3a.css
Normal file
59
main/squaremap/web/live-atlas/assets/index.a82d2095.js
Normal file
70
main/squaremap/web/live-atlas/assets/vendor.d0ab50b1.js
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
main/squaremap/web/live-atlas/favicons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
17
main/squaremap/web/live-atlas/favicons/favicon.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg viewBox="0 0 270.06 267.09" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(-.49951 -4.5791)">
|
||||
<style>
|
||||
path {
|
||||
fill: #222;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path {
|
||||
fill: #ccc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path transform="scale(.75)" d="m0.66602 9.7227v298.96c0.046934 2.8741 0.25213 2.6741 2.535 3.6641l110.65 47.987c3.8989 1.918 3.6147 1.7537 3.6147-2.3528v-297.68c-0.0605-3.8542 0.32855-4.1331-2.8087-5.4937l-110.14-47.768c-2.988-1.1299-3.8517-1.741-3.8517 2.6812zm65.271 177.53c19.088 0 34.562 15.922 34.562 35.562 2.2e-4 6.4076-1.6821 12.696-4.8691 18.201l-25.939 55.648c-1.8045 3.8714-3.0529 3.8185-5.0156-0.0742l-28.344-55.426c-0.68826-1.1762-1.3097-2.3924-1.8613-3.6426l-0.21875-0.42578 0.03711 8e-3c-1.9214-4.5044-2.9137-9.3701-2.9141-14.289 0-19.641 15.474-35.562 34.562-35.562zm0 20.625c-8.0178-6e-5 -14.518 6.6877-14.518 14.938s6.4998 14.938 14.518 14.938c8.0178 6e-5 14.518-6.6877 14.518-14.938s-6.4998-14.938-14.518-14.938z"/>
|
||||
<path transform="scale(.75)" d="m236.43 8.5914-86.609 38.801c-9.3133 4.1592-8.1971 3.54-8.1971 14.952v297.52c-0.0201 2.6943-0.30938 2.8029 2.1555 1.7437l97.364-41.839c2.3162-0.98051 2.0784-1.6006 2.0784-3.5142v-303.35c0.15129-9.1976 0.75524-7.3665-6.7914-4.3133zm-44.714 54.614c19.088 0 34.562 15.922 34.562 35.562-1.9e-4 6.4108-1.6846 12.702-4.875 18.209l-25.934 55.643c-1.8045 3.8714-3.0529 3.8185-5.0156-0.0742l-28.357-55.453c-0.6778-1.1591-1.2908-2.357-1.8359-3.5879l-0.23243-0.45508 0.0391 8e-3c-1.9214-4.5044-2.9136-9.3699-2.9141-14.289 0-19.641 15.474-35.562 34.562-35.562zm0 20.625c-8.0178-6e-5 -14.518 6.6877-14.518 14.938 0 8.2498 6.4998 14.938 14.518 14.938 8.0178 7e-5 14.518-6.6877 14.518-14.938 0-8.2498-6.4998-14.938-14.518-14.938z"/>
|
||||
<path d="m267.64 36.871-59.959-29.533c-6.555-3.2203-6.5798-4.161-6.4472 2.3579v225.51c-0.0251 3.9614 0.03 3.9662 3.1935 5.4609l62.579 29.567c3.8853 1.693 3.4322 1.9822 3.5316-1.5506v-224.87c0.0476-4.7459 0.23478-5.4796-2.8976-6.934z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
7
main/squaremap/web/live-atlas/favicons/mask.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 270.06 267.09" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(-.49951 -4.5791)">
|
||||
<path transform="scale(.75)" d="m0.66602 9.7227v298.96c0.046934 2.8741 0.25213 2.6741 2.535 3.6641l110.65 47.987c3.8989 1.918 3.6147 1.7537 3.6147-2.3528v-297.68c-0.0605-3.8542 0.32855-4.1331-2.8087-5.4937l-110.14-47.768c-2.988-1.1299-3.8517-1.741-3.8517 2.6812zm65.271 177.53c19.088 0 34.562 15.922 34.562 35.562 2.2e-4 6.4076-1.6821 12.696-4.8691 18.201l-25.939 55.648c-1.8045 3.8714-3.0529 3.8185-5.0156-0.0742l-28.344-55.426c-0.68826-1.1762-1.3097-2.3924-1.8613-3.6426l-0.21875-0.42578 0.03711 8e-3c-1.9214-4.5044-2.9137-9.3701-2.9141-14.289 0-19.641 15.474-35.562 34.562-35.562zm0 20.625c-8.0178-6e-5 -14.518 6.6877-14.518 14.938s6.4998 14.938 14.518 14.938c8.0178 6e-5 14.518-6.6877 14.518-14.938s-6.4998-14.938-14.518-14.938z"/>
|
||||
<path transform="scale(.75)" d="m236.43 8.5914-86.609 38.801c-9.3133 4.1592-8.1971 3.54-8.1971 14.952v297.52c-0.0201 2.6943-0.30938 2.8029 2.1555 1.7437l97.364-41.839c2.3162-0.98051 2.0784-1.6006 2.0784-3.5142v-303.35c0.15129-9.1976 0.75524-7.3665-6.7914-4.3133zm-44.714 54.614c19.088 0 34.562 15.922 34.562 35.562-1.9e-4 6.4108-1.6846 12.702-4.875 18.209l-25.934 55.643c-1.8045 3.8714-3.0529 3.8185-5.0156-0.0742l-28.357-55.453c-0.6778-1.1591-1.2908-2.357-1.8359-3.5879l-0.23243-0.45508 0.0391 8e-3c-1.9214-4.5044-2.9136-9.3699-2.9141-14.289 0-19.641 15.474-35.562 34.562-35.562zm0 20.625c-8.0178-6e-5 -14.518 6.6877-14.518 14.938 0 8.2498 6.4998 14.938 14.518 14.938 8.0178 7e-5 14.518-6.6877 14.518-14.938 0-8.2498-6.4998-14.938-14.518-14.938z"/>
|
||||
<path d="m267.64 36.871-59.959-29.533c-6.555-3.2203-6.5798-4.161-6.4472 2.3579v225.51c-0.0251 3.9614 0.03 3.9662 3.1935 5.4609l62.579 29.567c3.8853 1.693 3.4322 1.9822 3.5316-1.5506v-224.87c0.0476-4.7459 0.23478-5.4796-2.8976-6.934z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
14
main/squaremap/web/live-atlas/favicons/site.webmanifest
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Minecraft Dynamic Map",
|
||||
"short_name": "Minecraft Dynamic Map",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#222222",
|
||||
"background_color": "#222222",
|
||||
"display": "standalone"
|
||||
}
|
BIN
main/squaremap/web/tiles/minecraft_overworld/0/-1_-1.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/0/-1_0.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/0/-2_0.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/0/-3_0.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/0/0_-1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/0/0_0.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/1/-1_-1.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/1/-1_0.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/1/-2_0.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
main/squaremap/web/tiles/minecraft_overworld/1/-3_0.png
Normal file
After Width: | Height: | Size: 8.6 KiB |