Fake GPS Location

Falsify GPS location to protect privacy, or to provide a virtual location sensor device. This script should either be be configured for specific sites only, or for all sites but excludes some specific sites.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Fake GPS Location
// @namespace    https://greasyfork.org/en/users/85671-jcunews
// @version      1.0.1
// @license      AGPLv3
// @author       jcunews
// @description  Falsify GPS location to protect privacy, or to provide a virtual location sensor device. This script should either be be configured for specific sites only, or for all sites but excludes some specific sites.
// @match        *://www.example-mockgps.net/*
// @exclude      *://www.report-real-gps.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(() => {

  //=== CONFIGURATION BEGIN ===

  //Default latitude in decimal degrees (-90 to 90). Negative = South of Equator. Positive = North of Equator.
  var defaultLatitude         = 0;
  //Default longitude in decimal degrees (-180 to 180). Negative = West. Positive = East.
  var defaultLongitude        = 0;
  //Default latitude & longitude accuracy in positive decimal degrees.
  var defaultAccuracy         = 0.000001;

  //Default altitude above sea level in meters.
  var defaultAltitude         = 0;
  //Default altitude accuracy in positive meters. `null` if not available.
  var defaultAltitudeAccuracy = 1;

  //Default heading in degrees. 0 to less than 360. 0 = North, 90 = East, 270 = West, etc. `null` if not available.
  var defaultHeading          = 0;
  //Default speed in meters per second. `null` if not available.
  var defaultSpeed            = 0;

  //Location profiles based on host name of websites.
  //It provides different location information between different websites.
  //All properties for each location profile are optional.
  //If a website host name if not in the list, the default values for all properties will be used.
  //If a property is not specified or has invalid value, the default value will be used.
  //If a property has invalid value range, it will be capped to its value range boundary.
  var locations = {
    "www.example-mockgps.net": {
      latitude: 50.7, longitude: 55, accuracy: 0.1, altitude: 100, altitudeAccuracy: 1, heading: 79.9, speed: 10.5
    },
    "www.another-example-mockgps.net": {
      latitude: -5.1, longitude: 123.4322, accuracy: 0.0002, altitude: 1234.5, altitudeAccuracy: 0.5, heading: 222.2, speed: 1.3
    }
  };

  //Time interval in milliseconds (1000ms is second) to report periodic location report.
  var watchInterval = 1000;

  //=== CONFIGURATION END ===

  Object.keys(locations).forEach(n => {
    n = locations[n];
    if ("number" === typeof n.latitude) {
      if (n.latitude > 90) {
        n.latitude = 90;
      } else if (n.latitude < -90) n.latitude = -90;
    } else n.latitude = defaultLatitude;
    if ("number" === typeof n.longitude) {
      if (n.longitude > 180) {
        n.longitude = 180;
      } else if (n.longitude < -180) n.longitude = -180;
    } else n.longitude = defaultLongitude;
    if ("number" === typeof n.accuracy) {
      if (n.accuracy <= 0) n.accuracy = defaultAccuracy;
    } else if (n.accuracy !== null) n.accuracy = defaultAccuracy;
    if ("number" !== typeof n.altitude) n.altitude = defaultAltitude;
    if ("number" === typeof n.altitudeAccuracy) {
      if (n.altitudeAccuracy <= 0) n.altitudeAccuracy = defaultAltitudeAccuracy;
    } else if (n.altitudeAccuracy !== null) n.altitudeAccuracy = defaultAltitudeAccuracy;
    if ("number" === typeof n.heading) {
      if (n.heading >= 360) {
        n.heading = 359.999999;
      } else if (n.heading < 0) n.heading = 0;
    } else if (n.heading !== null) n.heading = defaultHeading;
    if ("number" === typeof n.speed) {
      if (n.speed < 0) n.speed = 0;
    } else if (n.speed !== null) n.speed = defaultSpeed;
  });

  navigator.geolocation = navigator.geolocation || {};
  navigator.geolocation.getCurrentPosition = function(succ, err) {
    setTimeout((a, b) => {
      if (!(a = locations[location.hostname])) {
        a = {
          latitude: defaultLatitude, longitude: defaultLongitude, accuracy: defaultAccuracy,
          altitude: defaultAltitude, altitudeAccuracy: defaultAltitudeAccuracy, heading: defaultHeading, speed: defaultSpeed
        };
      }
      b = {coords: {}, timestamp: (new Date()).getTime()};
      Object.keys(a).forEach(k => b.coords[k] = a[k]);
      succ(b);
    }, 0);
  };
  navigator.geolocation.watchPosition = function(succ, err) {
    return setInterval((succ, err) => {
      navigator.geolocation.getCurrentPosition(succ, err);
    }, watchInterval, succ, err);
  };
  navigator.geolocation.clearWatch = function(id) {
    clearInterval(id);
  };

})();