CheatGuessr | WorldGuessr Cheat

Extremely customizable WorldGuessr cheating client. Click 3 to open the settings menu.

安装此脚本?
作者推荐脚本

您可能也喜欢CheatGuessr | GeoGuessr Cheat

安装此脚本
  1. // ==UserScript==
  2. // @name CheatGuessr | WorldGuessr Cheat
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.6
  5. // @description Extremely customizable WorldGuessr cheating client. Click 3 to open the settings menu.
  6. // @author CheatGuessr
  7. // @match https://www.worldguessr.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=worldguessr.com
  9. // @grant none
  10. // @license GNU AGPLv3
  11. // ==/UserScript==
  12. (function() {
  13. 'use strict';
  14.  
  15. if (window.location.pathname === '/banned') {
  16. const handleBannedPage = () => {
  17. const backdrop = document.createElement('div');
  18. backdrop.style.position = 'fixed';
  19. backdrop.style.top = '0';
  20. backdrop.style.left = '0';
  21. backdrop.style.right = '0';
  22. backdrop.style.bottom = '0';
  23. backdrop.style.backgroundColor = 'rgba(0,0,0,0.5)';
  24. backdrop.style.zIndex = '10000';
  25. const modal = document.createElement('div');
  26. modal.style.position = 'fixed';
  27. modal.style.top = '50%';
  28. modal.style.left = '50%';
  29. modal.style.transform = 'translate(-50%, -50%)';
  30. modal.style.backgroundColor = '#1f2937';
  31. modal.style.padding = '20px';
  32. modal.style.borderRadius = '8px';
  33. modal.style.color = 'white';
  34. modal.style.zIndex = '10001';
  35. const message = document.createElement('p');
  36. message.textContent = 'The Cheat has been detected!\nPlease Enter 10-20 random characters to bypass the anti-cheat.\n\nExample (do not use the example):\ndf89aj3n4r98nd9';
  37. message.style.margin = '0 0 15px 0';
  38. const input = document.createElement('input');
  39. input.type = 'text';
  40. input.style.width = '100%';
  41. input.style.marginBottom = '15px';
  42. input.style.padding = '8px';
  43. input.style.borderRadius = '4px';
  44. input.style.border = '1px solid #4b5563';
  45. input.style.backgroundColor = '#374151';
  46. input.style.color = 'white';
  47. const submitButton = document.createElement('button');
  48. submitButton.textContent = 'Submit';
  49. submitButton.style.padding = '8px 16px';
  50. submitButton.style.backgroundColor = '#3b82f6';
  51. submitButton.style.color = 'white';
  52. submitButton.style.border = 'none';
  53. submitButton.style.borderRadius = '4px';
  54. submitButton.style.cursor = 'pointer';
  55. submitButton.onmouseenter = () => submitButton.style.backgroundColor = '#2563eb';
  56. submitButton.onmouseleave = () => submitButton.style.backgroundColor = '#3b82f6';
  57. submitButton.onclick = () => {
  58. const chars = input.value.trim();
  59. if (chars) {
  60. if (chars === 'df89aj3n4r98nd9') {
  61. alert('You cannot use the example!');
  62. return;
  63. }
  64. const history = JSON.parse(localStorage.getItem('mapDivClassHistory') || []);
  65. if (history.includes(chars)) {
  66. alert('You cannot reuse a previous map div name!');
  67. return;
  68. }
  69. localStorage.removeItem('banned');
  70. localStorage.setItem('mapDivClass', chars);
  71. history.push(chars);
  72. localStorage.setItem('mapDivClassHistory', JSON.stringify(history));
  73. window.location.href = 'https://www.worldguessr.com/';
  74. }
  75. };
  76. modal.appendChild(message);
  77. modal.appendChild(input);
  78. modal.appendChild(submitButton);
  79. document.body.appendChild(backdrop);
  80. document.body.appendChild(modal);
  81. };
  82. handleBannedPage();
  83. return;
  84. }
  85. let googleMapsIframe = null;
  86. let lastLocation = null;
  87. let loadingIndicator = null;
  88. let dotInterval = null;
  89. let settings = null;
  90. let settingsModal = null;
  91. let mapDivClass = localStorage.getItem('mapDivClass') || 'map-div';
  92. const DEFAULT_SETTINGS = {
  93. keybinds: {
  94. toggleMap: '1',
  95. newTab: '2',
  96. settings: '3',
  97. detailedLocation: '4'
  98. },
  99. mapPosition: 'top-left',
  100. mapSize: {
  101. width: 400,
  102. height: 300
  103. },
  104. loadingPosition: 'bottom',
  105. refreshInterval: 1000,
  106. blockAds: true
  107. };
  108. const style = document.createElement('style');
  109. style.textContent = `
  110. .${mapDivClass} {
  111. position: fixed;
  112. z-index: 9999;
  113. border: 2px solid #333;
  114. border-radius: 4px;
  115. }
  116. .close-button {
  117. position: absolute;
  118. top: -15px;
  119. right: -15px;
  120. width: 30px;
  121. height: 30px;
  122. background: red;
  123. border: 2px solid white;
  124. border-radius: 50%;
  125. color: white;
  126. font-weight: bold;
  127. cursor: pointer;
  128. display: flex;
  129. align-items: center;
  130. justify-content: center;
  131. z-index: 10000;
  132. }
  133. .loading-indicator {
  134. position: fixed;
  135. left: 10px;
  136. padding: 5px 10px;
  137. background: rgba(0,0,0,0.7);
  138. color: white;
  139. border-radius: 4px;
  140. z-index: 9999;
  141. }
  142. .settings-modal {
  143. position: fixed;
  144. top: 50%;
  145. left: 50%;
  146. transform: translate(-50%, -50%);
  147. background: #1f2937;
  148. padding: 20px;
  149. border-radius: 8px;
  150. z-index: 10001;
  151. width: 600px;
  152. max-height: 80vh;
  153. overflow-y: auto;
  154. color: white;
  155. }
  156. .settings-backdrop {
  157. position: fixed;
  158. top: 0;
  159. left: 0;
  160. right: 0;
  161. bottom: 0;
  162. background: rgba(0,0,0,0.5);
  163. z-index: 10000;
  164. }
  165. .settings-grid {
  166. display: grid;
  167. grid-template-columns: 1fr 1fr;
  168. gap: 20px;
  169. }
  170. .settings-section {
  171. background: #374151;
  172. padding: 15px;
  173. border-radius: 6px;
  174. }
  175. .settings-row {
  176. margin: 10px 0;
  177. }
  178. .settings-row label {
  179. display: block;
  180. margin-bottom: 5px;
  181. color: #e5e7eb;
  182. }
  183. .settings-input {
  184. width: 100%;
  185. padding: 8px;
  186. border: 1px solid #4b5563;
  187. border-radius: 4px;
  188. background: #1f2937;
  189. color: white;
  190. }
  191. .settings-button {
  192. padding: 8px 16px;
  193. border: none;
  194. border-radius: 4px;
  195. cursor: pointer;
  196. margin: 5px;
  197. background: #3b82f6;
  198. color: white;
  199. }
  200. .settings-button:hover {
  201. background: #2563eb;
  202. }
  203. `;
  204. document.head.appendChild(style);
  205. function loadSettings() {
  206. try {
  207. const saved = localStorage.getItem('worldGuessrHelper');
  208. settings = saved ? JSON.parse(saved) : DEFAULT_SETTINGS;
  209. } catch (e) {
  210. settings = DEFAULT_SETTINGS;
  211. }
  212. }
  213. function saveSettings() {
  214. localStorage.setItem('worldGuessrHelper', JSON.stringify(settings));
  215. }
  216. function blockAds() {
  217. if (!settings.blockAds) return;
  218. const adSelectors = [
  219. '[id^="google_ads_iframe"]',
  220. '[id^="worldguessr-com_"]',
  221. '.video-ad'
  222. ];
  223. const removeAds = () => {
  224. adSelectors.forEach(selector => {
  225. document.querySelectorAll(selector).forEach(ad => {
  226. ad.remove();
  227. });
  228. });
  229. };
  230. removeAds();
  231. const observer = new MutationObserver(removeAds);
  232. observer.observe(document.body, {
  233. childList: true,
  234. subtree: true
  235. });
  236. }
  237. function createLoadingIndicator() {
  238. loadingIndicator = document.createElement('div');
  239. loadingIndicator.className = 'loading-indicator';
  240. loadingIndicator.style.display = 'none';
  241. document.body.appendChild(loadingIndicator);
  242. let dots = 0;
  243. if (dotInterval) clearInterval(dotInterval);
  244. dotInterval = setInterval(() => {
  245. dots = (dots + 1) % 4;
  246. if (loadingIndicator) {
  247. loadingIndicator.textContent = 'Loading location' + '.'.repeat(dots);
  248. }
  249. }, 500);
  250. }
  251. function toggleSettingsModal() {
  252. if (settingsModal) {
  253. settingsModal.backdrop.remove();
  254. settingsModal.modal.remove();
  255. settingsModal = null;
  256. return;
  257. }
  258. const backdrop = document.createElement('div');
  259. backdrop.className = 'settings-backdrop';
  260. const modal = document.createElement('div');
  261. modal.className = 'settings-modal';
  262. modal.innerHTML = `
  263. <h2 style="margin-bottom: 20px">WorldGuessr Helper Settings</h2>
  264. <div class="settings-grid">
  265. <div class="settings-section">
  266. <h3>Keybinds</h3>
  267. <div class="settings-row">
  268. <label>Toggle Map Key</label>
  269. <input type="text" class="settings-input" id="toggleMapKey" value="${settings.keybinds.toggleMap}">
  270. </div>
  271. <div class="settings-row">
  272. <label>New Tab Key</label>
  273. <input type="text" class="settings-input" id="newTabKey" value="${settings.keybinds.newTab}">
  274. </div>
  275. <div class="settings-row">
  276. <label>Settings Key</label>
  277. <input type="text" class="settings-input" id="settingsKey" value="${settings.keybinds.settings}">
  278. </div>
  279. <div class="settings-row">
  280. <label>Detailed Location Alert</label>
  281. <input type="text" class="settings-input" id="detailedLocation" value="${settings.keybinds.detailedLocation}">
  282. </div>
  283. </div>
  284. <div class="settings-section">
  285. <h3>Map Settings</h3>
  286. <div class="settings-row">
  287. <label>Map Position</label>
  288. <select class="settings-input" id="mapPosition">
  289. <option value="top-left" ${settings.mapPosition === 'top-left' ? 'selected' : ''}>Top Left</option>
  290. <option value="top-right" ${settings.mapPosition === 'top-right' ? 'selected' : ''}>Top Right</option>
  291. <option value="bottom-left" ${settings.mapPosition === 'bottom-left' ? 'selected' : ''}>Bottom Left</option>
  292. <option value="bottom-right" ${settings.mapPosition === 'bottom-right' ? 'selected' : ''}>Bottom Right</option>
  293. </select>
  294. </div>
  295. <div class="settings-row">
  296. <label>Map Width (px)</label>
  297. <input type="number" class="settings-input" id="mapWidth" value="${settings.mapSize.width}">
  298. </div>
  299. <div class="settings-row">
  300. <label>Map Height (px)</label>
  301. <input type="number" class="settings-input" id="mapHeight" value="${settings.mapSize.height}">
  302. </div>
  303. </div>
  304. </div>
  305. <div class="settings-section" style="margin-top: 20px">
  306. <h3>Additional Settings</h3>
  307. <div class="settings-row">
  308. <label>
  309. <input type="checkbox" id="blockAds" ${settings.blockAds ? 'checked' : ''}>
  310. Block Advertisements
  311. </label>
  312. </div>
  313. </div>
  314. <div style="text-align: right; margin-top: 20px">
  315. <button class="settings-button" id="closeSettings">Cancel</button>
  316. <button class="settings-button" id="saveSettings">Save</button>
  317. </div>
  318. `;
  319. document.body.appendChild(backdrop);
  320. document.body.appendChild(modal);
  321. settingsModal = { backdrop, modal };
  322. document.getElementById('saveSettings').onclick = () => {
  323. settings.keybinds.toggleMap = document.getElementById('toggleMapKey').value;
  324. settings.keybinds.newTab = document.getElementById('newTabKey').value;
  325. settings.keybinds.settings = document.getElementById('settingsKey').value;
  326. settings.keybinds.detailedLocation = document.getElementById('detailedLocation').value;
  327. settings.mapPosition = document.getElementById('mapPosition').value;
  328. settings.mapSize.width = parseInt(document.getElementById('mapWidth').value);
  329. settings.mapSize.height = parseInt(document.getElementById('mapHeight').value);
  330. settings.blockAds = document.getElementById('blockAds').checked;
  331. saveSettings();
  332. blockAds();
  333. toggleSettingsModal();
  334. };
  335. document.getElementById('closeSettings').onclick = toggleSettingsModal;
  336. }
  337. function showLoadingIndicator() {
  338. if (loadingIndicator) {
  339. loadingIndicator.style.display = 'block';
  340. loadingIndicator.style.bottom = settings.loadingPosition === 'bottom' ? '10px' : 'auto';
  341. loadingIndicator.style.top = settings.loadingPosition === 'top' ? '10px' : 'auto';
  342. }
  343. }
  344. function hideLoadingIndicator() {
  345. if (loadingIndicator) {
  346. loadingIndicator.style.display = 'none';
  347. }
  348. }
  349. function extractLocationFromIframe() {
  350. showLoadingIndicator();
  351. const iframe = document.querySelector('iframe[src^="/svEmbed"]');
  352. if (!iframe) {
  353. hideLoadingIndicator();
  354. return null;
  355. }
  356. const urlParams = new URLSearchParams(iframe.src.split('?')[1]);
  357. const lat = parseFloat(urlParams.get('lat'));
  358. const long = parseFloat(urlParams.get('long'));
  359. if (!isNaN(lat) && !isNaN(long)) {
  360. hideLoadingIndicator();
  361. return { lat, long, timestamp: new Date() };
  362. }
  363. hideLoadingIndicator();
  364. return null;
  365. }
  366. function updateMapPosition() {
  367. if (!googleMapsIframe) return;
  368. const pos = settings.mapPosition.split('-');
  369. googleMapsIframe.style.top = pos[0] === 'top' ? '10px' : 'auto';
  370. googleMapsIframe.style.bottom = pos[0] === 'bottom' ? '10px' : 'auto';
  371. googleMapsIframe.style.left = pos[1] === 'left' ? '10px' : 'auto';
  372. googleMapsIframe.style.right = pos[1] === 'right' ? '10px' : 'auto';
  373. googleMapsIframe.style.width = settings.mapSize.width + 'px';
  374. googleMapsIframe.style.height = settings.mapSize.height + 'px';
  375. }
  376. function toggleGoogleMapsIframe(location) {
  377. if (!location) return;
  378. if (googleMapsIframe) {
  379. googleMapsIframe.remove();
  380. googleMapsIframe = null;
  381. return;
  382. }
  383. try {
  384. const container = document.createElement('div');
  385. container.className = mapDivClass;
  386. const closeBtn = document.createElement('button');
  387. closeBtn.className = 'close-button';
  388. closeBtn.textContent = '×';// ==UserScript==
  389. // @name CheatGuessr | WorldGuessr Cheat
  390. // @namespace http://tampermonkey.net/
  391. // @version 1.5
  392. // @description Extremely customizable WorldGuessr cheating client. Click 3 to open the settings menu.
  393. // @author CheatGuessr
  394. // @match https://www.worldguessr.com/*
  395. // @icon https://www.google.com/s2/favicons?sz=64&domain=worldguessr.com
  396. // @grant none
  397. // @license GNU AGPLv3
  398. // ==/UserScript==
  399. (function() {
  400. 'use strict';
  401.  
  402. if (window.location.pathname === '/banned') {
  403. const handleBannedPage = () => {
  404. const backdrop = document.createElement('div');
  405. backdrop.style.position = 'fixed';
  406. backdrop.style.top = '0';
  407. backdrop.style.left = '0';
  408. backdrop.style.right = '0';
  409. backdrop.style.bottom = '0';
  410. backdrop.style.backgroundColor = 'rgba(0,0,0,0.5)';
  411. backdrop.style.zIndex = '10000';
  412. const modal = document.createElement('div');
  413. modal.style.position = 'fixed';
  414. modal.style.top = '50%';
  415. modal.style.left = '50%';
  416. modal.style.transform = 'translate(-50%, -50%)';
  417. modal.style.backgroundColor = '#1f2937';
  418. modal.style.padding = '20px';
  419. modal.style.borderRadius = '8px';
  420. modal.style.color = 'white';
  421. modal.style.zIndex = '10001';
  422. const message = document.createElement('p');
  423. message.textContent = 'The Cheat has been detected!\nPlease Enter 10-20 random characters to bypass the anti-cheat.\n\nExample (do not use the example):\ndf89aj3n4r98nd9';
  424. message.style.margin = '0 0 15px 0';
  425. const input = document.createElement('input');
  426. input.type = 'text';
  427. input.style.width = '100%';
  428. input.style.marginBottom = '15px';
  429. input.style.padding = '8px';
  430. input.style.borderRadius = '4px';
  431. input.style.border = '1px solid #4b5563';
  432. input.style.backgroundColor = '#374151';
  433. input.style.color = 'white';
  434. const submitButton = document.createElement('button');
  435. submitButton.textContent = 'Submit';
  436. submitButton.style.padding = '8px 16px';
  437. submitButton.style.backgroundColor = '#3b82f6';
  438. submitButton.style.color = 'white';
  439. submitButton.style.border = 'none';
  440. submitButton.style.borderRadius = '4px';
  441. submitButton.style.cursor = 'pointer';
  442. submitButton.onmouseenter = () => submitButton.style.backgroundColor = '#2563eb';
  443. submitButton.onmouseleave = () => submitButton.style.backgroundColor = '#3b82f6';
  444. submitButton.onclick = () => {
  445. const chars = input.value.trim();
  446. if (chars) {
  447. if (chars === 'df89aj3n4r98nd9') {
  448. alert('You cannot use the example!');
  449. return;
  450. }
  451.  
  452. localStorage.setItem('mapDivClass', chars);
  453. const history = JSON.parse(localStorage.getItem('mapDivClassHistory') || '[]');
  454. history.push(chars);
  455. localStorage.setItem('mapDivClassHistory', JSON.stringify(history));
  456. window.location.href = 'https://www.worldguessr.com/';
  457. }
  458. };
  459. modal.appendChild(message);
  460. modal.appendChild(input);
  461. modal.appendChild(submitButton);
  462. document.body.appendChild(backdrop);
  463. document.body.appendChild(modal);
  464. };
  465. handleBannedPage();
  466. return;
  467. }
  468. let googleMapsIframe = null;
  469. let lastLocation = null;
  470. let loadingIndicator = null;
  471. let dotInterval = null;
  472. let settings = null;
  473. let settingsModal = null;
  474. let mapDivClass = localStorage.getItem('mapDivClass') || 'map-div';
  475. const DEFAULT_SETTINGS = {
  476. keybinds: {
  477. toggleMap: '1',
  478. newTab: '2',
  479. settings: '3',
  480. detailedLocation: '4'
  481. },
  482. mapPosition: 'top-left',
  483. mapSize: {
  484. width: 400,
  485. height: 300
  486. },
  487. loadingPosition: 'bottom',
  488. refreshInterval: 1000,
  489. blockAds: true
  490. };
  491. const style = document.createElement('style');
  492. style.textContent = `
  493. .${mapDivClass} {
  494. position: fixed;
  495. z-index: 9999;
  496. border: 2px solid #333;
  497. border-radius: 4px;
  498. }
  499. .close-button {
  500. position: absolute;
  501. top: -15px;
  502. right: -15px;
  503. width: 30px;
  504. height: 30px;
  505. background: red;
  506. border: 2px solid white;
  507. border-radius: 50%;
  508. color: white;
  509. font-weight: bold;
  510. cursor: pointer;
  511. display: flex;
  512. align-items: center;
  513. justify-content: center;
  514. z-index: 10000;
  515. }
  516. .loading-indicator {
  517. position: fixed;
  518. left: 10px;
  519. padding: 5px 10px;
  520. background: rgba(0,0,0,0.7);
  521. color: white;
  522. border-radius: 4px;
  523. z-index: 9999;
  524. }
  525. .settings-modal {
  526. position: fixed;
  527. top: 50%;
  528. left: 50%;
  529. transform: translate(-50%, -50%);
  530. background: #1f2937;
  531. padding: 20px;
  532. border-radius: 8px;
  533. z-index: 10001;
  534. width: 600px;
  535. max-height: 80vh;
  536. overflow-y: auto;
  537. color: white;
  538. }
  539. .settings-backdrop {
  540. position: fixed;
  541. top: 0;
  542. left: 0;
  543. right: 0;
  544. bottom: 0;
  545. background: rgba(0,0,0,0.5);
  546. z-index: 10000;
  547. }
  548. .settings-grid {
  549. display: grid;
  550. grid-template-columns: 1fr 1fr;
  551. gap: 20px;
  552. }
  553. .settings-section {
  554. background: #374151;
  555. padding: 15px;
  556. border-radius: 6px;
  557. }
  558. .settings-row {
  559. margin: 10px 0;
  560. }
  561. .settings-row label {
  562. display: block;
  563. margin-bottom: 5px;
  564. color: #e5e7eb;
  565. }
  566. .settings-input {
  567. width: 100%;
  568. padding: 8px;
  569. border: 1px solid #4b5563;
  570. border-radius: 4px;
  571. background: #1f2937;
  572. color: white;
  573. }
  574. .settings-button {
  575. padding: 8px 16px;
  576. border: none;
  577. border-radius: 4px;
  578. cursor: pointer;
  579. margin: 5px;
  580. background: #3b82f6;
  581. color: white;
  582. }
  583. .settings-button:hover {
  584. background: #2563eb;
  585. }
  586. `;
  587. document.head.appendChild(style);
  588. function loadSettings() {
  589. try {
  590. const saved = localStorage.getItem('worldGuessrHelper');
  591. settings = saved ? JSON.parse(saved) : DEFAULT_SETTINGS;
  592. } catch (e) {
  593. settings = DEFAULT_SETTINGS;
  594. }
  595. }
  596. function saveSettings() {
  597. localStorage.setItem('worldGuessrHelper', JSON.stringify(settings));
  598. }
  599. function blockAds() {
  600. if (!settings.blockAds) return;
  601. const adSelectors = [
  602. '[id^="google_ads_iframe"]',
  603. '[id^="worldguessr-com_"]',
  604. '.video-ad'
  605. ];
  606. const removeAds = () => {
  607. adSelectors.forEach(selector => {
  608. document.querySelectorAll(selector).forEach(ad => {
  609. ad.remove();
  610. });
  611. });
  612. };
  613. removeAds();
  614. const observer = new MutationObserver(removeAds);
  615. observer.observe(document.body, {
  616. childList: true,
  617. subtree: true
  618. });
  619. }
  620. function createLoadingIndicator() {
  621. loadingIndicator = document.createElement('div');
  622. loadingIndicator.className = 'loading-indicator';
  623. loadingIndicator.style.display = 'none';
  624. document.body.appendChild(loadingIndicator);
  625. let dots = 0;
  626. if (dotInterval) clearInterval(dotInterval);
  627. dotInterval = setInterval(() => {
  628. dots = (dots + 1) % 4;
  629. if (loadingIndicator) {
  630. loadingIndicator.textContent = 'Loading location' + '.'.repeat(dots);
  631. }
  632. }, 500);
  633. }
  634. function toggleSettingsModal() {
  635. if (settingsModal) {
  636. settingsModal.backdrop.remove();
  637. settingsModal.modal.remove();
  638. settingsModal = null;
  639. return;
  640. }
  641. const backdrop = document.createElement('div');
  642. backdrop.className = 'settings-backdrop';
  643. const modal = document.createElement('div');
  644. modal.className = 'settings-modal';
  645. modal.innerHTML = `
  646. <h2 style="margin-bottom: 20px">WorldGuessr Helper Settings</h2>
  647. <div class="settings-grid">
  648. <div class="settings-section">
  649. <h3>Keybinds</h3>
  650. <div class="settings-row">
  651. <label>Toggle Map Key</label>
  652. <input type="text" class="settings-input" id="toggleMapKey" value="${settings.keybinds.toggleMap}">
  653. </div>
  654. <div class="settings-row">
  655. <label>New Tab Key</label>
  656. <input type="text" class="settings-input" id="newTabKey" value="${settings.keybinds.newTab}">
  657. </div>
  658. <div class="settings-row">
  659. <label>Settings Key</label>
  660. <input type="text" class="settings-input" id="settingsKey" value="${settings.keybinds.settings}">
  661. </div>
  662. <div class="settings-row">
  663. <label>Detailed Location Alert</label>
  664. <input type="text" class="settings-input" id="detailedLocation" value="${settings.keybinds.detailedLocation}">
  665. </div>
  666. </div>
  667. <div class="settings-section">
  668. <h3>Map Settings</h3>
  669. <div class="settings-row">
  670. <label>Map Position</label>
  671. <select class="settings-input" id="mapPosition">
  672. <option value="top-left" ${settings.mapPosition === 'top-left' ? 'selected' : ''}>Top Left</option>
  673. <option value="top-right" ${settings.mapPosition === 'top-right' ? 'selected' : ''}>Top Right</option>
  674. <option value="bottom-left" ${settings.mapPosition === 'bottom-left' ? 'selected' : ''}>Bottom Left</option>
  675. <option value="bottom-right" ${settings.mapPosition === 'bottom-right' ? 'selected' : ''}>Bottom Right</option>
  676. </select>
  677. </div>
  678. <div class="settings-row">
  679. <label>Map Width (px)</label>
  680. <input type="number" class="settings-input" id="mapWidth" value="${settings.mapSize.width}">
  681. </div>
  682. <div class="settings-row">
  683. <label>Map Height (px)</label>
  684. <input type="number" class="settings-input" id="mapHeight" value="${settings.mapSize.height}">
  685. </div>
  686. </div>
  687. </div>
  688. <div class="settings-section" style="margin-top: 20px">
  689. <h3>Additional Settings</h3>
  690. <div class="settings-row">
  691. <label>
  692. <input type="checkbox" id="blockAds" ${settings.blockAds ? 'checked' : ''}>
  693. Block Advertisements
  694. </label>
  695. </div>
  696. </div>
  697. <div style="text-align: right; margin-top: 20px">
  698. <button class="settings-button" id="closeSettings">Cancel</button>
  699. <button class="settings-button" id="saveSettings">Save</button>
  700. </div>
  701. `;
  702. document.body.appendChild(backdrop);
  703. document.body.appendChild(modal);
  704. settingsModal = { backdrop, modal };
  705. document.getElementById('saveSettings').onclick = () => {
  706. settings.keybinds.toggleMap = document.getElementById('toggleMapKey').value;
  707. settings.keybinds.newTab = document.getElementById('newTabKey').value;
  708. settings.keybinds.settings = document.getElementById('settingsKey').value;
  709. settings.keybinds.detailedLocation = document.getElementById('detailedLocation').value;
  710. settings.mapPosition = document.getElementById('mapPosition').value;
  711. settings.mapSize.width = parseInt(document.getElementById('mapWidth').value);
  712. settings.mapSize.height = parseInt(document.getElementById('mapHeight').value);
  713. settings.blockAds = document.getElementById('blockAds').checked;
  714. saveSettings();
  715. blockAds();
  716. toggleSettingsModal();
  717. };
  718. document.getElementById('closeSettings').onclick = toggleSettingsModal;
  719. }
  720. function showLoadingIndicator() {
  721. if (loadingIndicator) {
  722. loadingIndicator.style.display = 'block';
  723. loadingIndicator.style.bottom = settings.loadingPosition === 'bottom' ? '10px' : 'auto';
  724. loadingIndicator.style.top = settings.loadingPosition === 'top' ? '10px' : 'auto';
  725. }
  726. }
  727. function hideLoadingIndicator() {
  728. if (loadingIndicator) {
  729. loadingIndicator.style.display = 'none';
  730. }
  731. }
  732. function extractLocationFromIframe() {
  733. showLoadingIndicator();
  734. const iframe = document.querySelector('iframe[src^="/svEmbed"]');
  735. if (!iframe) {
  736. hideLoadingIndicator();
  737. return null;
  738. }
  739. const urlParams = new URLSearchParams(iframe.src.split('?')[1]);
  740. const lat = parseFloat(urlParams.get('lat'));
  741. const long = parseFloat(urlParams.get('long'));
  742. if (!isNaN(lat) && !isNaN(long)) {
  743. hideLoadingIndicator();
  744. return { lat, long, timestamp: new Date() };
  745. }
  746. hideLoadingIndicator();
  747. return null;
  748. }
  749. function updateMapPosition() {
  750. if (!googleMapsIframe) return;
  751. const pos = settings.mapPosition.split('-');
  752. googleMapsIframe.style.top = pos[0] === 'top' ? '10px' : 'auto';
  753. googleMapsIframe.style.bottom = pos[0] === 'bottom' ? '10px' : 'auto';
  754. googleMapsIframe.style.left = pos[1] === 'left' ? '10px' : 'auto';
  755. googleMapsIframe.style.right = pos[1] === 'right' ? '10px' : 'auto';
  756. googleMapsIframe.style.width = settings.mapSize.width + 'px';
  757. googleMapsIframe.style.height = settings.mapSize.height + 'px';
  758. }
  759. function toggleGoogleMapsIframe(location) {
  760. if (!location) return;
  761. if (googleMapsIframe) {
  762. googleMapsIframe.remove();
  763. googleMapsIframe = null;
  764. return;
  765. }
  766. try {
  767. const container = document.createElement('div');
  768. container.className = mapDivClass;
  769. const closeBtn = document.createElement('button');
  770. closeBtn.className = 'close-button';
  771. closeBtn.textContent = '×';
  772. closeBtn.onclick = () => {
  773. container.remove();
  774. googleMapsIframe = null;
  775. };
  776. const iframe = document.createElement('iframe');
  777. iframe.width = '100%';
  778. iframe.height = '100%';
  779. iframe.style.border = 'none';
  780. iframe.src = "https://www.google.com/maps?q=" + location.lat + "," + location.long + "&z=18&output=embed";
  781. container.appendChild(closeBtn);
  782. container.appendChild(iframe);
  783. document.body.appendChild(container);
  784. googleMapsIframe = container;
  785. updateMapPosition();
  786. } catch (error) {
  787. console.error('Error creating iframe:', error);
  788. }
  789. }
  790. async function fetchLocationDetails(lat, long) {
  791. try {
  792. const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${long}`);
  793. if (!response.ok) {
  794. throw new Error('Failed to fetch location details');
  795. }
  796. const data = await response.json();
  797. const { address } = data;
  798. const locationDetails = `
  799. Area: ${address.neighbourhood || address.suburb || address.hamlet || 'N/A'}
  800. City: ${address.city || address.town || address.village || 'N/A'}
  801. State: ${address.state || 'N/A'}
  802. Country: ${address.country || 'N/A'}
  803. `;
  804. alert(locationDetails);
  805. } catch (error) {
  806. alert('Could not fetch location details: ' + error.message);
  807. }
  808. }
  809. window.addEventListener('keydown', function(event) {
  810. event.stopPropagation();
  811. const location = extractLocationFromIframe();
  812. if (!location) return;
  813. if (lastLocation && (lastLocation.lat !== location.lat || lastLocation.long !== location.long)) {
  814. if (googleMapsIframe) {
  815. toggleGoogleMapsIframe(location);
  816. toggleGoogleMapsIframe(location);
  817. }
  818. }
  819. lastLocation = location;
  820. if (event.key === settings.keybinds.toggleMap) {
  821. toggleGoogleMapsIframe(location);
  822. } else if (event.key === settings.keybinds.newTab) {
  823. window.open("https://www.google.com/maps?q=" + location.lat + "," + location.long, "_blank");
  824. } else if (event.key === settings.keybinds.settings) {
  825. if (googleMapsIframe) {
  826. toggleGoogleMapsIframe(location);
  827. }
  828. toggleSettingsModal();
  829. } else if (event.key === settings.keybinds.detailedLocation) {
  830. const { lat, long } = location;
  831. if (!lat || !long) {
  832. alert('Coordinates not yet available!');
  833. } else {
  834. fetchLocationDetails(lat, long);
  835. }
  836. }
  837. }, true);
  838. loadSettings();
  839. createLoadingIndicator();
  840. blockAds();
  841. setInterval(() => {
  842. const location = extractLocationFromIframe();
  843. if (!location || !lastLocation) return;
  844. if (lastLocation.lat !== location.lat || lastLocation.long !== location.long) {
  845. if (googleMapsIframe) {
  846. toggleGoogleMapsIframe(location);
  847. toggleGoogleMapsIframe(location);
  848. }
  849. lastLocation = location;
  850. }
  851. }, settings.refreshInterval);
  852. const observer = new MutationObserver(() => {
  853. if (!document.querySelector('iframe[src^="/svEmbed"]') && googleMapsIframe) {
  854. googleMapsIframe.remove();
  855. googleMapsIframe = null;
  856. }
  857. });
  858. observer.observe(document.body, { childList: true, subtree: true });
  859. })();
  860. closeBtn.onclick = () => {
  861. container.remove();
  862. googleMapsIframe = null;
  863. };
  864. const iframe = document.createElement('iframe');
  865. iframe.width = '100%';
  866. iframe.height = '100%';
  867. iframe.style.border = 'none';
  868. iframe.src = "https://www.google.com/maps?q=" + location.lat + "," + location.long + "&z=18&output=embed";
  869. container.appendChild(closeBtn);
  870. container.appendChild(iframe);
  871. document.body.appendChild(container);
  872. googleMapsIframe = container;
  873. updateMapPosition();
  874. } catch (error) {
  875. console.error('Error creating iframe:', error);
  876. }
  877. }
  878. async function fetchLocationDetails(lat, long) {
  879. try {
  880. const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${long}`);
  881. if (!response.ok) {
  882. throw new Error('Failed to fetch location details');
  883. }
  884. const data = await response.json();
  885. const { address } = data;
  886. const locationDetails = `
  887. Area: ${address.neighbourhood || address.suburb || address.hamlet || 'N/A'}
  888. City: ${address.city || address.town || address.village || 'N/A'}
  889. State: ${address.state || 'N/A'}
  890. Country: ${address.country || 'N/A'}
  891. `;
  892. alert(locationDetails);
  893. } catch (error) {
  894. alert('Could not fetch location details: ' + error.message);
  895. }
  896. }
  897. window.addEventListener('keydown', function(event) {
  898. event.stopPropagation();
  899. const location = extractLocationFromIframe();
  900. if (!location) return;
  901. if (lastLocation && (lastLocation.lat !== location.lat || lastLocation.long !== location.long)) {
  902. if (googleMapsIframe) {
  903. toggleGoogleMapsIframe(location);
  904. toggleGoogleMapsIframe(location);
  905. }
  906. }
  907. lastLocation = location;
  908. if (event.key === settings.keybinds.toggleMap) {
  909. toggleGoogleMapsIframe(location);
  910. return;
  911. } else if (event.key === settings.keybinds.newTab) {
  912. window.open("https://www.google.com/maps?q=" + location.lat + "," + location.long, "_blank");
  913. return;
  914. } else if (event.key === settings.keybinds.settings) {
  915. if (googleMapsIframe) {
  916. toggleGoogleMapsIframe(location);
  917. }
  918. toggleSettingsModal();
  919. return;
  920. } else if (event.key === settings.keybinds.detailedLocation) {
  921. const { lat, long } = location;
  922. if (!lat || !long) {
  923. alert('Coordinates not yet available!');
  924. } else {
  925. fetchLocationDetails(lat, long);
  926. }
  927. return;
  928. }
  929. }, true);
  930. loadSettings();
  931. createLoadingIndicator();
  932. blockAds();
  933. setInterval(() => {
  934. const location = extractLocationFromIframe();
  935. if (!location || !lastLocation) return;
  936. if (lastLocation.lat !== location.lat || lastLocation.long !== location.long) {
  937. if (googleMapsIframe) {
  938. toggleGoogleMapsIframe(location);
  939. toggleGoogleMapsIframe(location);
  940. }
  941. lastLocation = location;
  942. }
  943. }, settings.refreshInterval);
  944. const observer = new MutationObserver(() => {
  945. if (!document.querySelector('iframe[src^="/svEmbed"]') && googleMapsIframe) {
  946. googleMapsIframe.remove();
  947. googleMapsIframe = null;
  948. }
  949. });
  950. observer.observe(document.body, { childList: true, subtree: true });
  951. })();