Source: geo-position.js

import { logger } from "./shims";

const initialGeoTimeoutSeconds = 1;

const defaultCoords = {
  latitude: 1,
  longitude: 1
};

// for an initial rapid, low-accuracy position
const fastGeolocationPositionOptions = { 
  enableHighAccuracy: false,
  timeout: initialGeoTimeoutSeconds
};

// subsequent position monitoring should be high-accuracy
const accurateGeolocationPositionOptions = { 
  enableHighAccuracy: true
};

/** Responsible for tracking the user's position, when geo listening is enabled and the browser is capable
 * @property {Boolean} geoListenEnabled - whether or not the geo positioning system is enabled and available
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/Using_geolocation **/
export class GeoPosition {
  /** Create a new GeoPosition.
   * @param {Object} navigator - provides access to geolocation system
   * @param {Object} options - parameters for initializing this GeoPosition
   * @param {Boolean} [options.geoListenEnabled = false] - whether or not to attempt to use geolocation **/
  constructor(navigator,options = {}) {
    this._navigator = navigator;
    this._initialGeolocationPromise = Promise.resolve(defaultCoords);

    if (this._navigator.geolocation && options.geoListenEnabled) {
      this.geoListenEnabled = true;
    } else {
      this.geoListenEnabled = false;
    }
  }

  /** @return {String} Human-readable representation of this GeoPosition **/
  toString() { 
    return `GeoPosition (enabled: ${this.geoListenEnabled})`;
  }

  /** Attempts to get an initial rough geographic location for the listener, then sets up a callback
   * to update the position.
   * @param {Function} geoUpdateCallback - object that should receive geolocation coordinate updates
   * @see geoListenEnabled **/
  connect(geoUpdateCallback = () => {}) {
    if (!this.geoListenEnabled) {
      logger.info("Geolocation disabled");
      this._initialGeolocationPromise = Promise.resolve({});
      return;
    }

    logger.info("Initializing geolocation system");

    this._initialGeolocationPromise = new Promise((resolve,reject) => {
      this._navigator.geolocation.getCurrentPosition((initialPosition) => {
        let coords = initialPosition.coords;
        logger.info("Received initial geolocation",coords);
        geoUpdateCallback(coords);

        let geoWatchId = this._navigator.geolocation.watchPosition((updatedPosition) => {
          let newCoords = updatedPosition.coords;
          geoUpdateCallback(newCoords);
        },(error) => {
          logger.warn(`Unable to watch position: ${error.message} (code #${error.code})`);
        },accurateGeolocationPositionOptions);

        logger.info(`Monitoring geoposition updates (watch ID ${geoWatchId})`);
        resolve(coords);
      },function initialGeoError(error) {
        logger.warn(`Unable to get initial geolocation: ${error.message} (code #${error.code})`);
        resolve(defaultCoords);
      },fastGeolocationPositionOptions);
    });
  }

  /** Allows you to wait on the progress of the .connect() behavior, attempting to get an initial
   * estimate of the user's position. Note that this promise will never fail - if we cannot get an
   * accurate estimate, we fall back to default coordinates (currently latitude 1, longitude 1)
   * @return {Promise} Represents the attempt to get an initial estimate of the user's position **/
  waitForInitialGeolocation() {
    return this._initialGeolocationPromise;
  }
}