// ==UserScript==
// @name WME E58 Map's previews
// @name:uk WME 🇺🇦 E58 Map's previews
// @version 0.6.0
// @description Create small previews for chosen map providers
// @description:uk Створює невеличку карту для перегляду
// @license MIT License
// @author Anton Shevchuk
// @namespace https://greasyfork.org/users/227648-anton-shevchuk
// @supportURL https://github.com/AntonShevchuk/wme-e58/issues
// @match https://*.waze.com/editor*
// @match https://*.waze.com/*/editor*
// @exclude https://*.waze.com/user/editor*
// @icon data:image/png;base64,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
// @grant none
// @require https://update.greasyfork.org/scripts/389765/1090053/CommonUtils.js
// @require https://update.greasyfork.org/scripts/450160/1218867/WME-Bootstrap.js
// @require https://update.greasyfork.org/scripts/450221/1137043/WME-Base.js
// @require https://update.greasyfork.org/scripts/450320/1281847/WME-UI.js
// ==/UserScript==
/* jshint esversion: 8 */
/* global require */
/* global $, jQuery */
/* global W */
/* global I18n */
/* global WME, WMEBase, WMEUI, WMEUIHelper */
/* global Container, Settings, SimpleCache, Tools */
/* global H, google */
(function () {
'use strict'
const NAME = 'E58'
// Translation
const TRANSLATION = {
'en': {
// Tab title
title: 'Map preview',
// Tab description
description: 'Open a small preview modal window with the map',
// Tab help
help: 'You can use the <a href="#keyboard-dialog" target="_blank" rel="noopener noreferrer" data-toggle="modal">Keyboard shortcuts</a> to open the map preview window. It\'s more convenient than clicking on the button.',
maps: {
// Fieldset's legend
title: 'Sources',
// Fieldset's description
description: 'Choose a map provider',
// Description for option `Google`
google: 'Google',
// Description for option `OSM`
osm: 'Open Street Map',
},
options: {
title: 'Options',
description: 'Choose a map provider in the settings',
controls: 'Controls on the map',
interactive: 'Interaction with the map',
},
},
'uk': {
title: 'Карта',
description: 'Відкрити маленьку карту',
help: 'Використовуйте <a href="#keyboard-dialog" target="_blank" rel="noopener noreferrer" data-toggle="modal">гарячі клавіши</a>, це значно швидше ніж використовувати кнопку',
maps: {
title: 'Джерела',
description: 'Оберіть карту для відображення',
google: 'Google',
osm: 'Open Street Map',
},
options: {
title: 'Налаштування',
description: 'Оберіть у налаштуваннях карту для відображення',
controls: 'Елементи управління',
interactive: 'Можливість взаємодіяти с картою',
},
},
'ru': {
title: 'Карта',
description: 'Открыть маленькую карту',
help: 'Используйте <a href="#keyboard-dialog" target="_blank" rel="noopener noreferrer" data-toggle="modal">комбинации клавиш</a>, и не надо будет клацать кнопку',
maps: {
title: 'Источники',
description: 'Выберите карту для отображения',
google: 'Google',
osm: 'Open Street Map',
},
options: {
title: 'Настройки',
description: 'Выберите в настройках карту для отображения',
controls: 'Элементи управления карты',
interactive: 'Возможность взаимодествия с картой',
},
}
}
const STYLE =
'.e58 .header h5 { padding: 0 16px; font-size: 16px }' +
'.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 !important; }' +
'.e58 fieldset.e58 div.controls label { white-space: normal; font-weight: 400; }' +
'div.e58.e58-text { margin: 15px 0; }' +
'p.e58-info { border-top: 1px solid #ccc; color: #777; font-size: x-small; margin-top: 15px; padding-top: 10px; text-align: center; }'
WMEUI.addTranslation(NAME, TRANSLATION)
WMEUI.addStyle(STYLE)
// Default settings
const SETTINGS = {
map: 'google',
maps: [
'google', 'osm'
],
options: {
controls: false,
interactive: false,
}
}
/**
* Basic Map class
*/
class MapPreview {
constructor (uid, container, settings) {
this.uid = uid
this.map = null
this.wrapper = this._wrapper()
container.append(this.wrapper)
container.style.height = '256px'
this.settings = settings
this.controls = settings.get('options', 'controls')
this.interactive = settings.get('options', 'interactive')
}
/**
* Load external JS Map library
* @param {String} url
* @return {Promise<*>}
*/
async script (url) {
return $.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()
div.style.height = '256px'
return div
}
_uid () {
return NAME + '-map-' + this.uid
}
_center () {
let center = new OpenLayers.Geometry.Point(W.map.getCenter().lon, W.map.getCenter().lat).transform('EPSG:900913', 'EPSG:4326')
return {
lon: center.x,
lat: center.y,
}
}
_zoom () {
return W.map.getZoom() - 1
}
update () {
let center = this._center()
this._update(center.lat, center.lon, this._zoom())
}
_update (lat, lon, zoom) {
throw new Error('Abstract method')
}
}
/**
* Google Maps
*/
class GooglePreview extends MapPreview {
constructor (container, settings) {
super('Google', container, settings)
}
async render () {
let pos = this._center()
this.map = new google.maps.Map(this.wrapper, {
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, settings) {
super('OSM', container, settings)
}
async render () {
let pos = this._center()
this.map = new google.maps.Map(this.wrapper, {
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))
}
}
/**
* E58 Map Preview class
*/
class E58 extends WMEBase {
constructor (name, settings) {
super(name, settings)
this.helper = new WMEUIHelper(name)
let tab = this.helper.createTab(
I18n.t(name).title,
{
image: GM_info.script.icon
}
)
tab.addText('description', I18n.t(name).description)
let button = tab.addButton('preview', I18n.t(name).title, '', () => this.toggleMap())
button.html().className += ' waze-btn-blue'
// Setup providers map settings
let fsMap = this.helper.createFieldset(I18n.t(name).maps.title)
for (let i = 0; i < settings.maps.length; i++) {
let map = settings.maps[i]
fsMap.addRadio(
'maps-' + map,
I18n.t(name).maps[map],
() => this.settings.set(['map'], map),
'maps',
map,
this.settings.get('map') === map
)
}
tab.addElement(fsMap)
// Setup options for maps
let fsOptions = this.helper.createFieldset(I18n.t(name).options.title)
for (let item in settings.options) {
if (settings.options.hasOwnProperty(item)) {
fsOptions.addCheckbox(
'options-' + item,
I18n.t(name).options[item],
(event) => this.settings.set(['options', item], event.target.checked),
this.settings.get('options', item))
}
}
tab.addElement(fsOptions)
tab.addDiv('text', I18n.t(name).help)
tab.addText(
'info',
'<a href="' + GM_info.scriptUpdateURL + '">' + GM_info.script.name + '</a> ' + GM_info.script.version
)
tab.inject()
}
/**
* Show modal with map preview
*/
toggleMap () {
if (document.getElementById('e58-map-preview')) {
this.log('hide preview map')
$('#panel-container a.close-panel').click()
return
}
/** @type {WMEUIHelperModal} */
let modal = this.helper.createModal(
'<h5>' + I18n.t(this.name).title + '</h5>'
)
// Setup Preview Map element
let map = modal.addDiv('map-preview').html()
modal.inject()
this.log('show preview map', this.settings.get('map'))
if (this.settings.get('map') === 'google') {
let Google = new GooglePreview(map, this.settings)
Google.render()
} else if (this.settings.get('map') === 'osm') {
let OSM = new OSMPreview(map, this.settings)
OSM.render()
} else {
// disabled
map.innerText = I18n.t(this.name).maps.description
}
}
}
// Main handler
$(document).on('bootstrap.wme', () => {
// Create E58 Instance
let Instance = new E58(NAME, SETTINGS)
// Bind shortcut
WMEUI.addShortcut(
NAME,
I18n.t(NAME).description,
NAME,
I18n.t(NAME).title + ' 🗺️',
'A+M',
() => Instance.toggleMap(),
)
})
})()