WME E50 Fetch POI Data

Fetch information about the POI from external sources

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey to install this script.

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         WME E50 Fetch POI Data
// @name:uk      WME 🇺🇦 E50 Fetch POI Data
// @name:ru      WME 🇺🇦 E50 Fetch POI Data
// @version      0.14.2
// @description  Fetch information about the POI from external sources
// @description:uk Скрипт дозволяє отримувати інформацію про POI зі сторонніх ресурсів
// @description:ru Скрипт для получения информации о POI с внешних ресурсов
// @license      MIT License
// @author       Anton Shevchuk
// @namespace    https://greasyfork.org/users/227648-anton-shevchuk
// @supportURL   https://github.com/AntonShevchuk/wme-e50/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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ICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmE5OWYwNzEyLTc5ZDctNDY5Ni05MWUyLTE5YmEzNjM2NTIzYiIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjZGIzMGE2ZS00M2RkLTQ0MzktYTQ0Ny05ODc4YTc0MWZhZWYiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpiMjBiMWUzNS1mZjE3LTRjZjgtODk5My0wMTYwNTc0OGY5MDkiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJNYWMgT1MiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjczNDMzNzgwNTU3NzE0IgogICBHSU1QOlZlcnNpb249IjIuMTAuMzIiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIgogICB4bXA6TWV0YWRhdGFEYXRlPSIyMDIzOjAxOjExVDExOjQzOjAwKzAxOjAwIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyMzowMToxMVQxMTo0MzowMCswMTowMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmI4N2M2YmI2LTJmZGItNDdlMi05MjYzLWE3ZjNmMjY0ZDYyOSIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iR2ltcCAyLjEwIChNYWMgT1MpIgogICAgICBzdEV2dDp3aGVuPSIyMDIzLTAxLTExVDExOjQzOjAwKzAxOjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PgpaVBgAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfnAQsKKwBWE1eUAAAGfklEQVR42u2ae0zTVxTHv7R1yuRVymgrdMYhGh9oYDQBCzPMByYm4hAfU1E0IjjjIBKVxEqJIz7+ULFRJEq0hGCFzdDEaEBNAGkZERcnL3EMUXC0gIUCE9BSuj+wP/qzrZSHSy2/T0L4nXvPuXDP795z7rn5ARQUFBQUFBQUFBQUUxEH4ikFNDjgJwCbAIRAb9RnHzPVA5ADyIcDMpCKoZFOEbgQQSG5K9Er1Uq9vaJUK/WSuxI9RFBABK6xA+SV9ZX6qUJlfaUeIsiHF4YIcdcE1zJjVsUAAMprypFTkYPMfzLtagfEe8UjOigayxYvAwBI7kmwS7ErzgEilCp/Vn7HcedAUa1AyM0Quw568g1yCPwEUHWqwBVzH9DooIdy3DkAgCx5lt1HfcMcOe4c0EEPpXHpXCLaS9okdu8A4zly6VwH2lQ/BzCsUYpkRmKhx8Jx/YGetz0QN4sJeb3beiz+arHV9vdb7qNioMJsn9BXiOC5wfB088SgbhC1zbXI+jPLov64HbAtYBsiQyPH5YDW160QXxhxwL7QfVj97WrrB8gFKhrIEwp2DEbO9hz4ePmQ2oMWBmFr2FaIb4mRXJNs1fD/+xZwd3KfkD0NNOTtyjOZvAHH6Y5I+iEJQl+hbTpgotxcdRM8T97HlzWdgYS1CZO3BT5E3a1Gk6rJKt2O7o6P9h/LPTZqDDBm+ZLlJLmosghlz8rAnMlE7OpYuMx0AQB4uHngtN9pHKk+MvkOaFI1gS/lj+sNGv5BA2kNaVbbJs5OBNOZSci1TbVYc3sNIQ/ph3Ao8hAhhy4IBaptbAu4OI44YODdwJhsw+aHkeTy+nKSfLjqMPrf9hPyPK95th0DNP9qxqTvxfIiyXWqOtPt2aMmnlmuLLjR3GzMAca3DPrhX18zvobQVwihrxC7ObstmnJZXJKc/jLdREfVqSLJ+7/ZP/kxYCI4OTqR5BthN7AuaB0cpzsSbSe6TuDy3ctIeZpCflu0sb8vOp0++Q5gu7GRF5Y3+hJ/o0HcwziLDuCwONi8fLPp+Ew2hJuF4BZxEft7LNHu6uRKCnjmGBoity/gLgCeTbIDeGweeGzeqHoqtcrEAdbvFAfsXLETd+rvoKCrYPiQ88XIKul602XWrq2njbwCHOi2EwMczFwzage1qGqswqNnj6DuVpP6pjGmIen7JLNj6bQ6s+1arXbyi6HJYovnFpI88G4AybnJOP/yPNH2JPoJlvgsIWTjZ9Kbo5t/d3QG/dM7oKW9BRVPR6+4Plym0nYppCKp+YzwHtEtEQoSCwjZ+UtnRHtGI6c9BwPaAcyYNgMA4OZkPr2xXdjkmGAhVkzIAW1dbdhUvGniadAMMo0MKrUKHBaHaJvtOhtoBzS9Ghhurxg0hsU6gHRWUNZ9fsVQR4/5+kE3pBvzWDqd7vNzANOJaTGrGHOAd8BsCjXmXOM523FAtiAblT9W4nnccygPKCGaLzKrZ5zvAeBVzysAQHNHM6l9EXeRia2Hqwepau3T99mOA2axZiFwfiDmcOeAw+IgIjDCROci/yKcHZ0Jube/l7jILPmrhKQb6BNIkk8uOkk6UdY1132aLMBmspEflm+1/vXH1yHTyFD6tBQrA1YS7f6+/ihcW4i04jTI++TICs7CjhU7SLbVjSP1rLhZjJTuFLBcWQCApb5Lke6fjsTHiYhiRSFmRQzJtri2+NM4gOfJG/VWxpiqV1WQaWRIa0jD3va9JNtwfjjC+eHmDzWDWlwovUBqK6kuwYaQDUQmSIhIQEJEgtlULaoX2V45fPb2WWgHrTut5T/Ih7RDSj5M3d+C1tetH7XTDmqRUZRhm3eC6S/TceK3E+js7bSo0/+uH1eLrmL7g+0mfYMYxNFfj5qUvcaTPyM7g1ONpybvJCj/Wz6uHGygpqOGJKfWp0LaKMVB/4MI8AkAx50DBo0BZacSDa0NuPTwEkrelFgcT9ImgUQswZWgK+DP5YPNZKO7rxsvVC+QqciETCOz/lzmfdxb33KsZVhIta9vIiyhTx2+ieH9wgNNqVPqDR0x7Bi7n7zxHJU6pZ6mg67MsJ/2hOyxewcY5qjqVEEHXRkNQG7hH4UAAIGfAIooBeK944eLFjv6ifeOhyJKAYGfAADwfs6572vQqfuJjCENbuRL+eXZ97Itphd7QNWpQva9bPCl/HIAG8nV+VT6TE6PDBzHECgoKCgoKCgoKCgopiz/AYtz5BmJvxgCAAAAAElFTkSuQmCC
// @connect      revgeocode.search.hereapi.com
// @connect      api.visicom.ua
// @connect      nominatim.openstreetmap.org
// @connect      dev.virtualearth.net
// @connect      maps.googleapis.com
// @connect      stat.waze.com.ua
// @grant        GM.xmlHttpRequest
// @grant        GM.setClipboard
// @require      https://update.greasyfork.org/scripts/389765/1794584/CommonUtils.js
// @require      https://update.greasyfork.org/scripts/450160/1792042/WME-Bootstrap.js
// @require      https://update.greasyfork.org/scripts/450221/1804989/WME-Base.js
// @require      https://update.greasyfork.org/scripts/450320/1796236/WME-UI.js
// @require      https://cdn.jsdelivr.net/npm/@turf/[email protected]/turf.min.js
// ==/UserScript==

