WME E58 Map's previews

Create small previews for chosen map providers

当前为 2019-09-18 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         WME E58 Map's previews
// @version      0.1.0
// @description  Create small previews for chosen map providers
// @author       Anton Shevchuk
// @license      MIT License
// @grant        none
// @include      https://www.waze.com/editor*
// @include      https://www.waze.com/*/editor*
// @include      https://beta.waze.com/editor*
// @include      https://beta.waze.com/*/editor*
// @exclude      https://www.waze.com/user/editor*
// @exclude      https://beta.waze.com/user/editor*
// @icon         data:image/png;base64,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
// @require      https://greasyfork.org/scripts/389765-common-utils/code/CommonUtils.js?version=731051
// @require      https://greasyfork.org/scripts/389117-apihelper/code/APIHelper.js?version=733775
// @require      https://greasyfork.org/scripts/389577-apihelperui/code/APIHelperUI.js?version=733761
// @namespace    https://greasyfork.org/users/227648
// ==/UserScript==

/* jshint esversion: 8 */
/* global require, $, window, console, W, I18n, APIHelper, APIHelperUI, Settings */
(function () {
  'use strict';

  let helper, tab, sidebar;

  const NAME = 'E58';

  // Translation
  const TRANSLATION = {
    'en': {
      // Tab title
      title: 'Maps 🗺',
      maps: {
        // Fieldset's legend
        title: 'Sources',
        // Fieldset's description
        description: 'Reload page for apply changes',
        // Description for option `gis`
        gis: '2GIS',
        // Description for option `Google`
        google: 'Google',
        // Description for option `OSM`
        osm: 'Open Street Map',
        // Description for option `yandex`
        yandex: 'Yandex',
      },
      options: {
        title: 'Options',
        controls: 'Controls on the map',
        interactive: 'Interaction with the map'
      }
    },
    'uk': {
      title: 'Карти 🗺',
      maps: {
        title: 'Джерела',
        description: 'Оновіть сторінку після внесення змін',
        gis: '2GIS',
        google: 'Google',
        osm: 'Open Street Map',
        yandex: 'Яндекс',
      },
      options: {
        title: 'Налаштування',
        controls: 'Елементи управління',
        interactive: 'Можливість взаємодіяти с картою'
      }
    },
    'ru': {
      title: 'Карты 🗺',
      maps: {
        title: 'Источники',
        description: 'Обновите страницу после изменений',
        gis: '2GIS',
        google: 'Google',
        osm: 'Open Street Map',
        yandex: 'Яндекс',
      },
      options: {
        title: 'Настройки',
        controls: 'Элементи управления',
        interactive: 'Возможность взаимодествия с картой'
      }
    }
  };

  // Default settings
  const settings = {
    maps: {
      gis: false,
      google: false,
      osm: false,
      yandex: false,
    },
    options: {
      controls: false,
      interactive: false,
    }
  };

  APIHelper.bootstrap();
  APIHelper.addTranslation(NAME, TRANSLATION);
  APIHelper.addStyle(
    '#sidebar #links:before { display: none; }' +
    '.e58 legend { cursor:pointer; font-size: 12px; font-weight: bold; width: auto; text-align: right; border: 0; margin: 0; padding: 0 8px; }' +
    '.e58 fieldset { border: 1px solid #ddd; padding: 4px; }' +
    '.e58 fieldset p { padding: 0; margin: 0 8px; }' +
    '.e58 fieldset.e58 div.controls label { white-space: normal; }'
  );

  let ScriptSettings = new Settings(NAME, settings);

  /**
   * Basic Map class
   */
  class MapPreview {
    constructor(uid, container) {
      this.uid = uid;
      this.map = null;
      this.wrapper = this._wrapper();
      container.append(this.wrapper);

      this.controls = ScriptSettings.get('options').controls;
      this.interactive = ScriptSettings.get('options').interactive;
    }

    /**
     * Load external JS Map library
     * @param  {String} url
     * @return {Promise<*>}
     */
    async script(url) {
      this.wrapper.style.height = '200px';
      return await $.ajax({
        url: url,
        cache: true,
        dataType: 'script',
        success: () => console.log(NAME, this.uid, 'loaded')
      });
    }
    /**
     * Build div for map
     * @return {HTMLDivElement}
     * @protected
     */
    _wrapper() {
      let div = document.createElement('div');
      div.id = this._uid();
      return div;
    }
    _uid() {
      return NAME + '-map-' + this.uid;
    }
    _center() {
      return W.map.getCenter().transform('EPSG:900913', 'EPSG:4326');
    }
    _zoom() {
      return W.map.getZoom() + 10;
    }
    update() {
      let center = this._center();
      this._update(center.lat, center.lon, this._zoom());
    }
    _update(lat, lon, zoom) {
      throw new Error('Abstract method');
    }
  }

  /**
   * 2Gis
   */
  class GisPreview extends MapPreview {
    constructor(container) {
      super('2Gis', container);
    }
    async render() {
      await this.script('https://maps.api.2gis.ru/2.0/loader.js?pkg=basic');
      let pos = this._center();
      DG.then(() => {
        this.map = DG.map(this._uid(), {
          center: [pos.lat, pos.lon],
          zoom: this._zoom(),
          fullscreenControl: this.controls,
          zoomControl: this.controls,
          boxZoom: this.controls,
          doubleClickZoom: this.interactive,
          scrollWheelZoom: this.interactive,
          dragging: this.interactive,
          keyboard: this.interactive,
        });
        // Setup handler
        W.map.events.register('moveend', null, () => this.update());
      });
    }
    _update(lat, lon, zoom) {
      this.map.setZoom(zoom);
      this.map.panTo([lat, lon]);
    }
  }

  /**
   * Google Maps
   */
  class GooglePreview extends MapPreview {
    constructor(container) {
      super('Google', container);
    }
    async render() {
      let pos = this._center();
      let container = document.getElementById(this._uid());
      container.style.height = '200px';
      this.map = new google.maps.Map(container, {
        center: new google.maps.LatLng(pos.lat, pos.lon),
        zoom: this._zoom(),
        mapTypeId: 'roadmap',
        mapTypeControl: false,
        streetViewControl: false,
        disableDefaultUI: !this.controls,
        gestureHandling: this.interactive ? 'cooperative ' : 'none',
        zoomControl: this.controls,
      });

      // Setup handler
      W.map.events.register('moveend', null, () => this.update());
    }
    _update(lat, lon, zoom) {
      this.map.setZoom(zoom);
      this.map.setCenter(new google.maps.LatLng(lat, lon));
    }
  }

  /**
   * Open Street Maps
   */
  class OSMPreview extends MapPreview {
    constructor(container) {
      super('OSM', container);
    }
    async render() {
      let pos = this._center();
      let container = document.getElementById(this._uid());
      container.style.height = '200px';
      this.map = new google.maps.Map(container, {
        center: new google.maps.LatLng(pos.lat, pos.lon),
        zoom: this._zoom(),
        mapTypeId: 'OSM',
        mapTypeControl: false,
        streetViewControl: false,
        disableDefaultUI: !this.controls,
        gestureHandling: this.interactive ? 'cooperative ' : 'none',
        zoomControl: this.controls,
      });

      // Define OSM map type pointing at the OpenStreetMap tile server
      this.map.mapTypes.set('OSM', new google.maps.ImageMapType({
        getTileUrl: function(coord, zoom) {
          return 'https://tile.openstreetmap.org/' + zoom + '/' + coord.x + '/' + coord.y + '.png';
        },
        tileSize: new google.maps.Size(256, 256),
        name: 'OpenStreetMap',
        maxZoom: 18
      }));

      // Setup handler
      W.map.events.register('moveend', null, () => this.update());
    }
    _update(lat, lon, zoom) {
      this.map.setZoom(zoom);
      this.map.setCenter(new google.maps.LatLng(lat, lon));
    }
  }

  /**
   * Yandex Maps
   */
  class YandexPreview extends MapPreview {
    constructor(container) {
      super('Yandex', container);
    }
    async render() {
      await this.script('https://api-maps.yandex.ru/2.1/?lang=uk_UA');
      let pos = this._center();
      ymaps.ready(() => {
        this.map = new ymaps.Map(this._uid(), {
          center: [pos.lat, pos.lon],
          zoom: this._zoom(),
          controls: this.controls ? ['default'] : []
        });
        // Disable all controls
        if (!this.interactive) {
          this.map.behaviors.disable(['drag', 'dblClickZoom', 'scrollZoom', 'rightMouseButtonMagnifier']);
        }
        // Setup handler
        W.map.events.register('moveend', null, () => this.update());
      });
    }
    _update(lat, lon, zoom) {
      this.map.setZoom(zoom);
      this.map.setCenter([lat, lon]);
    }
  }

  // Handlers
  $(document).on('ready.apihelper', ready);
  $(window).on('beforeunload', () => ScriptSettings.save());

  function ready() {
    // Setup Tab with options
    helper = new APIHelperUI(NAME);
    tab = helper.createTab(I18n.t(NAME).title);

    // Setup providers map settings
    let fsMap = helper.createFieldset(I18n.t(NAME).maps.title);
    let maps = ScriptSettings.get('maps');
    fsMap.addText('description', I18n.t(NAME).maps.description);
    for (let item in maps) {
      if (maps.hasOwnProperty(item)) {
        fsMap.addCheckbox('maps-' + item, I18n.t(NAME).maps[item], I18n.t(NAME).maps[item], function (event) {
          ScriptSettings.set(['maps', item], event.target.checked);
        }, ScriptSettings.get('maps', item));
      }
    }
    tab.addElement(fsMap);

    // Setup options for maps
    let fsOptions = helper.createFieldset(I18n.t(NAME).options.title);
    let options = ScriptSettings.get('options');
    for (let item in options) {
      if (options.hasOwnProperty(item)) {
        fsOptions.addCheckbox('options-' + item, I18n.t(NAME).options[item], I18n.t(NAME).options[item], function (event) {
          ScriptSettings.set(['options', item], event.target.checked);
        }, ScriptSettings.get('options', item));
      }
    }
    tab.addElement(fsOptions);

    tab.inject();

    // Setup Preview Map element
    sidebar = document.createElement('div');
    sidebar.id = NAME + '-map-container';
    sidebar.className = 'flex-noshrink';

    // Injection
    document.getElementById('sidebar').insertBefore(sidebar, document.getElementById('links'));

    if (ScriptSettings.get('maps').gis) {
      let Gis = new GisPreview(sidebar);
      Gis.render();
    }
    if (ScriptSettings.get('maps').google) {
      let Google = new GooglePreview(sidebar);
      Google.render();
    }
    if (ScriptSettings.get('maps').osm) {
      let OSM = new OSMPreview(sidebar);
      OSM.render();
    }
    if (ScriptSettings.get('maps').yandex) {
      let Yandex = new YandexPreview(sidebar);
      Yandex.render();
    }
  }
})();