// ==UserScript==
// @name 虚拟地理位置
// @name:en Virtual Geographic Location
// @namespace https://github.com/LaLa-HaHa-Hei/
// @version 1.0.0
// @description 自定义浏览器中的地理位置,在菜单中点击即可启用
// @author 代码见三
// @include *
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// ==/UserScript==
GM_registerMenuCommand("启动虚拟地理位置",
function () {
'use strict';
// 防止重复注入
if (document.querySelector('#VGL-popup-window') !== null) {
alert("已启动虚拟地理位置,请勿重复启用")
return
}
injectPopupWindow()
injectPopupWindowCSS()
let accuracyInput = null
let latitudeInput = null
let longitudeInput = null
setTimeout(() => {
accuracyInput = document.querySelector("#VGL-accuracy-input")
latitudeInput = document.querySelector("#VGL-latitude-input")
longitudeInput = document.querySelector("#VGL-longitude-input")
setPopupWindowDraggable()
initInputs()
bindEvent()
}, 0);
// 注入弹窗
function injectPopupWindow() {
const injectedHTML = `
`
document.body.insertAdjacentHTML('beforeend', injectedHTML)
}
// 注入弹窗的css
function injectPopupWindowCSS() {
const injectedCSS = `
#VGL-popup-window {
border: 1px solid #ccc;
position: absolute;
bottom: 10px;
left: 10px;
background-color: #fff;
z-index: 100;
color: black;
border-radius: 10px;
overflow: hidden;
}
#VGL-popup-window-header {
height: 30px;
line-height: 30px;
background-color: #409EFF;
font-size: 18px;
font-weight: 500;
color: #fff;
cursor: move;
user-select: none;
text-align: center;
}
#VGL-popup-window-main {
padding: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
#VGL-popup-window-main label {
display: inline;
}
#VGL-popup-window-main input {
height: 25px;
width: 150px;
border: 1px solid #ccc;
}
#VGL-confirm-button {
width: 60px;
height: 30px;
background-color: #409EFF;
color: #fff;
font-size: 14px;
font-weight: 500;
border: none;
cursor: pointer;
border-radius: 10px;
}
`
const style = document.createElement('style')
style.textContent = injectedCSS
document.head.appendChild(style)
}
// 实现窗口可拖动功能
function setPopupWindowDraggable() {
const header = document.getElementById("VGL-popup-window-header")
const popupWindow = document.getElementById("VGL-popup-window")
let isMouseDown = false
let offsetX = 0
let offsetY = 0
header.addEventListener("mousedown", (event) => {
isMouseDown = true
offsetX = event.clientX - popupWindow.offsetLeft
offsetY = popupWindow.offsetHeight - (event.clientY - popupWindow.offsetTop)
})
document.addEventListener("mousemove", (event) => {
if (isMouseDown) {
const x = event.clientX - offsetX
const y = window.innerHeight - (event.clientY + offsetY)
popupWindow.style.left = `${x}px`
popupWindow.style.bottom = `${y}px`
}
});
document.addEventListener("mouseup", () => {
isMouseDown = false;
});
}
// 初始化输入框的值
function initInputs() {
const savedAccuracy = GM_getValue("VGL-accuracy", 20000)
const savedLatitude = GM_getValue("VGL-latitude", 0)
const savedLongitude = GM_getValue("VGL-longitude", 0)
accuracyInput.value = savedAccuracy
latitudeInput.value = savedLatitude
longitudeInput.value = savedLongitude
}
// 保存输入框的值
function saveInputs() {
GM_setValue("VGL-accuracy", parseFloat(accuracyInput.value))
GM_setValue("VGL-latitude", parseFloat(latitudeInput.value))
GM_setValue("VGL-longitude", parseFloat(longitudeInput.value))
}
// 绑定事件
function bindEvent() {
document.querySelector("#VGL-confirm-button").addEventListener("click", (event) => {
saveInputs()
// 覆盖原本的API navigator.geolocation.getCurrentPosition 方法,使其返回自定义的位置数据
navigator.geolocation.getCurrentPosition = function (successCallback, errorCallback, options) {
let positionPromise = new Promise((resolve, reject) => {
// 自定义的位置数据
let fakePosition = {
coords: {
accuracy: parseFloat(accuracyInput.value),
altitude: null,
altitudeAccuracy: null,
'latitude': parseFloat(latitudeInput.value),
'longitude': parseFloat(longitudeInput.value),
heading: null,
speed: null
},
timestamp: Date.now()
}
console.log(fakePosition)
resolve(fakePosition)
});
// 处理 Promise 的结果
positionPromise.then((position) => {
if (successCallback) {
successCallback(position)
}
}).catch((error) => {
if (errorCallback) {
errorCallback(error)
}
});
}
alert("已设定虚拟地理位置")
})
}
},
"h"
);