// ==UserScript==
// @name Advanced Geolocation Manager
// @namespace https://github.com/kaerez/JSMonkey
// @version 4.6
// @description Multi-profile async geolocation manager. Features file I/O, permission spoofing, and integrated diagnostics.
// @author EK
// @match *://*/*
// @supportURL https://github.com/kaerez/JSMonkey
// @downloadURL https://raw.githubusercontent.com/kaerez/JSMonkey/main/GeoManager/advanced-geo-manager.user.js
// @updateURL https://raw.githubusercontent.com/kaerez/JSMonkey/main/GeoManager/advanced-geo-manager.user.js
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// @license AGPL-3.0-or-later
// @copyright 2026, kaerez (https://openuserjs.org/users/kaerez)
// ==/UserScript==
(function() {
'use strict';
const STORE_KEY = 'GeoManagerProfiles_v4';
const targetWindow = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
// --- Native Hardware Capture ---
const nativeGeolocation = targetWindow.navigator.geolocation;
const nativeGetCurrentPosition = nativeGeolocation ? nativeGeolocation.getCurrentPosition.bind(nativeGeolocation) : null;
const nativePermissionsQuery = targetWindow.navigator.permissions && targetWindow.navigator.permissions.query
? targetWindow.navigator.permissions.query.bind(targetWindow.navigator.permissions)
: null;
// --- Data Management ---
let profiles = GM_getValue(STORE_KEY, []);
const saveProfiles = () => GM_setValue(STORE_KEY, profiles);
// --- Matching Engine ---
const matchPattern = (url, pattern) => {
const escapeRegex = (str) => str.replace(/([.+?^=!:${}()|\[\]\/\\])/g, "\\$1");
const regexStr = "^" + pattern.split('*').map(escapeRegex).join('.*') + "$";
return new RegExp(regexStr).test(url);
};
const getActiveProfile = () => {
const currentUrl = window.location.href;
for (const profile of profiles) {
if (!profile.enabled) continue;
for (const rule of profile.rules) {
if (rule.enabled && matchPattern(currentUrl, rule.pattern)) {
return profile;
}
}
}
return null;
};
// --- Core Spoofing Logic ---
const activeProfile = getActiveProfile();
if (activeProfile) {
const c = activeProfile.coords;
const fakeGeo = {
coords: {
latitude: Number(c.latitude), longitude: Number(c.longitude),
accuracy: c.accuracy.isNull ? null : Number(c.accuracy.value),
altitude: c.altitude.isNull ? null : Number(c.altitude.value),
altitudeAccuracy: c.altitudeAccuracy.isNull ? null : Number(c.altitudeAccuracy.value),
heading: c.heading.isNull ? null : Number(c.heading.value),
speed: c.speed.isNull ? null : Number(c.speed.value)
},
timestamp: Date.now()
};
const mockGeolocation = {
getCurrentPosition: function(success, error, options) {
setTimeout(() => {
if (activeProfile.permissionState === 'denied') {
if (typeof error === 'function') error({ code: 1, message: "User denied Geolocation" });
return;
}
if (typeof success === 'function') { fakeGeo.timestamp = Date.now(); success(fakeGeo); }
}, Math.floor(Math.random() * 400) + 300);
},
watchPosition: function(success, error, options) {
setTimeout(() => {
if (activeProfile.permissionState === 'denied') {
if (typeof error === 'function') error({ code: 1, message: "User denied Geolocation" });
return;
}
if (typeof success === 'function') { fakeGeo.timestamp = Date.now(); success(fakeGeo); }
}, Math.floor(Math.random() * 400) + 300);
return Math.floor(Math.random() * 10000);
},
clearWatch: function(id) {}
};
if (targetWindow.navigator) {
Object.defineProperty(targetWindow.navigator, 'geolocation', { value: mockGeolocation, configurable: true, enumerable: true, writable: true });
}
if (targetWindow.Navigator && targetWindow.Navigator.prototype) {
Object.defineProperty(targetWindow.Navigator.prototype, 'geolocation', { get: function() { return mockGeolocation; }, configurable: true });
}
if (targetWindow.navigator.permissions && targetWindow.navigator.permissions.query) {
const originalQuery = targetWindow.navigator.permissions.query;
targetWindow.navigator.permissions.query = function(parameters) {
if (parameters && parameters.name === 'geolocation') {
return Promise.resolve({ state: activeProfile.permissionState || 'granted', onchange: null, name: 'geolocation' });
}
return originalQuery.apply(this, arguments);
};
}
}
// --- DevConsole Diagnostics Export ---
const logPositionDetails = (position) => {
const { latitude, longitude, accuracy, altitude, altitudeAccuracy, heading, speed } = position.coords;
return `Latitude: ${latitude}°Longitude: ${longitude}°Accuracy: ${accuracy} metersAltitude: ${altitude !== null ? `${altitude} meters` : 'Not available'}Altitude Accuracy: ${altitudeAccuracy !== null ? `${altitudeAccuracy} meters` : 'Not available'}Heading: ${heading !== null ? `${heading}°` : 'Not available'}Speed: ${speed !== null ? `${speed} m/s` : 'Not available'}Timestamp: ${new Date(position.timestamp).toLocaleString()}`;
};
const fetchGeoAsync = (method) => {
return new Promise((resolve) => {
if (!method) { resolve(`Error: Method unavailable `); return; }
method(
(position) => resolve(logPositionDetails(position)),
(error) => resolve(`Error (${error.code}): ${error.message}${error.code === 1 ? `⚠️ Hardware/Profile Blocked: Location access denied.
` : ''} `),
{ timeout: 10000, maximumAge: 0, enableHighAccuracy: true }
);
});
};
targetWindow.GeoSpoof = async () => {
if (!activeProfile) return "No active profile matched for this domain. Geolocation is operating natively.";
return "GeoSpoof Diagnostics running. Open the Advanced Geolocation Manager GUI to view detailed Unspoofed vs Spoofed comparisons.";
};
// --- GUI Engine (Only in top frame) ---
if (window.self === window.top) {
let isGuiBuilt = false;
let openModalFunc = null;
let ttPolicy;
const setHTML = (el, htmlStr) => {
if (window.trustedTypes && window.trustedTypes.createPolicy) {
if (!ttPolicy) {
try { ttPolicy = window.trustedTypes.createPolicy('geoSpooferPolicy_v4', { createHTML: (s) => s }); } catch (e) {}
}
el.innerHTML = ttPolicy ? ttPolicy.createHTML(htmlStr) : htmlStr;
} else {
el.innerHTML = htmlStr;
}
};
const buildGUI = () => {
if (isGuiBuilt || !document.body) return;
isGuiBuilt = true;
const shadowHost = document.createElement('div');
shadowHost.id = 'geospoof-manager-host';
document.body.appendChild(shadowHost);
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = `
:host { all: initial; font-family: system-ui, -apple-system, sans-serif; direction: ltr; text-align: left; color: #333; }
* { box-sizing: border-box; }
.modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.6); display: none; align-items: center; justify-content: center; z-index: 999999; }
.modal-content { width: 80vw; max-width: 850px; height: 80vh; background: #fff; border-radius: 8px; display: flex; flex-direction: column; box-shadow: 0 10px 30px rgba(0,0,0,0.5); overflow: hidden; }
.header { padding: 20px; background: #f8f9fa; border-bottom: 1px solid #ddd; display: flex; justify-content: space-between; align-items: center; }
.header h2 { margin: 0; font-size: 20px; }
.body { padding: 20px; overflow-y: auto; flex-grow: 1; }
.footer { padding: 15px 20px; border-top: 1px solid #ddd; background: #fafafa; display: flex; justify-content: flex-end; gap: 10px; }
button { padding: 8px 14px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; transition: opacity 0.2s; }
button:hover { opacity: 0.8; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: #0056b3; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-danger { background: #dc3545; color: white; }
.btn-secondary { background: #6c757d; color: white; }
.btn-info { background: #17a2b8; color: white; }
.btn-outline { background: transparent; border: 1px solid #ccc; color: #333; }
.profile-card { border: 1px solid #ddd; border-radius: 6px; padding: 15px; margin-bottom: 15px; background: #fff; display: flex; justify-content: space-between; align-items: center; }
.profile-info h3 { margin: 0 0 5px 0; display: flex; align-items: center; gap: 10px; }
.profile-info p { margin: 0; font-size: 13px; color: #666; }
.profile-actions { display: flex; gap: 8px; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; font-size: 14px; }
.form-control { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; }
select.form-control { background: #fff; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
.param-row { display: flex; align-items: center; gap: 10px; border: 1px solid #eee; padding: 8px; border-radius: 4px; background: #fafafa; }
.param-row label { flex: 1; margin: 0; font-weight: 500; }
.param-row .unit { width: 60px; color: #666; font-size: 13px; }
.param-row input[type="number"] { width: 100px; padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; }
.rule-row { display: flex; gap: 10px; margin-bottom: 10px; align-items: center; }
.rule-row input[type="text"] { flex-grow: 1; }
.syntax-help { font-size: 12px; color: #555; background: #e9ecef; padding: 10px; border-radius: 4px; margin-top: 10px; line-height: 1.5; }
.syntax-help code { background: #d6d8db; padding: 2px 4px; border-radius: 3px; }
.toggle-switch { display: inline-flex; align-items: center; cursor: pointer; gap: 8px; }
textarea { width: 100%; height: 300px; font-family: monospace; padding: 10px; border: 1px solid #ccc; border-radius: 4px; resize: none; }
.results-container { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.result-pane { background: #f4f4f4; padding: 15px; border-radius: 5px; border: 1px solid #ddd; line-height: 1.6; }
.result-pane h3 { margin-top: 0; padding-bottom: 8px; border-bottom: 1px solid #ddd; }
.perm-badge { margin-bottom: 15px; font-size: 13px; padding: 8px; background: #fff; border: 1px solid #ddd; border-radius: 4px; }
.text-error { color: #d9534f; line-height: 1.5; display: inline-block; }
.error-box { background:#fff3cd; color:#856404; padding:10px; border-radius:4px; font-size:13px; border: 1px solid #ffeeba; }
.context-menu { position: fixed; z-index: 999999; background: #fff; border: 1px solid #ccc; padding: 8px 12px; border-radius: 4px; box-shadow: 2px 2px 6px rgba(0,0,0,0.2); cursor: pointer; font-size: 14px; color: #333; transition: background 0.2s; }
.context-menu:hover { background: #f0f0f0; }
`;
shadowRoot.appendChild(style);
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
const content = document.createElement('div');
content.className = 'modal-content';
setHTML(content, `
`);
overlay.appendChild(content);
shadowRoot.appendChild(overlay);
const modalBody = shadowRoot.getElementById('modal-body');
const modalFooter = shadowRoot.getElementById('modal-footer');
const modalTitle = shadowRoot.getElementById('modal-title');
openModalFunc = () => { overlay.style.display = 'flex'; renderList(); };
const closeModal = () => { overlay.style.display = 'none'; };
shadowRoot.getElementById('close-btn').addEventListener('click', closeModal);
overlay.addEventListener('click', (e) => { if (e.target === overlay) closeModal(); });
const renderList = () => {
modalTitle.innerText = "Geo Manager Profiles";
setHTML(modalBody, `
+ Add Profile
📊 Run Diagnostics
Export All
Import All
`);
setHTML(modalFooter, '');
const container = shadowRoot.getElementById('profiles-container');
if (profiles.length === 0) setHTML(container, `No profiles found. Click "+ Add Profile" to begin.
`);
profiles.forEach((p, index) => {
const activeRuleCount = p.rules.filter(r => r.enabled).length;
const el = document.createElement('div');
el.className = 'profile-card';
setHTML(el, `
Edit
Export
Delete
`);
container.appendChild(el);
});
shadowRoot.getElementById('btn-add-profile').onclick = () => renderEdit(-1);
shadowRoot.getElementById('btn-diagnostics').onclick = renderDiagnostic;
shadowRoot.getElementById('btn-export-all').onclick = () => renderImportExport(null, 'export');
shadowRoot.getElementById('btn-import-all').onclick = () => renderImportExport(null, 'import');
container.querySelectorAll('.profile-toggle').forEach(chk => { chk.onchange = (e) => { profiles[e.target.dataset.idx].enabled = e.target.checked; saveProfiles(); }; });
container.querySelectorAll('.btn-edit').forEach(btn => { btn.onclick = (e) => { renderEdit(e.target.dataset.idx); }; });
container.querySelectorAll('.btn-export').forEach(btn => { btn.onclick = (e) => { renderImportExport(e.target.dataset.idx, 'export'); }; });
container.querySelectorAll('.btn-delete').forEach(btn => { btn.onclick = (e) => { profiles.splice(e.target.dataset.idx, 1); saveProfiles(); renderList(); }; });
};
const renderEdit = (index) => {
const isNew = index === -1;
const p = isNew ? {
name: "New Profile", enabled: true, permissionState: 'granted',
coords: {
latitude: 0, longitude: 0,
accuracy: { value: 50, isNull: false }, altitude: { value: 0, isNull: false },
altitudeAccuracy: { value: 0, isNull: true }, heading: { value: 0, isNull: true }, speed: { value: 0, isNull: true }
},
rules: [{ pattern: "*://*.example.com/*", enabled: true }]
} : JSON.parse(JSON.stringify(profiles[index]));
modalTitle.innerText = isNew ? "Create Profile" : "Edit Profile";
const buildParam = (id, label, unit, data) => `
${label}
Null?
${unit}
`;
setHTML(modalBody, `
Coordinates
📍 Fetch Current Real Location
${buildParam('acc', 'Accuracy', 'meters', p.coords.accuracy)}
${buildParam('alt', 'Altitude', 'meters', p.coords.altitude)}
${buildParam('altacc', 'Alt. Accuracy', 'meters', p.coords.altitudeAccuracy)}
${buildParam('head', 'Heading', '°', p.coords.heading)}
${buildParam('speed', 'Speed', 'm/s', p.coords.speed)}
`);
shadowRoot.getElementById('btn-fetch-real').onclick = (e) => {
const btn = e.target;
btn.innerText = "Fetching...";
btn.disabled = true;
if (!nativeGetCurrentPosition) {
alert("Native Geolocation method not found. Cannot fetch.");
btn.innerText = "📍 Fetch Current Real Location";
btn.disabled = false;
return;
}
nativeGetCurrentPosition(
(pos) => {
shadowRoot.getElementById('edit-lat').value = pos.coords.latitude;
shadowRoot.getElementById('edit-lng').value = pos.coords.longitude;
const setParam = (id, val) => {
const isNull = val === null;
shadowRoot.getElementById(`edit-null-${id}`).checked = isNull;
shadowRoot.getElementById(`edit-val-${id}`).disabled = isNull;
if (!isNull) shadowRoot.getElementById(`edit-val-${id}`).value = val;
};
setParam('acc', pos.coords.accuracy);
setParam('alt', pos.coords.altitude);
setParam('altacc', pos.coords.altitudeAccuracy);
setParam('head', pos.coords.heading);
setParam('speed', pos.coords.speed);
btn.innerText = "✅ Success!";
setTimeout(() => { btn.innerText = "📍 Fetch Current Real Location"; btn.disabled = false; }, 2000);
},
(err) => {
alert(`Hardware Error (${err.code}): ${err.message}\nMake sure your browser is allowing location access to this website.`);
btn.innerText = "📍 Fetch Current Real Location";
btn.disabled = false;
},
{ enableHighAccuracy: true, timeout: 10000 }
);
};
const rulesList = shadowRoot.getElementById('rules-list');
const renderRules = () => {
setHTML(rulesList, '');
p.rules.forEach((r, i) => {
const row = document.createElement('div');
row.className = 'rule-row';
setHTML(row, `
✕
`);
rulesList.appendChild(row);
});
rulesList.querySelectorAll('.rule-enable').forEach(el => { el.onchange = e => { p.rules[e.target.dataset.idx].enabled = e.target.checked; }; });
rulesList.querySelectorAll('.rule-pattern').forEach(el => { el.oninput = e => { p.rules[e.target.dataset.idx].pattern = e.target.value; }; });
rulesList.querySelectorAll('.btn-del-rule').forEach(el => { el.onclick = e => { p.rules.splice(e.target.dataset.idx, 1); renderRules(); }; });
};
renderRules();
shadowRoot.getElementById('btn-add-rule').onclick = () => { p.rules.push({ pattern: "*://*/*", enabled: true }); renderRules(); };
setHTML(modalFooter, `Cancel Save Profile `);
shadowRoot.getElementById('btn-cancel').onclick = renderList;
shadowRoot.getElementById('btn-save').onclick = () => {
const getP = (id) => ({
isNull: shadowRoot.getElementById(`edit-null-${id}`).checked,
value: shadowRoot.getElementById(`edit-val-${id}`).value
});
p.name = shadowRoot.getElementById('edit-name').value;
p.permissionState = shadowRoot.getElementById('edit-perm').value;
p.coords.latitude = shadowRoot.getElementById('edit-lat').value;
p.coords.longitude = shadowRoot.getElementById('edit-lng').value;
p.coords.accuracy = getP('acc');
p.coords.altitude = getP('alt');
p.coords.altitudeAccuracy = getP('altacc');
p.coords.heading = getP('head');
p.coords.speed = getP('speed');
if (isNew) profiles.push(p); else profiles[index] = p;
saveProfiles(); renderList();
};
};
const renderDiagnostic = async () => {
modalTitle.innerText = "Geolocation Diagnostics";
setHTML(modalBody, `
Comparing Native Hardware API vs Currently Active Spoofed Profile on this domain.
Unspoofed (Native) Loading... (Awaiting hardware)
Spoofed (Mocked) Loading...
`);
setHTML(modalFooter, `← Back to Profiles `);
shadowRoot.getElementById('btn-back').onclick = renderList;
const unspooofedPane = shadowRoot.getElementById('unspoofed-pane');
const spoofedPane = shadowRoot.getElementById('spoofed-pane');
let permStatusHtml = "";
if (nativePermissionsQuery) {
try {
const permStatus = await nativePermissionsQuery({name: 'geolocation'});
const color = permStatus.state === 'granted' ? '#28a745' : (permStatus.state === 'denied' ? '#dc3545' : '#fd7e14');
permStatusHtml = `Hardware Permission State: ${permStatus.state}
`;
} catch (e) {}
}
const nativeData = await fetchGeoAsync(nativeGetCurrentPosition);
setHTML(unspooofedPane, `Unspoofed (Native) ${permStatusHtml}${nativeData}`);
const mockedPermState = activeProfile ? activeProfile.permissionState : 'UNMATCHED (Native)';
const spoofColor = mockedPermState === 'granted' ? '#28a745' : (mockedPermState === 'denied' ? '#dc3545' : '#fd7e14');
const mockPermStatusHtml = `Mocked Permission State: ${mockedPermState}
`;
const spoofedData = await fetchGeoAsync(targetWindow.navigator.geolocation.getCurrentPosition.bind(targetWindow.navigator.geolocation));
setHTML(spoofedPane, `Spoofed (Mocked) ${mockPermStatusHtml}${spoofedData}`);
};
const renderImportExport = (index, mode) => {
const isSingle = index !== null;
modalTitle.innerText = mode === 'export' ? "Export Config" : "Import Config";
let outputStr = mode === 'export' ? JSON.stringify(isSingle ? [profiles[index]] : profiles, null, 2) : "";
setHTML(modalBody, `
${mode === 'export' ? 'Copy or download the JSON below:' : 'Paste your JSON array or upload a file to import:'}
`);
setHTML(modalFooter, `
Cancel
${mode === 'export'
? `Copy to Clipboard
Download .json `
: `
Browse File...
Apply Import `}
`);
shadowRoot.getElementById('btn-cancel').onclick = renderList;
if (mode === 'export') {
shadowRoot.getElementById('btn-copy').onclick = () => {
shadowRoot.getElementById('io-textarea').select();
document.execCommand('copy');
};
shadowRoot.getElementById('btn-download').onclick = () => {
const blob = new Blob([outputStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `geospoof_${isSingle ? 'profile' : 'all'}_${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
};
} else {
shadowRoot.getElementById('file-import').onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => { shadowRoot.getElementById('io-textarea').value = ev.target.result; };
reader.readAsText(file);
};
shadowRoot.getElementById('btn-import').onclick = () => {
try {
const input = JSON.parse(shadowRoot.getElementById('io-textarea').value);
if (!Array.isArray(input)) throw new Error("Must be an array of profiles");
profiles = [...profiles, ...input];
saveProfiles(); renderList();
} catch(e) {
shadowRoot.getElementById('io-textarea').value = "INVALID JSON FORMAT!\n\n" + e.message;
}
};
}
};
};
const executeOpenRequest = () => {
if (!isGuiBuilt) buildGUI();
if (openModalFunc) openModalFunc();
};
GM_registerMenuCommand("📍 Advanced Geolocation Manager", executeOpenRequest);
document.addEventListener('contextmenu', (e) => {
if (!isGuiBuilt) buildGUI();
const host = document.getElementById('geospoof-manager-host');
if (!host || !host.shadowRoot) return;
const shadowRoot = host.shadowRoot;
const existing = shadowRoot.getElementById('geo-context-menu');
if (existing) existing.remove();
const menu = document.createElement('div');
menu.id = 'geo-context-menu';
menu.className = 'context-menu';
setHTML(menu, "📍 Advanced Geolocation Manager");
menu.style.top = `${e.clientY}px`;
menu.style.left = `${e.clientX + 10}px`;
menu.addEventListener('click', () => { menu.remove(); executeOpenRequest(); });
shadowRoot.appendChild(menu);
const cleanup = () => menu.remove();
document.addEventListener('click', cleanup, { once: true });
document.addEventListener('scroll', cleanup, { once: true });
setTimeout(() => { if (shadowRoot.contains(menu)) menu.remove(); }, 3000);
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', buildGUI);
} else {
buildGUI();
}
}
})();