Implement link copying and url handling
This commit is contained in:
parent
a0326b00d1
commit
9e28e695ee
44
package-lock.json
generated
44
package-lock.json
generated
@ -1317,6 +1317,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/clipboard": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz",
|
||||
"integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
@ -3929,6 +3935,17 @@
|
||||
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||
"dev": true
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
|
||||
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"clipboardy": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||
@ -4921,6 +4938,12 @@
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
},
|
||||
"delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||
"dev": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
@ -6616,6 +6639,15 @@
|
||||
"slash": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
@ -10528,6 +10560,12 @@
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||
"dev": true
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@ -11846,6 +11884,12 @@
|
||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"dev": true
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
|
@ -12,6 +12,7 @@
|
||||
"vue": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/clipboard": "^2.0.1",
|
||||
"@types/leaflet": "^1.5.19",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
@ -22,6 +23,7 @@
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/eslint-config-typescript": "^5.0.2",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"clipboard": "^2.0.6",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-plugin-vue": "^7.0.0-0",
|
||||
"leaflet": "^1.7.1",
|
||||
|
22
src/App.vue
22
src/App.vue
@ -9,6 +9,8 @@ import Map from './components/Map.vue';
|
||||
import Sidebar from './components/Sidebar.vue';
|
||||
import {useStore} from "./store";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import Util from '@/util';
|
||||
import {MutationTypes} from "@/store/mutation-types";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
@ -18,9 +20,11 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
setup() {
|
||||
const store = useStore(),
|
||||
const initialUrl = window.location.hash.replace('#', ''),
|
||||
store = useStore(),
|
||||
updateInterval = computed(() => store.state.configuration.updateInterval),
|
||||
title = computed(() => store.state.configuration.title),
|
||||
currentUrl = computed(() => store.getters.url),
|
||||
updatesEnabled = ref(false),
|
||||
updateTimeout = ref(0),
|
||||
|
||||
@ -52,12 +56,28 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
updateTimeout.value = 0;
|
||||
},
|
||||
|
||||
parseUrl = () => {
|
||||
if(!initialUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = Util.parseMapHash(initialUrl);
|
||||
store.commit(MutationTypes.SET_PARSED_URL, result);
|
||||
} catch(e) {
|
||||
console.warn('Ignoring invalid url ' + e);
|
||||
}
|
||||
};
|
||||
|
||||
watch(title, (title) => document.title = title);
|
||||
watch(currentUrl, (url) => window.history.replaceState({}, '', url));
|
||||
|
||||
onMounted(() => loadConfiguration());
|
||||
onBeforeUnmount(() => stopUpdates());
|
||||
|
||||
parseUrl();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -12,8 +12,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, computed} from "@vue/runtime-core";
|
||||
import {LatLng, CRS} from 'leaflet';
|
||||
import {computed, defineComponent} from "@vue/runtime-core";
|
||||
import {CRS, LatLng} from 'leaflet';
|
||||
import {useStore} from '@/store';
|
||||
import MapLayer from "@/components/map/layer/MapLayer.vue";
|
||||
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
|
||||
@ -23,7 +23,7 @@ import ClockControl from "@/components/map/control/ClockControl.vue";
|
||||
import LinkControl from "@/components/map/control/LinkControl.vue";
|
||||
import LogoControl from "@/components/map/control/LogoControl.vue";
|
||||
import {MutationTypes} from "@/store/mutation-types";
|
||||
import {DynmapPlayer} from "@/dynmap";
|
||||
import {Coordinate, DynmapPlayer} from "@/dynmap";
|
||||
import {ActionTypes} from "@/store/action-types";
|
||||
import DynmapMap from "@/leaflet/DynmapMap";
|
||||
|
||||
@ -85,9 +85,36 @@ export default defineComponent({
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
currentWorld(newValue) {
|
||||
currentWorld(newValue, oldValue) {
|
||||
const store = useStore();
|
||||
|
||||
if(newValue) {
|
||||
useStore().dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
||||
let location: Coordinate;
|
||||
let zoom: number;
|
||||
|
||||
store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
||||
|
||||
if(oldValue || !store.state.parsedUrl.location) {
|
||||
location = newValue.center;
|
||||
} else {
|
||||
location = store.state.parsedUrl.location;
|
||||
}
|
||||
|
||||
if(!oldValue) {
|
||||
zoom = store.state.parsedUrl.zoom || store.state.configuration.defaultZoom;
|
||||
}
|
||||
|
||||
//Delay the pan a frame, to allow the projection to be updated by the new world
|
||||
requestAnimationFrame(() => {
|
||||
this.leaflet!.panTo(this.currentProjection.locationToLatLng(location), {
|
||||
animate: false,
|
||||
noMoveStart: true,
|
||||
});
|
||||
|
||||
this.leaflet!.setZoom(zoom, {
|
||||
animate: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
configuration: {
|
||||
@ -100,7 +127,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@ -119,16 +146,12 @@ export default defineComponent({
|
||||
}));
|
||||
|
||||
this.leaflet.on('moveend', () => {
|
||||
const location = this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64),
|
||||
locationString = `${Math.round(location.x)},${Math.round(location.y)},${Math.round(location.z)}`,
|
||||
url = `#${this.currentWorld!.name};${this.currentMap!.name};${locationString}`;
|
||||
useStore().commit(MutationTypes.SET_CURRENT_LOCATION, this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64));
|
||||
});
|
||||
|
||||
window.history.replaceState({
|
||||
location,
|
||||
world: this.currentWorld!.name,
|
||||
map: this.currentMap!.name,
|
||||
}, '', url);
|
||||
})
|
||||
this.leaflet.on('zoomend', () => {
|
||||
useStore().commit(MutationTypes.SET_CURRENT_ZOOM, this.leaflet!.getZoom());
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {defineComponent, onMounted, onUnmounted, computed, watch} from "@vue/runtime-core";
|
||||
import {defineComponent, onUnmounted, computed, watch} from "@vue/runtime-core";
|
||||
import {DynmapWorldMap} from "@/dynmap";
|
||||
import {Map} from 'leaflet';
|
||||
import {useStore} from "@/store";
|
||||
@ -36,12 +36,9 @@ export default defineComponent({
|
||||
active = computed(() => props.map === store.state.currentMap),
|
||||
|
||||
enableLayer = () => {
|
||||
console.log('Set current projection');
|
||||
useStore().commit(MutationTypes.SET_CURRENT_PROJECTION, layer.getProjection());
|
||||
props.leaflet.addLayer(layer);
|
||||
props.leaflet.panTo(layer.getProjection().locationToLatLng(props.map.world.center), {
|
||||
noMoveStart: true,
|
||||
animate: false,
|
||||
});
|
||||
|
||||
stopUpdateWatch = watch(pendingUpdates, (newValue, oldValue) => {
|
||||
if(newValue && !oldValue && !updateFrame) {
|
||||
@ -75,13 +72,11 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
||||
|
||||
onMounted(() => {
|
||||
if(active.value) {
|
||||
enableLayer();
|
||||
}
|
||||
});
|
||||
|
||||
watch(active, (newValue) => newValue ? enableLayer() : disableLayer());
|
||||
|
||||
onUnmounted(() => {
|
||||
disableLayer();
|
||||
|
7
src/dynmap.d.ts
vendored
7
src/dynmap.d.ts
vendored
@ -251,3 +251,10 @@ interface DynmapTileUpdate {
|
||||
name: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
interface DynmapParsedUrl {
|
||||
world?: string;
|
||||
map?: string;
|
||||
location?: Coordinate;
|
||||
zoom?: number;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import {Control, ControlOptions, DomUtil, Map} from 'leaflet';
|
||||
import {useStore} from "@/store";
|
||||
import linkIcon from '@/assets/icons/link.svg';
|
||||
import ClipboardJS from 'clipboard';
|
||||
|
||||
export class LinkControl extends Control {
|
||||
// @ts-ignore
|
||||
@ -22,9 +23,8 @@ export class LinkControl extends Control {
|
||||
<use xlink:href="${linkIcon.url}" />
|
||||
</svg>`;
|
||||
|
||||
linkButton.addEventListener('click', () => {
|
||||
const projection = useStore().state.currentProjection;
|
||||
console.log(projection.latLngToLocation(this._map!.getCenter(), 64));
|
||||
new ClipboardJS(linkButton, {
|
||||
text: () => window.location.href.split("#")[0] + useStore().getters.url,
|
||||
});
|
||||
|
||||
return linkButton;
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
DynmapConfigurationResponse, DynmapLineUpdate,
|
||||
DynmapMarkerSet,
|
||||
DynmapMarkerUpdate,
|
||||
DynmapPlayer, DynmapServerConfig, DynmapTileUpdate,
|
||||
DynmapUpdateResponse
|
||||
DynmapPlayer, DynmapTileUpdate,
|
||||
DynmapUpdateResponse, DynmapWorld
|
||||
} from "@/dynmap";
|
||||
|
||||
type AugmentedActionContext = {
|
||||
@ -57,17 +57,52 @@ export interface Actions {
|
||||
}
|
||||
|
||||
export const actions: ActionTree<State, State> & Actions = {
|
||||
[ActionTypes.LOAD_CONFIGURATION]({commit}): Promise<DynmapConfigurationResponse> {
|
||||
[ActionTypes.LOAD_CONFIGURATION]({commit, state}): Promise<DynmapConfigurationResponse> {
|
||||
return API.getConfiguration().then(config => {
|
||||
commit(MutationTypes.SET_CONFIGURATION, config.config);
|
||||
commit(MutationTypes.SET_MESSAGES, config.messages);
|
||||
commit(MutationTypes.SET_WORLDS, config.worlds);
|
||||
commit(MutationTypes.SET_COMPONENTS, config.components);
|
||||
|
||||
if(config.config.defaultWorld && config.config.defaultMap) {
|
||||
let worldName, mapName;
|
||||
|
||||
// Use config default world if it exists
|
||||
if(config.config.defaultWorld && state.worlds.has(config.config.defaultWorld)) {
|
||||
worldName = config.config.defaultWorld;
|
||||
}
|
||||
|
||||
// Prefer world from parsed url if present and it exists
|
||||
if(state.parsedUrl.world && state.worlds.has(state.parsedUrl.world)) {
|
||||
worldName = state.parsedUrl.world;
|
||||
}
|
||||
|
||||
// Use first world, if any, if neither of the above exist
|
||||
if(!worldName) {
|
||||
worldName = state.worlds.size ? state.worlds.entries().next().value.name : undefined;
|
||||
}
|
||||
|
||||
if(worldName) {
|
||||
const world = state.worlds.get(worldName) as DynmapWorld;
|
||||
|
||||
// Use config default map if it exists
|
||||
if(config.config.defaultMap && world.maps.has(config.config.defaultMap)) {
|
||||
mapName = config.config.defaultMap;
|
||||
}
|
||||
|
||||
// Prefer map from parsed url if present and it exists
|
||||
if(state.parsedUrl.map && world.maps.has(state.parsedUrl.map)) {
|
||||
mapName = state.parsedUrl.map;
|
||||
}
|
||||
|
||||
// Use first map, if any, if neither of the above exist
|
||||
if(!mapName) {
|
||||
mapName = world.maps.size ? world.maps.entries().next().value.name : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if(worldName && mapName) {
|
||||
commit(MutationTypes.SET_CURRENT_MAP, {
|
||||
worldName: config.config.defaultWorld,
|
||||
mapName: config.config.defaultMap
|
||||
worldName, mapName
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ export type Getters = {
|
||||
clockControlEnabled(state: State): boolean;
|
||||
night(state: State): boolean;
|
||||
mapBackground(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||
url(state: State, getters: GetterTree<State, State> & Getters): string;
|
||||
}
|
||||
|
||||
export const getters: GetterTree<State, State> & Getters = {
|
||||
@ -41,5 +42,19 @@ export const getters: GetterTree<State, State> & Getters = {
|
||||
}
|
||||
|
||||
return state.currentMap.background || 'transparent';
|
||||
},
|
||||
|
||||
url(state: State): string {
|
||||
const x = Math.round(state.currentLocation.x),
|
||||
y = Math.round(state.currentLocation.y),
|
||||
z = Math.round(state.currentLocation.z),
|
||||
locationString = `${x},${y},${z}`,
|
||||
zoom = state.currentZoom;
|
||||
|
||||
if(!state.currentWorld || !state.currentMap) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `#${state.currentWorld.name};${state.currentMap.name};${locationString};${zoom}`;
|
||||
}
|
||||
}
|
@ -20,6 +20,9 @@ export enum MutationTypes {
|
||||
SYNC_PLAYERS = 'syncPlayers',
|
||||
SET_CURRENT_MAP = 'setCurrentMap',
|
||||
SET_CURRENT_PROJECTION = 'setCurrentProjection',
|
||||
SET_CURRENT_LOCATION = 'setCurrentLocation',
|
||||
SET_CURRENT_ZOOM = 'setCurrentZoom',
|
||||
SET_PARSED_URL = 'setParsedUrl',
|
||||
FOLLOW_PLAYER = 'followPlayer',
|
||||
CLEAR_FOLLOW = 'clearFollow',
|
||||
}
|
@ -8,7 +8,7 @@ import {
|
||||
DynmapCircleUpdate,
|
||||
DynmapComponentConfig,
|
||||
DynmapLine,
|
||||
DynmapLineUpdate,
|
||||
DynmapLineUpdate, Coordinate,
|
||||
DynmapMarker,
|
||||
DynmapMarkerSet,
|
||||
DynmapMarkerSetUpdates,
|
||||
@ -17,7 +17,7 @@ import {
|
||||
DynmapPlayer,
|
||||
DynmapServerConfig, DynmapTileUpdate,
|
||||
DynmapWorld,
|
||||
DynmapWorldState
|
||||
DynmapWorldState, DynmapParsedUrl
|
||||
} from "@/dynmap";
|
||||
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
||||
|
||||
@ -49,6 +49,9 @@ export type Mutations<S = State> = {
|
||||
[MutationTypes.SYNC_PLAYERS](state: S, keep: Set<string>): void
|
||||
[MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void
|
||||
[MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void
|
||||
[MutationTypes.SET_CURRENT_LOCATION](state: S, payload: Coordinate): void
|
||||
[MutationTypes.SET_CURRENT_ZOOM](state: S, payload: number): void
|
||||
[MutationTypes.SET_PARSED_URL](state: S, payload: DynmapParsedUrl): void
|
||||
[MutationTypes.FOLLOW_PLAYER](state: S, payload: DynmapPlayer): void
|
||||
[MutationTypes.CLEAR_FOLLOW](state: S, a?: void): void
|
||||
}
|
||||
@ -288,6 +291,18 @@ export const mutations: MutationTree<State> & Mutations = {
|
||||
state.currentProjection = projection;
|
||||
},
|
||||
|
||||
[MutationTypes.SET_CURRENT_LOCATION](state: State, payload: Coordinate) {
|
||||
state.currentLocation = payload;
|
||||
},
|
||||
|
||||
[MutationTypes.SET_CURRENT_ZOOM](state: State, payload: number) {
|
||||
state.currentZoom = payload;
|
||||
},
|
||||
|
||||
[MutationTypes.SET_PARSED_URL](state: State, payload: DynmapParsedUrl) {
|
||||
state.parsedUrl = payload;
|
||||
},
|
||||
|
||||
[MutationTypes.FOLLOW_PLAYER](state: State, player: DynmapPlayer) {
|
||||
state.following = player;
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
DynmapMessageConfig,
|
||||
DynmapPlayer,
|
||||
DynmapServerConfig, DynmapTileUpdate,
|
||||
DynmapWorld, DynmapWorldState
|
||||
DynmapWorld, DynmapWorldState, Coordinate, DynmapParsedUrl
|
||||
} from "@/dynmap";
|
||||
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
|
||||
|
||||
@ -26,10 +26,14 @@ export type State = {
|
||||
currentWorldState: DynmapWorldState;
|
||||
currentWorld?: DynmapWorld;
|
||||
currentMap?: DynmapWorldMap;
|
||||
currentLocation: Coordinate;
|
||||
currentZoom: number;
|
||||
currentProjection: DynmapProjection;
|
||||
|
||||
updateRequestId: number;
|
||||
updateTimestamp: Date;
|
||||
|
||||
parsedUrl: DynmapParsedUrl;
|
||||
}
|
||||
|
||||
export const state: State = {
|
||||
@ -100,6 +104,13 @@ export const state: State = {
|
||||
|
||||
currentWorld: undefined,
|
||||
currentMap: undefined,
|
||||
currentLocation: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
currentZoom: 0,
|
||||
|
||||
currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons
|
||||
currentWorldState: {
|
||||
raining: false,
|
||||
@ -109,4 +120,11 @@ export const state: State = {
|
||||
|
||||
updateRequestId: 0,
|
||||
updateTimestamp: new Date(),
|
||||
|
||||
parsedUrl: {
|
||||
world: undefined,
|
||||
map: undefined,
|
||||
location: undefined,
|
||||
zoom: undefined,
|
||||
}
|
||||
};
|
||||
|
36
src/util.ts
36
src/util.ts
@ -58,5 +58,41 @@ export default {
|
||||
return (x: number, y: number, z: number) => {
|
||||
return projection.locationToLatLng({x, y, z});
|
||||
};
|
||||
},
|
||||
|
||||
parseMapHash(hash: string) {
|
||||
const parts = hash.replace('#', '').split(';');
|
||||
|
||||
if(parts.length < 3) {
|
||||
throw new TypeError('Not enough parts');
|
||||
}
|
||||
|
||||
const world = parts[0],
|
||||
map = parts[1],
|
||||
location = parts[2].split(',').map(item => parseFloat(item)),
|
||||
zoom = parts[3] ? parseInt(parts[3]) : undefined;
|
||||
|
||||
if(location.length !== 3) {
|
||||
throw new TypeError('Location should contain exactly 3 numbers');
|
||||
}
|
||||
|
||||
if(location.filter(item => isNaN(item) || !isFinite(item)).length) {
|
||||
throw new TypeError('Invalid value in location');
|
||||
}
|
||||
|
||||
if(typeof zoom !== 'undefined' && (isNaN(zoom) || zoom < 0 || !isFinite(zoom))) {
|
||||
throw new TypeError('Invalid value for zoom');
|
||||
}
|
||||
|
||||
return {
|
||||
world,
|
||||
map,
|
||||
location: {
|
||||
x: location[0],
|
||||
y: location[1],
|
||||
z: location[2],
|
||||
},
|
||||
zoom,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user