(function () {
    'use strict';

    const NAME = 'E50';

    const TRANSLATION = {
        'en': {
            title: 'Information 📍',
            notFound: 'Not found',
            options: {
                title: 'Options',
                modal: 'Use modal window',
                transparent: 'Transparent modal window',
                entryPoint: 'Create Entry Point if not exists',
                externalProvider: 'Show pointer to linked place',
                copyData: 'Copy POI data to clipboard on click',
                lock: 'Lock POI to 2 level',
                keys: 'API keys',
            },
            ranges: {
                title: 'Additional',
                radius: 'Radius for search',
                collapse: 'Collapse the lists longer than',
            },
            providers: {
                title: 'Providers',
                magic: 'Closest Segments',
                osm: 'Open Street Map',
                bing: 'Bing',
                here: 'HERE',
                google: 'Google',
                visicom: 'Visicom',
                ua: 'UA Addresses',
            },
            questions: {
                changeName: 'Are you sure to change the name?',
                changeCity: 'Are you sure to change the city?',
                changeStreet: 'Are you sure to change the street name?',
                changeNumber: 'Are you sure to change the house number?',
                notFoundCity: 'City not found in the current location, are you sure to create a new one?',
                notFoundStreet: 'Street not found in the current location, are you sure to create a new one?'
            }
        },
        'uk': {
            title: 'Інформація 📍',
            notFound: 'Нічого не знайдено',
            options: {
                title: 'Налаштування',
                modal: 'Використовувати окрему панель',
                transparent: 'Напівпрозора панель',
                entryPoint: 'Створювати точку в\'їзду, якщо відсутня',
                externalProvider: 'Відображати пов\'язане місце',
                copyData: 'При виборі, копіювати до буферу обміну назву та адресу POI',
                lock: 'Блокувати POI 2-м рівнем',
                keys: 'Ключі до API',
            },
            ranges: {
                title: 'Додаткові',
                radius: 'Радіус для пошуку',
                collapse: 'Складати перелік, більший за',
            },
            providers: {
                title: 'Джерела',
                magic: 'Сегменти поруч',
                osm: 'Open Street Map',
                bing: 'Bing',
                here: 'HERE',
                google: 'Google',
                visicom: 'Візіком',
                ua: 'UA Адреси',
            },
            questions: {
                changeName: 'Ви впевненні що хочете змінити им\'я?',
                changeCity: 'Ви впевненні що хочете змінити місто?',
                changeStreet: 'Ви впевненні що хочете змінити вулицю?',
                changeNumber: 'Ви впевненні що хочете змінити номер дома?',
                notFoundCity: 'Ми не знайшли такого міста у поточному місці, ви впевнені, що треба його додати?',
                notFoundStreet: 'Ми не знайшли таку вулицю у поточному місці, ви впевнені, що треба її додати?',
            }
        },
        'ru': {
            title: 'Информация 📍',
            notFound: 'Ничего не найдено',
            options: {
                title: 'Настройки',
                modal: 'Использовать отдельную панель',
                transparent: 'Полупрозрачная панель',
                entryPoint: 'Создавать точку въезда если отсутствует',
                externalProvider: 'Показывать связанное место',
                copyData: 'При виборе, копировать в буфер обмена название и адрес POI',
                lock: 'Блокировать POI 2-м уровнем',
                keys: 'Ключи к API',
            },
            ranges: {
                title: 'Дополнительно',
                radius: 'Радиус поиска',
                collapse: 'Складывать списки, которые больше',
            },
            providers: {
                title: 'Источники',
                magic: 'Ближайшие сегменты',
                osm: 'Open Street Map',
                bing: 'Bing',
                here: 'HERE',
                google: 'Google',
                visicom: 'Визиком',
                ua: 'UA Адреса',
            },
            questions: {
                changeName: 'Ви уверены, что хотите изменить имя?',
                changeCity: 'Ви уверены, что хотите изменить город?',
                changeStreet: 'Ви уверены, что хотите изменить улицу?',
                changeNumber: 'Ви уверены, что хотите изменить номер дома?',
                notFoundCity: 'Мы не нашли такого города в данной локации, вы уверены что нужно его добавить?',
                notFoundStreet: 'Мы не нашли такую улицу в данной локации, вы уверены что нужно её добавить?',
            }
        },
        'fr': {
            title: 'Informations 📍',
            notFound: 'Lieu inconnu',
            options: {
                title: 'Réglages',
                modal: 'Activer la fenêtre',
                transparent: 'Fenêtre transparente',
                entryPoint: 'Créer le point d\'entrée s\'il n\'existe pas',
                copyData: 'Copier les informations du POI en cliquant',
                lock: 'Verrouiller le POI au niveau 2',
                keys: 'API keys',
            },
            ranges: {
                title: 'Supplémentaire',
                radius: 'Rayon de recherche',
                collapse: 'Réduire les listes plus grandes que',
            },
            providers: {
                title: 'Sources',
                magic: 'Au plus proche du segment',
                osm: 'Open Street Map',
                bing: 'Bing',
                here: 'HERE',
                google: 'Google',
                visicom: 'Visicom',
                ua: 'UA Addresses',
            },
            questions: {
                changeName: 'Êtes-vous sûr de changer le nom ?',
                changeCity: 'Êtes-vous sûr de changer la ville ?',
                changeStreet: 'Êtes-vous sûr de changer la rue ?',
                changeNumber: 'Êtes-vous sûr de changer le numéro de rue ?',
                notFoundCity: 'City not found in the current location, are you sure to create a new one?',
                notFoundStreet: 'Street not found in the current location, are you sure to create a new one?'
            }
        }
    };

    const SETTINGS = {
        options: {
            modal: true,
            transparent: false,
            entryPoint: true,
            externalProvider: false,
            copyData: true,
            lock: true,
        },
        ranges: {
            radius: 200,
            collapse: 3,
        },
        providers: {
            magic: true,
            osm: false,
            // bing: false,
            here: false,
            google: true,
            visicom: false,
            ua: false,
        },
        keys: {
            // Russian warship, go f*ck yourself!
            visicom: '',
            here: '',
            // bing: '',
            google: 'AIzaSyBWB3' + 'jiUm1dkFwvJWy4w4ZmO7K' + 'PyF4oUa0', // extracted from WME
            ua: 'E50'
        }
    };

    const LOCALE = {
        // Ukraine
        232: {
            country: 'uk',
            language: 'ua',
            locale: 'uk_UA'
        }
    };
    // Road Types
    //   I18n.translations.uk.segment.road_types
    //   I18n.translations.en.segment.road_types
    const TYPES = {
        boardwalk: 10,
        stairway: 16,
        railroad: 18,
        runway: 19,
        parking: 20};

    const layerConfig = {
        styleContext: {
            label: (context) => {
                const style = context?.feature?.properties?.style;
                if (!style)
                    return style;
                return style?.label;
            },
        },
        styleRules: [
            {
                predicate: (properties) => properties.styleName === "styleNode",
                style: {
                    pointRadius: 8,
                    fillOpacity: 0.5,
                    fillColor: '#fff',
                    strokeColor: '#fff',
                    strokeWidth: 2,
                    strokeLinecap: 'round',
                    graphicZIndex: 9999,
                },
            },
            {
                predicate: (properties) => properties.styleName === "styleLine",
                style: {
                    strokeWidth: 4,
                    strokeColor: '#fff',
                    strokeLinecap: 'round',
                    strokeDashstyle: 'dash',
                    label: "${label}",
                    labelOutlineColor: '#000',
                    labelOutlineWidth: 3,
                    labelAlign: 'cm',
                    fontColor: '#fff',
                    fontSize: '24px',
                    fontFamily: 'Courier New, monospace',
                    fontWeight: 'bold',
                    labelYOffset: 24,
                    graphicZIndex: 9999,
                }
            }
        ],
    };

    /**
     * Normalize the string:
     *  - remove the double quotes
     *  - remove double space
     */
    function normalizeString$1(str) {
        // Clear space symbols and double quotes
        str = str.trim()
            .replace(/["""]/g, '')
            .replace(/\s{2,}/g, ' ');
        // Clear accents/diacritics, but "\u0306" needed for "й"
        // str = str.normalize('NFD').replace(/[\u0300-\u0305\u0309-\u036f]/g, '');
        return str;
    }
    /**
     * Normalize the name:
     *  - remove № and # chars
     *  - remove dots
     */
    function normalizeName(name) {
        name = normalizeString$1(name);
        name = name.replace(/[№#]/g, '');
        name = name.replace(/\.$/, '');
        return name;
    }
    /**
     * Normalize the city name
     */
    function normalizeCity(city) {
        return normalizeString$1(city);
    }
    /**
     * Normalize the street name by UA rules
     */
    function normalizeStreet(street) {
        street = normalizeString$1(street);
        if (street === '') {
            return '';
        }
        // Prepare street name
        street = street.replace(/['']/, '\'');
        // Remove text in the "()", OSM puts alternative name to the pair brackets
        street = street.replace(/( ?\(.*\))/gi, '');
        // Normalize title
        let regs = {
            '(^| )бульвар( |$)': '$1б-р$2', // normalize
            '(^| )вїзд( |$)': '$1в\'їзд$2', // fix mistakes
            '(^| )в\'ізд( |$)': '$1в\'їзд$2', // fix mistakes
            '(^|.+?) ?вулиця ?(.+|$)': 'вул. $1$2', // normalize, but ignore Lviv rules
            '(^|.+?) ?улица ?(.+|$)': 'вул. $1$2', // translate, but ignore Lviv rules
            '^(.+) в?ул\\.?$': 'вул. $1', // normalize and translate, but ignore Lviv rules
            '^в?ул.? (.+)$': 'вул. $1', // normalize and translate, but ignore Lviv rules
            '(^| )дорога( |$)': '$1дор.$2', // normalize
            '(^| )мікрорайон( |$)': '$1мкрн.$2', // normalize
            '(^| )набережна( |$)': '$1наб.$2', // normalize
            '(^| )площадь( |$)': '$1площа$2', // translate
            '(^| )провулок провулок( |$)': '$1пров.$2', // O_o
            '(^| )провулок( |$)': '$1пров.$2', // normalize
            //'(^| )проїзд( |$)': '$1пр.$2',          // normalize
            '(^| )проспект( |$)': '$1просп.$2', // normalize
            '(^| )район( |$)': '$1р-н$2', // normalize
            '(^| )станція( |$)': '$1ст.$2', // normalize
        };
        for (let key in regs) {
            let re = new RegExp(key, 'gi');
            if (re.test(street)) {
                street = street.replace(re, regs[key]);
                break;
            }
        }
        return street;
    }
    /**
     * Normalize the number by UA rules
     */
    function normalizeNumber(number) {
        // invalid data as a number
        if (number?.trim().length > 16) {
            return '';
        }
        // process "д."
        number = number.replace(/^д\. ?/i, '');
        // process "дом"
        number = number.replace(/^дом ?/i, '');
        // process "буд."
        number = number.replace(/^буд\. ?/i, '');
        // remove spaces
        number = number.trim().replace(/\s/g, '');
        number = number.toUpperCase();
        // process Latin to Cyrillic
        number = number.replace('A', 'А');
        number = number.replace('B', 'В');
        number = number.replace('E', 'Е');
        number = number.replace('I', 'І');
        number = number.replace('K', 'К');
        number = number.replace('M', 'М');
        number = number.replace('H', 'Н');
        number = number.replace('О', 'О');
        number = number.replace('P', 'Р');
        number = number.replace('C', 'С');
        number = number.replace('T', 'Т');
        number = number.replace('Y', 'У');
        // process і, з, о
        number = number.replace('І', 'і');
        number = number.replace('З', 'з');
        number = number.replace('О', 'о');
        // process "корпус" to "к"
        number = number.replace(/(.*)к(?:орп|орпус)?(\d+)/gi, '$1к$2');
        // process "N-M" or "N/M" to "NM"
        number = number.replace(/(.*)[-/]([а-яі])/gi, '$1$2');
        // valid number format
        //  123А  123А/321 123А/321Б 123к1 123Ак2
        /*if (!number.match(/^\d+[а-яі]?([/к]\d+[а-яі]?)?$/gi)) {
          return ''
        }*/
        return number;
    }
    /**
     * Sørensen-Dice coefficient similarity
     * @link https://github.com/aceakash/string-similarity
     */
    function compareTwoStrings(first, second) {
        first = first.replace(/\s+/g, '');
        second = second.replace(/\s+/g, '');
        if (!first.length && !second.length)
            return 1; // if both are empty strings
        if (!first.length || !second.length)
            return 0; // if only one is empty string
        if (first === second)
            return 1; // identical
        if (first.length === 1 && second.length === 1)
            return 0; // both are 1-letter strings
        if (first.length < 2 || second.length < 2)
            return 0; // if either is a 1-letter string
        let firstBigrams = new Map();
        for (let i = 0; i < first.length - 1; i++) {
            const bigram = first.substring(i, i + 2);
            const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) + 1 : 1;
            firstBigrams.set(bigram, count);
        }
        let intersectionSize = 0;
        for (let i = 0; i < second.length - 1; i++) {
            const bigram = second.substring(i, i + 2);
            const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) : 0;
            if (count > 0) {
                firstBigrams.set(bigram, count - 1);
                intersectionSize++;
            }
        }
        return (2.0 * intersectionSize) / (first.length + second.length - 2);
    }
    /**
     * Find the best match from an array of target strings
     */
    function findBestMatch(mainString, targetStrings) {
        let bestMatch = '';
        let bestMatchRating = 0;
        let bestMatchIndex = -1;
        for (let i = 0; i < targetStrings.length; i++) {
            let rating = compareTwoStrings(mainString, targetStrings[i]);
            if (rating > bestMatchRating) {
                bestMatch = targetStrings[i];
                bestMatchRating = rating;
                bestMatchIndex = i;
            }
        }
        if (bestMatch === '' || bestMatchRating < 0.35) {
            console.log('❌', mainString, '🆚', targetStrings);
            return -1;
        }
        else {
            console.log('✅', mainString, '🆚', bestMatch, ':', bestMatchRating);
            return bestMatchIndex;
        }
    }

    /**
     * Search the city name from available in the editor area
     */
    function detectCity(wmeSDK, cityName) {
        // Get the list of all available cities
        let cities = wmeSDK.DataModel.Cities.getAll()
            .filter((city) => city.name);
        console.log("Total found " + cities.length + " cities.");
        // More than one city, use city with best matching score
        // Remove text in the "()"; Waze puts the region name to the pair brackets
        let best = findBestMatch(cityName, cities.map((city) => city.name.replace(/( ?\(.*\))/gi, '')));
        if (best > -1) {
            console.info("✅ City detected");
            return [cities[best]['id'], cities[best]['name']];
            /*} else if (cities.length === 1) {
              console.info("❎ City doesn't found, uses default city")
              return [cities[0]['id'], cities[0]['name']]*/
        }
        else {
            console.info("❌ City doesn't found");
            return [null, cityName];
        }
    }
    /**
     * Search the street name from available in the editor area
     */
    function detectStreet(wmeSDK, cityId, streetName) {
        // It can be empty
        if (streetName.trim() === '') {
            return [null, null];
        }
        // Get all streets
        let streets = wmeSDK.DataModel.Streets.getAll()
            .filter((street) => street.cityId === cityId)
            .filter((street) => street.name);
        // Get type and create RegExp for filter streets
        let reTypes = new RegExp('(алея|б-р|в\'їзд|вул\\.|дор\\.|мкрн|наб\\.|площа|пров\\.|проїзд|просп\\.|р-н|ст\\.|тракт|траса|тупик|узвіз|шосе)', 'gi');
        let matches = [...streetName.matchAll(reTypes)];
        let types = [];
        // Detect type(s)
        if (matches.length === 0) {
            types.push('вул.'); // set up a basic type
            streetName = 'вул. ' + streetName;
        }
        else {
            types = matches.map(match => match[0].toLowerCase());
        }
        // Filter streets by detected type(s)
        let filteredStreets = streets.filter((street) => types.some(type => street.name.indexOf(type) > -1));
        // Matching names without type(s)
        let best = findBestMatch(streetName.replace(reTypes, '').toLowerCase().trim(), filteredStreets.map((street) => street.name.replace(reTypes, '').toLowerCase().trim()));
        if (best > -1) {
            return [filteredStreets[best]['id'], filteredStreets[best]['name']];
        }
        else {
            return [null, streetName];
        }
    }

    let E50Cache;
    function setCache(cache) { E50Cache = cache; }
    class Provider {
        constructor(uid, container, settings, scriptSettings, wmeSDK) {
            this.uid = uid.trim().toLowerCase().replace(/\s/g, '-');
            this.name = uid;
            this.response = [];
            this.settings = settings;
            this.scriptSettings = scriptSettings;
            this.wmeSDK = wmeSDK;
            // prepare DOM
            this.panel = this._panel();
            this.container = container;
            this.container.append(this.panel);
        }
        /**
         * @param {String} url
         * @param {Object} data
         * @returns {Promise<unknown>}
         */
        async makeRequest(url, data) {
            let query = new URLSearchParams(data).toString();
            if (query.length) {
                url = url + '?' + query;
            }
            console.log(url);
            return new Promise((resolve, reject) => {
                GM.xmlHttpRequest({
                    method: 'GET',
                    responseType: 'json',
                    url: url,
                    onload: (response) => {
                        if (response && response.response) {
                            resolve(response.response);
                        }
                        else {
                            reject(response);
                        }
                    },
                    onabort: () => reject('aborted'),
                    onerror: (response) => reject(response),
                    ontimeout: () => reject('timeout'),
                });
            });
        }
        /**
         * @param  {Number} lon
         * @param  {Number} lat
         * @param  {Number} radius
         * @return {Promise<array>}
         */
        async request(lon, lat, radius) {
            throw new Error('Abstract method');
        }
        /**
         * @param  {Number} lon
         * @param  {Number} lat
         * @param  {Number} radius
         * @return {Promise<void>}
         */
        async search(lon, lat, radius = 1000) {
            let key = this.uid + ':' + lon + ',' + lat;
            if (E50Cache.has(key)) {
                console.log('E50 Cache hit for ' + key);
                this.response = E50Cache.get(key);
            }
            else {
                console.log('E50 Cache miss for ' + key);
                this.response = await this.request(lon, lat, radius).catch(e => { console.error(this.uid, 'search return error', e); return []; });
                E50Cache.set(key, this.response);
            }
            return new Promise((resolve, reject) => {
                if (this.response) {
                    resolve();
                }
                else {
                    reject();
                }
            });
        }
        /**
         * @param  {Array} res
         * @return {Array}
         */
        collection(res) {
            let result = [];
            for (let i = 0; i < res.length; i++) {
                result.push(this.item(res[i]));
            }
            result = result.filter(x => x);
            return result;
        }
        /**
         * Should return {Object}
         * @param  {Object} res
         * @return {Object}
         */
        item(res) {
            throw new Error('Abstract method');
        }
        /**
         * @param  {Number} lon
         * @param  {Number} lat
         * @param  {String} city
         * @param  {String} street
         * @param  {String} number
         * @param  {String} name
         * @param  {String} reference
         * @return {{number: *, cityId: Number, cityName: *, streetId: Number, streetName: *, name: *, raw: *, lon: *, title: *, lat: *}}
         */
        element(lon, lat, city, street, number, name = '', reference = '') {
            // Raw data from provider
            let raw = [city, street, number, name].filter(x => !!x).join(', ');
            {
                city = normalizeCity(city);
                street = normalizeStreet(street);
                number = normalizeNumber(number);
                name = normalizeName(name);
            }
            let [cityId, cityName] = detectCity(this.wmeSDK, city);
            let [streetId, streetName] = detectStreet(this.wmeSDK, cityId, street);
            if (!cityId && streetId) {
                let streetModel = this.wmeSDK.DataModel.Streets.getById({ streetId: streetId });
                let cityModel = this.wmeSDK.DataModel.Cities.getById({ cityId: streetModel.cityId });
                cityId = cityModel.id;
                cityName = cityModel.name;
            }
            let title = [street, number, name].filter(x => !!x).join(', ');
            return {
                lat: lat,
                lon: lon,
                cityId: cityId,
                cityName: cityName,
                streetId: streetId,
                streetName: streetName,
                number: number,
                name: name,
                title: title,
                raw: raw,
                reference: reference
            };
        }
        /**
         * Render result to target element
         */
        render() {
            if (this.response.length === 0) {
                // remove empty panel
                this.panel.remove();
                return;
            }
            this.panel.append(this._fieldset());
        }
        /**
         * Create div for all items
         * @return {HTMLDivElement}
         * @private
         */
        _panel() {
            let div = document.createElement('div');
            div.id = NAME.toLowerCase() + '-' + this.name;
            div.className = NAME.toLowerCase();
            return div;
        }
        /**
         * Build fieldset with the list of the response items
         * @return {HTMLFieldSetElement}
         * @protected
         */
        _fieldset() {
            let fieldset = document.createElement('fieldset');
            let list = document.createElement('ul');
            let collapse = parseInt(this.scriptSettings.get('ranges', 'collapse'));
            if (collapse && this.response.length > collapse) {
                fieldset.className = 'collapsed';
            }
            else {
                fieldset.className = '';
            }
            for (let i = 0; i < this.response.length; i++) {
                let item = document.createElement('li');
                item.append(this._link(this.response[i]));
                list.append(item);
            }
            let legend = document.createElement('legend');
            legend.innerHTML = this.name + ' <span>' + this.response.length + '</span>';
            legend.onclick = function () {
                this.parentElement.classList.toggle("collapsed");
                return false;
            };
            fieldset.append(legend, list);
            return fieldset;
        }
        /**
         * Build link by {Object}
         * @param  {Object} item
         * @return {HTMLAnchorElement}
         * @protected
         */
        _link(item) {
            let a = document.createElement('a');
            a.href = '#';
            a.dataset.lat = item.lat;
            a.dataset.lon = item.lon;
            a.dataset.cityId = item.cityId || '';
            a.dataset.cityName = item.cityName || '';
            a.dataset.streetId = item.streetId || '';
            a.dataset.streetName = item.streetName || '';
            a.dataset.number = item.number;
            a.dataset.name = item.name;
            a.dataset.reference = item.reference || '';
            a.innerText = item.title || item.raw;
            a.title = item.raw;
            a.className = NAME + '-link';
            if (!item.cityId || !item.streetId) {
                a.className += ' noaddress';
            }
            if (!item.number) {
                a.className += ' nonumber';
            }
            return a;
        }
    }

    /**
     * Based on the closest segment and city
     */
    class MagicProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK) {
            super(WMEUI.t(NAME).providers.magic, container, settings, scriptSettings, wmeSDK);
        }
        async request(lon, lat, radius) {
            let segments = this.wmeSDK.DataModel.Segments.getAll();
            let except = [TYPES.boardwalk, TYPES.stairway, TYPES.railroad, TYPES.runway, TYPES.parking];
            segments = segments.filter((segment) => except.indexOf(segment.roadType) === -1);
            let streets = {};
            console.groupCollapsed(this.uid);
            for (let key in segments) {
                let segment = segments[key];
                let address = this.wmeSDK.DataModel.Segments.getAddress({ segmentId: segment.id });
                if (address.street.name === '') {
                    continue;
                }
                let distance = turf.pointToLineDistance(turf.point([lon, lat]), segment.geometry, {
                    units: 'meters'
                });
                if (!streets[address.street.id]
                    || distance < streets[address.street.id].distance) {
                    let nearestPointOnLine = turf.nearestPointOnLine(segment.geometry, turf.point([lon, lat]));
                    streets[address.street.id] = {
                        lon: nearestPointOnLine.geometry.coordinates[0],
                        lat: nearestPointOnLine.geometry.coordinates[1],
                        streetId: address.street.id,
                        streetName: address.street.name,
                        cityId: address.city.id,
                        cityName: address.city.name,
                        number: '',
                        name: '',
                        title: address.street.name,
                        raw: address.city.name + ', ' + address.street.name,
                        distance: distance,
                    };
                }
            }
            let result = [];
            for (let key in streets) {
                if (streets.hasOwnProperty(key) && streets[key].distance <= radius) {
                    result.push(streets[key]);
                }
            }
            result.sort((a, b) => {
                if (a.distance < b.distance) {
                    return -1;
                }
                if (a.distance > b.distance) {
                    return 1;
                }
                return 0;
            });
            console.log(result.length + ' streets found.');
            console.groupEnd();
            return result;
        }
    }

    /**
     * UA Addresses
     */
    class UaAddressesProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK, key) {
            super(WMEUI.t(NAME).providers.ua, container, settings, scriptSettings, wmeSDK);
            this.key = key;
        }
        async request(lon, lat, radius) {
            let result = [];
            let url = 'https://stat.waze.com.ua/address_map/address_map.php';
            let data = {
                lon: lon,
                lat: lat,
                radius: radius,
                limit: 20,
                script: this.key
            };
            let response = await this.makeRequest(url, data).catch(e => console.error(this.uid, 'return error', e));
            console.groupCollapsed(this.uid);
            if (response?.result && response.result === 'success') {
                result = this.collection(response.data.polygons.Default);
            }
            else {
                console.info('No response returned');
            }
            console.groupEnd();
            return result;
        }
        item(res) {
            let data = res.name.split(",");
            data = data.map((part) => part.trim());
            let number = data.length ? data.pop() : null;
            let street = data.length ? data.pop() : null;
            let city = data.length ? data.pop() : null;
            // https://cdn.jsdelivr.net/npm/[email protected]/wellknown.min.js
            // let element = wellknown.parse(res.polygon);
            // let center = turf.centroid(element)
            //  center.geometry.coordinates[0],
            //  center.geometry.coordinates[1],
            let [lat, lon] = res.center.split(';');
            return this.element(lon, lat, city, street, number);
        }
    }

    /**
     * visicom.ua
     */
    class VisicomProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK, key) {
            super(WMEUI.t(NAME).providers.visicom, container, settings, scriptSettings, wmeSDK);
            this.key = key;
        }
        async request(lon, lat, radius) {
            let result = [];
            let url = 'https://api.visicom.ua/data-api/5.0/uk/geocode.json';
            let data = {
                near: lon + ',' + lat,
                categories: 'adr_address',
                order: 'distance',
                radius: radius,
                limit: 10,
                key: this.key,
            };
            let response = await this.makeRequest(url, data).catch(e => console.error(this.uid, 'return error', e));
            console.groupCollapsed(this.uid);
            if (response?.features?.length > 0) {
                result = this.collection(response.features);
            }
            else {
                console.info('No response returned');
                if (response?.status) {
                    console.info('Status:', response.status);
                }
            }
            console.groupEnd();
            return result;
        }
        item(res) {
            let city = '';
            let street = '';
            let number = '';
            if (res.properties.settlement) {
                city = res.properties.settlement;
            }
            if (res.properties.street) {
                street = res.properties.street_type + ' ' + res.properties.street;
            }
            if (res.properties.name) {
                number = res.properties.name;
            }
            return this.element(res.geo_centroid.coordinates[0], res.geo_centroid.coordinates[1], city, street, number);
        }
    }

    /**
     * OpenStreetMap
     */
    class OsmProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK) {
            super(WMEUI.t(NAME).providers.osm, container, settings, scriptSettings, wmeSDK);
        }
        async request(lon, lat, radius) {
            let result = [];
            let url = 'https://nominatim.openstreetmap.org/reverse';
            let data = {
                lon: lon,
                lat: lat,
                zoom: 18,
                addressdetails: 1,
                countrycodes: this.settings.language,
                'accept-language': this.settings.locale,
                format: 'json',
            };
            let response = await this.makeRequest(url, data).catch(e => console.error(this.uid, 'return error', e));
            console.groupCollapsed(this.uid);
            if (response?.address) {
                result = [this.item(response)];
            }
            else {
                console.info('No response returned');
            }
            console.groupEnd();
            return result;
        }
        item(res) {
            let city = '';
            let street = '';
            let number = '';
            if (res.address.city) {
                city = res.address.city;
            }
            else if (res.address.town) {
                city = res.address.town;
            }
            if (res.address.road) {
                street = res.address.road;
            }
            if (res.address.house_number) {
                number = res.address.house_number;
            }
            return this.element(res.lon, res.lat, city, street, number);
        }
    }

    /**
     * Here Maps
     * @link https://developer.here.com/documentation/geocoder/topics/quick-start-geocode.html
     * @link https://www.here.com/docs/bundle/geocoder-api-developer-guide/page/topics/resource-reverse-geocode.html
     */
    class HereProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK, key) {
            super(WMEUI.t(NAME).providers.here, container, settings, scriptSettings, wmeSDK);
            this.key = key;
        }
        async request(lon, lat, radius) {
            let result = [];
            let url = 'https://revgeocode.search.hereapi.com/v1/revgeocode';
            let data = {
                apiKey: this.key,
                at: lat + ',' + lon,
                types: 'address',
                limit: 20
            };
            let response = await this.makeRequest(url, data).catch(e => console.error(this.uid, 'return error', e));
            console.groupCollapsed(this.uid);
            if (response?.items?.length) {
                result = this.collection(response.items.filter((x) => x.resultType === 'houseNumber'));
            }
            else {
                console.info('No response returned');
            }
            console.groupEnd();
            return result;
        }
        item(res) {
            console.log(res);
            return this.element(res.position.lng, res.position.lat, res.address.city, res.address.street, res.address.houseNumber);
        }
    }

    /**
     * Bing Maps
     * @link https://docs.microsoft.com/en-us/bingmaps/rest-services/locations/find-a-location-by-point
     * http://dev.virtualearth.net/REST/v1/Locations/50.03539,36.34732?o=xml&key=AuBfUY8Y1Nzf3sRgceOYxaIg7obOSaqvs0k5dhXWfZyFpT9ArotYNRK7DQ_qZqZw&c=uk
     * http://dev.virtualearth.net/REST/v1/Locations/50.03539,36.34732?o=xml&key=AuBfUY8Y1Nzf3sRgceOYxaIg7obOSaqvs0k5dhXWfZyFpT9ArotYNRK7DQ_qZqZw&c=uk&includeEntityTypes=Address
     */
    class BingProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK, key) {
            super(WMEUI.t(NAME).providers.bing, container, settings, scriptSettings, wmeSDK);
            this.key = key;
        }
        async request(lon, lat, radius) {
            let result = [];
            let url = 'https://dev.virtualearth.net/REST/v1/Locations/' + lat + ',' + lon;
            let data = {
                includeEntityTypes: 'Address',
                c: this.settings.country,
                key: this.key,
            };
            let response = await this.makeRequest(url, data).catch(e => console.error(this.uid, 'return error', e));
            console.groupCollapsed(this.uid);
            if (response?.resourceSets?.[0]?.resources?.length) {
                result = this.collection(response.resourceSets[0].resources.filter((el) => el.address?.addressLine?.includes(',')));
            }
            else {
                console.info('No response returned');
            }
            console.groupEnd();
            return result;
        }
        item(res) {
            let address = res.address.addressLine.split(',');
            return this.element(res.point.coordinates[1], res.point.coordinates[0], res.address.locality, address[0], address[1]);
        }
    }

    /**
     * Google Place
     * @link https://developers.google.com/places/web-service/search
     */
    class GoogleProvider extends Provider {
        constructor(container, settings, scriptSettings, wmeSDK, key) {
            super(WMEUI.t(NAME).providers.google, container, settings, scriptSettings, wmeSDK);
            this.key = key;
        }
        async request(lon, lat, radius) {
            let result = [];
            let response = await this.makeAPIRequest(lat, lon, radius)
                .catch(e => null);
            //.catch(e => console.error(this.uid, 'return error', e))
            console.groupCollapsed(this.uid);
            if (response?.length) {
                result = this.collection(response);
            }
            else {
                console.info('No response returned');
            }
            console.groupEnd();
            return result;
        }
        async makeAPIRequest(lat, lon, radius) {
            let center = new google.maps.LatLng(lat, lon);
            let map = new google.maps.Map(document.createElement('div'), { center: center });
            let request = {
                location: center,
                radius: radius,
                type: 'point_of_interest',
                // doesn't work
                // fields: ['name', 'address_component', 'geometry'],
                // language: this.settings.country,
            };
            let service = new google.maps.places.PlacesService(map);
            return new Promise((resolve, reject) => {
                service.nearbySearch(request, (results, status) => {
                    if (status === google.maps.places.PlacesServiceStatus.OK) {
                        resolve(results);
                    }
                    else {
                        reject(status);
                    }
                });
            });
        }
        item(res) {
            let address = res.vicinity.split(',');
            address = address.map((str) => str.trim());
            // looks like hell
            let street = address[0] && address[0].length > 4 ? address[0] : '';
            let number = address[1] && address[1].length < 13 ? address[1] : '';
            let city = address[2] ? address[2] : '';
            return this.element(res.geometry.location.lng(), res.geometry.location.lat(), city, street, number, res.name, res.reference);
        }
        /**
         * Details about a specific object or entity.
         *
         * This variable is used to encapsulate information or attributes
         * related to a particular subject. The structure and type of the
         * details may vary depending on the specific application or use-case.
         */
        static async makeDetailsRequest(reference) {
            // We need a map instance to initialize the service (even a dummy one)
            let map = new google.maps.Map(document.createElement('div'));
            let service = new google.maps.places.PlacesService(map);
            let request = {
                placeId: reference, // Google now uses placeId instead of reference
                // Specifying fields is cheaper and faster
                fields: ['business_status', 'geometry', 'name', 'place_id', 'vicinity']
            };
            return new Promise((resolve, reject) => {
                service.getDetails(request, (place, status) => {
                    if (status === google.maps.places.PlacesServiceStatus.OK) {
                        resolve(place);
                    }
                    else {
                        reject(status);
                    }
                });
            });
        }
    }

    class E50 extends WMEBase {
        constructor(name, settings) {
            super(name, settings);
            this.initTab();
            this.initLayer();
        }
        initTab() {
            this.modal = this.helper.createModal(WMEUI.t(NAME).title);
            this.panel = this.helper.createPanel(WMEUI.t(NAME).title);
            let tab = this.helper.createTab(WMEUI.t(NAME).title, {
                sidebar: this.wmeSDK.Sidebar,
                image: GM_info.script.icon
            });
            // Setup options
            /** @type {WMEUIHelperFieldset} */
            let fsOptions = this.helper.createFieldset(WMEUI.t(NAME).options.title);
            let options = this.settings.get('options');
            let checkboxes = {};
            for (let item in options) {
                if (options.hasOwnProperty(item)) {
                    checkboxes[item] = {
                        title: WMEUI.t(NAME).options[item],
                        callback: (event) => this.settings.set('options', item, event.target.checked),
                        checked: this.settings.get('options', item),
                    };
                }
            }
            fsOptions.addCheckboxes(checkboxes);
            tab.addElement(fsOptions);
            // Setup ranges
            /** @type {WMEUIHelperFieldset} */
            let fsRanges = this.helper.createFieldset(WMEUI.t(NAME).ranges.title);
            let ranges = this.settings.get('ranges');
            for (let item in ranges) {
                if (ranges.hasOwnProperty(item)) {
                    fsRanges.addNumber('settings-ranges-' + item, WMEUI.t(NAME).ranges[item], (event) => this.settings.set('ranges', item, event.target.value), this.settings.get('ranges', item), (item === 'radius') ? 100 : 0, (item === 'radius') ? 1000 : 10, (item === 'radius') ? 50 : 1);
                }
            }
            tab.addElement(fsRanges);
            // Setup providers settings
            /** @type {WMEUIHelperFieldset} */
            let fsProviders = this.helper.createFieldset(WMEUI.t(NAME).providers.title);
            let providers = this.settings.get('providers');
            let providerCheckboxes = {};
            for (let item in providers) {
                if (providers.hasOwnProperty(item) && SETTINGS.providers.hasOwnProperty(item)) {
                    providerCheckboxes[item] = {
                        title: WMEUI.t(NAME).providers[item],
                        callback: (event) => this.settings.set('providers', item, event.target.checked),
                        checked: this.settings.get('providers', item),
                    };
                }
            }
            fsProviders.addCheckboxes(providerCheckboxes);
            tab.addElement(fsProviders);
            // Set up provider's keys
            /** @type {WMEUIHelperFieldset} */
            let fsKeys = this.helper.createFieldset(WMEUI.t(NAME).options.keys);
            let keys = this.settings.get('keys');
            for (let item in keys) {
                if (keys.hasOwnProperty(item) && SETTINGS.keys.hasOwnProperty(item)) {
                    fsKeys.addInput('key-' + item, WMEUI.t(NAME).providers[item], (event) => this.settings.set('keys', item, event.target.value), this.settings.get('keys', item));
                }
            }
            tab.addElement(fsKeys);
            tab.addText('info', '<a href="' + GM_info.scriptUpdateURL + '">' + GM_info.script.name + '</a> ' + GM_info.script.version);
            tab.addText('blue', 'made in');
            tab.addText('yellow', 'Ukraine');
            tab.inject();
        }
        initLayer() {
            this.wmeSDK.Map.addLayer({
                layerName: this.name,
                styleRules: layerConfig.styleRules,
                styleContext: layerConfig.styleContext
            });
            // this.wmeSDK.LayerSwitcher.addLayerCheckbox({ name: this.name });
            this.wmeSDK.Map.setLayerZIndex({ layerName: this.name, zIndex: 9999 });
            this.wmeSDK.Map.setLayerVisibility({ layerName: this.name, visibility: false });
        }
        /**
         * Create the vector from the center of the selected POI to point by lon and lat
         * @param {Number} lon
         * @param {Number} lat
         */
        createVector(lon, lat) {
            let poi = this.getSelectedPOI();
            if (!poi) {
                return;
            }
            const from = turf.centroid(poi.geometry);
            const to = turf.point([lon, lat], { styleName: "styleNode" }, { id: `node_${lon}_${lat}` });
            this.wmeSDK.Map.addFeatureToLayer({ layerName: this.name, feature: to });
            const lineCoordinates = [
                from.geometry.coordinates,
                to.geometry.coordinates,
            ];
            const distance = Math.round(turf.distance(to, from) * 1000);
            const label = (distance > 2000)
                ? (distance / 1000).toFixed(1) + 'km'
                : distance + 'm';
            // https://www.waze.com/editor/sdk/interfaces/index.SDK.FeatureStyle.html
            const line = turf.lineString(lineCoordinates, {
                styleName: "styleLine",
                style: {
                    label: label,
                },
            }, { id: `line_${lon}_${lat}` });
            this.wmeSDK.Map.addFeatureToLayer({ layerName: this.name, feature: line });
        }
        /**
         * Remove all vectors from the layer
         */
        removeVectors() {
            this.wmeSDK.Map.removeAllFeaturesFromLayer({ layerName: this.name });
        }
        /**
         * Show the Layer
         */
        showLayer() {
            this.wmeSDK.Map.setLayerVisibility({ layerName: this.name, visibility: true });
        }
        /**
         * Hide the Layer
         */
        hideLayer() {
            this.wmeSDK.Map.setLayerVisibility({ layerName: this.name, visibility: false });
        }
        /**
         * Handler for `none.wme` event
         * @param {jQuery.Event} event
         * @return {Null}
         */
        onNone(event) {
            if (this.settings.get('options', 'modal')) {
                this.modal.remove();
            }
        }
        /**
         * Handler for `venue.wme` event
         *  - create and fill the modal panel
         *
         * @param {jQuery.Event} event
         * @param {HTMLElement} element
         * @param {Venue} model
         * @return {null|void}
         */
        onVenue(event, element, model) {
            let container, parent;
            if (this.settings.get('options', 'modal')) {
                parent = this.modal.html();
                container = parent.querySelector('.wme-ui-modal-content');
            }
            else {
                parent = this.panel.html();
                container = parent.querySelector('.wme-ui-panel-content');
            }
            // Clear container
            try {
                if (container)
                    while (container.hasChildNodes()) {
                        container.removeChild(container.lastChild);
                    }
            }
            catch (e) {
                console.error(e);
            }
            if (!model) {
                return;
            }
            let feature = turf.centroid(model.geometry);
            let [lon, lat] = feature.geometry.coordinates;
            let providers = [];
            let country = this.wmeSDK.DataModel.Countries.getTopCountry()?.id || 232;
            let settings = LOCALE[country] || { country: 'en', language: 'en', locale: 'en_US' };
            this.group('\u{1F4CD}' + lon + ' ' + lat);
            let radius = this.settings.get('ranges', 'radius');
            if (this.settings.get('providers', 'magic')) {
                let Magic = new MagicProvider(container, settings, this.settings, this.wmeSDK);
                let providerPromise = Magic
                    .search(lon, lat, radius)
                    .then(() => Magic.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'ua')) {
                let UaAddresses = new UaAddressesProvider(container, settings, this.settings, this.wmeSDK, this.settings.get('keys', 'ua'));
                let providerPromise = UaAddresses
                    .search(lon, lat, radius)
                    .then(() => UaAddresses.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'osm')) {
                let Osm = new OsmProvider(container, settings, this.settings, this.wmeSDK);
                let providerPromise = Osm
                    .search(lon, lat, radius)
                    .then(() => Osm.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'visicom')) {
                let Visicom = new VisicomProvider(container, settings, this.settings, this.wmeSDK, this.settings.get('keys', 'visicom'));
                let providerPromise = Visicom
                    .search(lon, lat, radius)
                    .then(() => Visicom.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'here')) {
                let Here = new HereProvider(container, settings, this.settings, this.wmeSDK, this.settings.get('keys', 'here'));
                let providerPromise = Here
                    .search(lon, lat, radius)
                    .then(() => Here.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'bing')) {
                let Bing = new BingProvider(container, settings, this.settings, this.wmeSDK, this.settings.get('keys', 'bing'));
                let providerPromise = Bing
                    .search(lon, lat, radius)
                    .then(() => Bing.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('providers', 'google')) {
                let Google = new GoogleProvider(container, settings, this.settings, this.wmeSDK, this.settings.get('keys', 'google'));
                let providerPromise = Google
                    .search(lon, lat, radius)
                    .then(() => Google.render())
                    .catch(() => this.log(':('));
                providers.push(providerPromise);
            }
            if (this.settings.get('options', 'externalProvider')) {
                if (model.externalProviderIds?.length) {
                    let items = element.querySelectorAll('.external-providers-control .external-provider');
                    for (let i = 0; i < model.externalProviderIds.length; i++) {
                        let externalProviderId = model.externalProviderIds[i];
                        let item = items[i];
                        GoogleProvider
                            .makeDetailsRequest(externalProviderId)
                            .then((details) => {
                            let extLat = details.geometry.location.lat();
                            let extLng = details.geometry.location.lng();
                            let distance = turf.distance(turf.point([lon, lat]), turf.point([extLng, extLat]), {
                                units: 'meters'
                            });
                            item.dataset.distance = Math.round(distance);
                            item.dataset.lat = extLat;
                            item.dataset.lon = extLng;
                            if (details.business_status === 'OPERATIONAL') {
                                item.classList.add('external-operational');
                            }
                            else if (details.business_status === 'CLOSED_TEMPORARILY') {
                                item.classList.add('external-closed-temporarily');
                            }
                            else if (details.business_status === 'CLOSED_PERMANENTLY') {
                                item.classList.add('external-closed-permanently');
                            }
                            item.classList.add(this.name + '-external');
                            if (distance > 1000) {
                                item.classList.add('distance-over-1000');
                            }
                            else if (distance > 200) {
                                item.classList.add('distance-over-200');
                            }
                        })
                            .catch(() => { this.log(':('); });
                    }
                }
            }
            Promise
                .all(providers)
                .then(() => this.groupEnd());
            if (this.settings.get('options', 'modal')) {
                if (this.settings.get('options', 'transparent')) {
                    parent.style.opacity = '0.6';
                    parent.onmouseover = () => (parent.style.opacity = '1');
                    parent.onmouseout = () => (parent.style.opacity = '0.6');
                }
                this.modal.container().append(parent);
            }
            else {
                element.prepend(parent);
            }
        }
        /**
         * Get Selected Venue if it not the NATURAL_FEATURES
         * @return {null|Object}
         */
        getSelectedPOI() {
            let venue = this.getSelectedVenues().shift();
            if (!venue) {
                return null;
            }
            let except = [
                'CAMPING_TRAILER_PARK',
                'FOREST_GROVE',
                'JUNCTION_INTERCHANGE',
                'NATURAL_FEATURES',
                'OUTDOORS',
                'PARKING_LOT',
                'PLAYGROUND',
            ];
            if (except.indexOf(venue.categories[0]) === -1) {
                return venue;
            }
            return null;
        }
        /**
         * Apply data to the current selected place
         * @param {Object} data
         */
        applyData(data) {
            let venue = this.getSelectedPOI();
            if (!this.canEditVenue(venue)) {
                this.log('You don\'t have permissions to edit this venue');
                return;
            }
            let address = this.wmeSDK.DataModel.Venues.getAddress({ venueId: venue.id });
            let lat = parseFloat(data.lat);
            let lon = parseFloat(data.lon);
            if (isNaN(lat) || isNaN(lon)) {
                this.log('Invalid coordinates');
                return;
            }
            this.group('Apply data to selected Venue \u2193');
            let name = data.name ? data.name.trim() : '';
            let cityId = isNaN(parseInt(data.cityId)) ? null : parseInt(data.cityId);
            let cityName = data.cityName ? data.cityName.trim() : '';
            let streetId = isNaN(parseInt(data.streetId)) ? null : parseInt(data.streetId);
            let streetName = data.streetName ? data.streetName.trim() : '';
            let number = data.number ? data.number.trim() : '';
            if (this.settings.get('options', 'copyData')) {
                toClipboard([name, number, streetName, cityName].filter(x => !!x).join(' '));
            }
            // Apply new Name
            let newName;
            // If exists, ask the user to replace it or not
            // If not exists - use name or house number as name
            if (venue.name) {
                this.log('The Venue has a Name \u00AB' + venue.name + '\u00BB');
                if (name && name !== venue.name) {
                    this.log('Replace a Venue Name with a new one?');
                    if (window.confirm(WMEUI.t(NAME).questions.changeName + '\n\u00AB' + venue.name + '\u00BB \u27F6 \u00AB' + name + '\u00BB?')) {
                        newName = name;
                        this.log(' \u2014 Yes, a new Venue Name is \u00AB' + newName + '\u00BB');
                    }
                    else {
                        newName = venue.name;
                        this.log(' \u2014 No, use a old Venue Name \u00AB' + newName + '\u00BB');
                    }
                }
                else if (number && number !== venue.name) {
                    this.log('Replace the Venue Name with a number?');
                    if (window.confirm(WMEUI.t(NAME).questions.changeName + '\n\u00AB' + venue.name + '\u00BB \u27F6 \u00AB' + number + '\u00BB?')) {
                        newName = number;
                        this.log(' \u2014 Yes, a new Venue Name is \u00AB' + newName + '\u00BB');
                    }
                    else {
                        newName = venue.name;
                        this.log(' \u2014 No, use a old Venue Name \u00AB' + newName + '\u00BB');
                    }
                }
            }
            else if (name) {
                newName = name;
                this.log('Use a new Venue Name \u00AB' + newName + '\u00BB');
            }
            else if (number) {
                newName = number;
                this.log('Use a new Venue Name \u00AB' + newName + '\u00BB');
                // Update alias for korpus
                if ((new RegExp('[0-9]+[\u0430-\u044F\u0456]?\u043A[0-9]+', 'i')).test(number)) {
                    let alias = number.replace('\u043A', ' \u043A\u043E\u0440\u043F\u0443\u0441 ');
                    let aliases = venue.aliases?.slice() || [];
                    if (aliases.indexOf(alias) === -1) {
                        aliases.push(alias);
                        this.log('Apply a new Venue Alias \u00AB' + alias + '\u00BB');
                        this.wmeSDK.DataModel.Venues.updateVenue({
                            venueId: venue.id,
                            aliases: aliases
                        });
                    }
                }
            }
            // Set only really new name
            if (newName && newName !== venue.name) {
                this.log('Apply a new Venue Name \u00AB' + newName + '\u00BB');
                this.wmeSDK.DataModel.Venues.updateVenue({
                    venueId: venue.id,
                    name: newName
                });
            }
            // Apply a City name
            if (!cityId && cityName) {
                this.log('We don\'t find a City with name \u00AB' + cityName + '\u00BB, create a new one?');
                // Ask to create a new City
                if (window.confirm(WMEUI.t(NAME).questions.notFoundCity + '\n\u00AB' + cityName + '\u00BB?')) {
                    cityId = this.getCity(cityName).id;
                    this.log(' \u2014 Yes, create new City \u00AB' + cityName + '\u00BB');
                }
                else {
                    cityId = this.getCity().id;
                    this.log(' \u2014 No, use the empty City with ID \u00AB' + cityId + '\u00BB');
                }
            }
            else if (!cityId && !cityName) {
                cityId = this.getCity().id;
                this.log('We don\'t find a City and use the empty City with ID \u00AB' + cityId + '\u00BB');
            }
            let city = this.getCityById(cityId);
            let newStreetId;
            // Apply a new Street
            if (streetId && address.street
                && streetId !== address.street.id
                && '' !== address.street.name) {
                this.log('Replace the Street with a new one?');
                if (window.confirm(WMEUI.t(NAME).questions.changeStreet + '\n\u00AB' + address.street.name + '\u00BB \u27F6 \u00AB' + streetName + '\u00BB?')) {
                    newStreetId = streetId;
                    this.log(' \u2014 Yes, use a new Street Name \u00AB' + streetName + '\u00BB');
                }
                else {
                    this.log(' \u2014 No, use a old Street Name \u00AB' + address.street.name + '\u00BB');
                }
            }
            else if (streetId) {
                newStreetId = streetId;
                this.log('Use a new Street with ID \u00AB' + newStreetId + '\u00BB');
            }
            else if (!streetId) {
                let street;
                if (streetName) {
                    this.log('We don\'t find the street \u00AB' + streetName + '\u00BB');
                    this.log('Create a new Street?');
                    if (window.confirm(WMEUI.t(NAME).questions.notFoundStreet + '\n\u00AB' + streetName + '\u00BB?')) {
                        street = this.getStreet(city.id, streetName);
                        this.log(' \u2014 Yes, create a new Street \u00AB' + streetName + '\u00BB');
                    }
                    else if ('' !== address.street?.name) {
                        street = this.wmeSDK.DataModel.Streets.getById({ streetId: address.street.id });
                        this.log(' \u2014 No, use the current Street \u00AB' + street.name + '\u00BB');
                    }
                    else {
                        street = this.getStreet(city.id, '');
                        this.log(' \u2014 No, use the empty Street with ID \u00AB' + street.id + '\u00BB');
                    }
                }
                else {
                    this.log('We don\'t find the street');
                    street = this.getStreet(city.id, '');
                    this.log('Use the empty Street with ID \u00AB' + street.id + '\u00BB');
                }
                if (street.id !== address.street?.id && '' !== address.street?.name) {
                    this.log('Replace the Street with new one?');
                    if (window.confirm(WMEUI.t(NAME).questions.changeStreet + '\n\u00AB' + address.street.name + '\u00BB \u27F6 \u00AB' + streetName + '\u00BB?')) {
                        newStreetId = street.id;
                        this.log(' \u2014 Yes, use a new Street Name \u00AB' + streetName + '\u00BB');
                    }
                    else {
                        this.log(' \u2014 No, use the current Street Name \u00AB' + address.street.name + '\u00BB');
                    }
                }
                else {
                    newStreetId = street.id;
                }
            }
            if (newStreetId && newStreetId !== address.street?.id) {
                this.log('Apply a new Street ID \u00AB' + newStreetId + '\u00BB');
                this.wmeSDK.DataModel.Venues.updateAddress({
                    venueId: venue.id,
                    streetId: newStreetId
                });
            }
            let newHouseNumber;
            // Apply a House Number
            if (number) {
                if (address.houseNumber) {
                    this.log('Replace the House Number with a new one?');
                    if (address.houseNumber !== number &&
                        window.confirm(WMEUI.t(NAME).questions.changeNumber + '\n\u00AB' + address.houseNumber + '\u00BB \u27F6 \u00AB' + number + '\u00BB?')) {
                        newHouseNumber = number;
                        this.log(' \u2014 Yes, use a new House Number \u00AB' + number + '\u00BB');
                    }
                    else {
                        this.log(' \u2014 No, use the current House Number \u00AB' + address.houseNumber + '\u00BB');
                    }
                }
                else {
                    newHouseNumber = number;
                    this.log('Use a new House Number \u00AB' + number + '\u00BB');
                }
            }
            if (newHouseNumber) {
                this.log('Apply a new House Number \u00AB' + newHouseNumber + '\u00BB');
                this.wmeSDK.DataModel.Venues.updateAddress({
                    venueId: venue.id,
                    houseNumber: newHouseNumber
                });
            }
            // Lock to level 2
            if (this.settings.get('options', 'lock')
                && venue.lockRank < 1
                && this.wmeSDK.State.getUserInfo().rank > 0) {
                this.log('Apply a new Lock Rank \u00AB' + (1 + 1) + '\u00BB');
                this.wmeSDK.DataModel.Venues.updateVenue({
                    venueId: venue.id,
                    lockRank: 1
                });
            }
            // If no an entry point, we would create it
            if (this.settings.get('options', 'entryPoint')
                && venue.navigationPoints?.length === 0) {
                this.log('Create a Navigation Point');
                let point = turf.point([lon, lat]);
                if (venue.geometry.type === 'Point') {
                    this.log('Use the coordinates for new Navigation Point for Point');
                }
                else if (turf.pointsWithinPolygon(point, venue.geometry).features?.length > 0) {
                    this.log('Use the coordinates for new Navigation Point inside Polygon');
                }
                else {
                    // point is outside the venue geometry
                    this.log('Use the intersection of Polygon and vector to coordinates as new Navigation Point');
                    let centroid = turf.centroid(venue.geometry);
                    let line = turf.lineString([
                        centroid.geometry.coordinates,
                        point.geometry.coordinates,
                    ]);
                    let featureCollection = turf.lineIntersect(venue.geometry, line);
                    point = featureCollection.features?.pop();
                }
                // create a navigation point
                let navigationPoint = {
                    isEntry: true,
                    isExit: false,
                    isPrimary: true,
                    name: "",
                    point: point.geometry
                };
                this.log('Apply a new Navigation Point');
                this.wmeSDK.DataModel.Venues.replaceNavigationPoints({
                    venueId: venue.id,
                    navigationPoints: [navigationPoint]
                });
            }
            this.groupEnd();
        }
        getCityById(cityID) {
            if (!cityID || isNaN(parseInt(cityID))) {
                return null;
            }
            return this.wmeSDK.DataModel.Cities.getById({
                cityId: cityID
            });
        }
        getCity(cityName = '') {
            return this.wmeSDK.DataModel.Cities.getCity({
                countryId: this.wmeSDK.DataModel.Countries.getTopCountry().id,
                cityName: cityName
            })
                || this.wmeSDK.DataModel.Cities.addCity({
                    countryId: this.wmeSDK.DataModel.Countries.getTopCountry().id,
                    cityName: cityName
                });
        }
        getStreet(cityId, streetName = '') {
            return this.wmeSDK.DataModel.Streets.getStreet({
                cityId: cityId,
                streetName: streetName,
            })
                || this.wmeSDK.DataModel.Streets.addStreet({
                    cityId: cityId,
                    streetName: streetName
                });
        }
    }
    /**
     * Copy to clipboard
     */
    function toClipboard(text) {
        // normalize
        text = normalizeString(text);
        text = text.replace(/'/g, '');
        GM.setClipboard(text);
        console.log('%c' + NAME + ': %cCopied \u00AB' + text + '\u00BB to the clipboard', 'color: #0DAD8D; font-weight: bold', 'color: dimgray; font-weight: normal');
    }
    /**
     * Normalize the string:
     *  - remove the double quotes
     *  - remove double space
     */
    function normalizeString(str) {
        // Clear space symbols and double quotes
        str = str.trim()
            .replace(/["\u201C\u201D]/g, '')
            .replace(/\s{2,}/g, ' ');
        return str;
    }

    let E50Instance;
    function setE50Instance(instance) {
        E50Instance = instance;
    }
    /**
     * Apply data to the current selected POI
     */
    function applyData(event) {
        event.preventDefault();
        E50Instance.applyData(event.target.dataset);
    }
    /**
     * Create the vector from the center of the selected POI to point by lon and lat
     */
    function showLayer(event) {
        const lon = parseFloat(event.target.dataset.lon);
        const lat = parseFloat(event.target.dataset.lat);
        E50Instance.createVector(lon, lat);
        E50Instance.showLayer();
    }
    /**
     * Remove all vectors and hide the layer
     */
    function hideLayer() {
        E50Instance.removeVectors();
        E50Instance.hideLayer();
    }

    var css_248z = ".wme-ui-modal.e50 .wme-ui-modal-header h5 {\n  padding: 16px 16px 0;\n  font-size: 16px;\n}\n\n.wme-ui-modal.e50 .wme-ui-modal-content {\n  overflow-x: auto;\n  max-height: 420px;\n  padding: 4px 0;\n}\n\n#venue-edit-general .e50 fieldset {\n  border: 0;\n  padding: 0;\n  margin: 0;\n}\n\n#venue-edit-general .e50 legend {\n  width: 100%;\n  text-align: left;\n}\n\n#venue-edit-general .e50 fieldset legend,\n.wme-ui-panel.e50 fieldset legend,\n.wme-ui-modal.e50 fieldset legend {\n  cursor: pointer;\n  font-size: 12px;\n  font-weight: bold;\n  margin: 0;\n  padding: 0 8px;\n  background-color: #f6f7f7;\n  border: 1px solid #e5e5e5;\n}\n\n#venue-edit-general .e50 fieldset legend::after,\n.wme-ui-panel.e50 fieldset legend::after,\n.wme-ui-modal.e50 fieldset legend::after {\n  display: inline-block;\n  text-rendering: auto;\n  content: \"\\2191\";\n  float: right;\n  font-size: 10px;\n  line-height: inherit;\n  position: relative;\n  right: 3px;\n}\n\n#venue-edit-general .e50 fieldset legend span,\n.wme-ui-panel.e50 fieldset legend span,\n.wme-ui-modal.e50 fieldset legend span {\n  font-weight: bold;\n  background-color: #fff;\n  border-radius: 5px;\n  color: #ed503b;\n  display: inline-block;\n  font-size: 12px;\n  line-height: 14px;\n  max-width: 30px;\n  padding: 1px 5px;\n  text-align: center;\n}\n\n#venue-edit-general .e50 fieldset ul,\n.wme-ui-panel.e50 fieldset ul,\n.wme-ui-modal.e50 fieldset ul {\n  border: 1px solid #ddd;\n}\n\n#venue-edit-general .e50 fieldset.collapsed ul,\n.wme-ui-panel.e50 fieldset.collapsed ul,\n.wme-ui-modal.e50 fieldset.collapsed ul {\n  display: none;\n}\n\n#venue-edit-general .e50 fieldset.collapsed legend::after,\n.wme-ui-panel.e50 fieldset.collapsed legend::after,\n.wme-ui-modal.e50 fieldset.collapsed legend::after {\n  content: \"\\2193\";\n}\n\n#venue-edit-general .e50 ul,\n.wme-ui-panel.e50 ul,\n.wme-ui-modal.e50 ul {\n  padding: 8px;\n  margin: 0;\n}\n\n#venue-edit-general .e50 li,\n.wme-ui-panel.e50 li,\n.wme-ui-modal.e50 li {\n  padding: 0;\n  margin: 0;\n  list-style: none;\n  margin-bottom: 2px;\n}\n\n#venue-edit-general .e50 li a,\n.wme-ui-panel.e50 li a,\n.wme-ui-modal.e50 li a {\n  display: block;\n  padding: 2px 4px;\n  text-decoration: none;\n  border: 1px solid #e4e4e4;\n}\n\n#venue-edit-general .e50 li a:hover,\n.wme-ui-panel.e50 li a:hover,\n.wme-ui-modal.e50 li a:hover {\n  background: rgba(255, 255, 200, 1);\n}\n\n#venue-edit-general .e50 li a.nonumber,\n.wme-ui-panel.e50 li a.nonumber,\n.wme-ui-modal.e50 li a.nonumber {\n  background: rgba(250, 250, 200, 0.5);\n}\n\n#venue-edit-general .e50 li a.nonumber:hover,\n.wme-ui-panel.e50 li a.nonumber:hover,\n.wme-ui-modal.e50 li a.nonumber:hover {\n  background: rgba(250, 250, 200, 1);\n}\n\n#venue-edit-general .e50 li a.noaddress,\n.wme-ui-panel.e50 li a.noaddress,\n.wme-ui-modal.e50 li a.noaddress {\n  background: rgba(250, 200, 100, 0.5);\n}\n\n#venue-edit-general .e50 li a.noaddress:hover,\n.wme-ui-panel.e50 li a.noaddress:hover,\n.wme-ui-modal.e50 li a.noaddress:hover {\n  background: rgba(250, 200, 100, 1);\n}\n\n.wme-ui-panel.e50 .wme-ui-fieldset-content:empty,\n.wme-ui-modal.e50 .wme-ui-modal-content:empty {\n  min-height: 20px;\n}\n\n.wme-ui-panel.e50 .wme-ui-fieldset-content:empty::after,\n.wme-ui-modal.e50 .wme-ui-modal-content:empty::after {\n  color: #ccc;\n  padding: 0 8px;\n  content: \"\\2014\";\n}\n\n.wme-ui-panel.e50 .wme-ui-fieldset-content label {\n  margin-top: 5px;\n  line-height: 18px;\n}\n\n.wme-ui-panel.e50 .wme-ui-fieldset-content input[type=\"text\"] {\n  float: right;\n}\n\n.distance-over-200 {\n  background-color: #f08a24;\n}\n\n.distance-over-1000 {\n  background-color: #ed503b;\n}\n\n.external-operational a.url {\n  border: 4px solid #009900;\n  border-radius: 50%;\n}\n\n.external-closed-temporarily a.url {\n  border: 4px solid #ff7300;\n  border-radius: 50%;\n}\n\n.external-closed-permanently a.url {\n  border: 4px solid #ff0000;\n  border-radius: 50%;\n}\n\n.e50 .wme-ui-tab-content {\n  padding: 8px;\n}\n\np.e50-info {\n  border-top: 1px solid #ccc;\n  color: #777;\n  font-size: x-small;\n  margin-top: 15px;\n  padding-top: 10px;\n  text-align: center;\n}\n\n#sidebar p.e50-blue {\n  background-color: #0057B8;\n  color: white;\n  height: 32px;\n  text-align: center;\n  line-height: 32px;\n  font-size: 24px;\n  margin: 0;\n}\n\n#sidebar p.e50-yellow {\n  background-color: #FFDD00;\n  color: black;\n  height: 32px;\n  text-align: center;\n  line-height: 32px;\n  font-size: 24px;\n  margin: 0;\n}\n";

    $(document)
        .on('bootstrap.wme', () => {
        WMEUI.addTranslation(NAME, TRANSLATION);
        WMEUI.addStyle(css_248z);
        let scriptSettings = new Settings(NAME, SETTINGS);
        let instance = new E50(NAME, scriptSettings);
        setE50Instance(instance);
        setCache(new Container());
    })
        .on('click', '.' + NAME + '-link', applyData)
        .on('mouseenter', '.' + NAME + '-link', showLayer)
        .on('mouseleave', '.' + NAME + '-link', hideLayer)
        .on('mouseenter', '.' + NAME + '-external', showLayer)
        .on('mouseleave', '.' + NAME + '-external', hideLayer)
        .on('none.wme', hideLayer);

})();