您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Check and fix street names for POI and segments. UA fork of original WME Assist
当前为
// ==UserScript== // @name WME Assist UA // @namespace waze-ua // @author borman84 (Boris Molodenkov), madnut, turbopirate + (add yourself here) // @description Check and fix street names for POI and segments. UA fork of original WME Assist // @require https://code.jquery.com/jquery-migrate-3.0.0.min.js // @grant GM_xmlhttpRequest // @connect google.com // @connect script.googleusercontent.com // @include /^https:\/\/(www|beta)\.waze\.com(\/\w{2,3}|\/\w{2,3}-\w{2,3}|\/\w{2,3}-\w{2,3}-\w{2,3})?\/editor\b/ // @icon data:image/png;base64,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 // @version 2022.09.22.006 // ==/UserScript== /* jshint esversion: 8 */ /* global W */ /* global $ */ /* global jQuery */ /* global map */ /* global require */ const scriptName = GM_info.script.name; function debug(message) { if (!$('#assist_debug').is(':checked')) return; if (typeof message === 'string') { console.log(scriptName + " DEBUG: " + message); } else { console.log(scriptName + " DEBUG: ", message); } } function info(message) { if (typeof message === 'string') { console.log(scriptName + " INFO: " + message); } else { console.log(scriptName + " INFO: ", message); } } function warning(message) { if (typeof message === 'string') { console.warn(scriptName + " WARN: " + message); } else { console.warn(scriptName + " WARN: ", message); } } function series(array, start, action, alldone) { var helper = function (i) { if (i < array.length) { action(array[i], function () { helper(i + 1); }); } else { if (alldone) { alldone(); } } }; helper(start); } function run_wme_assist() { const supportedRulesVersion = "1.1"; const requestsTimeout = 20000; // in ms const rulesHash = "AKfycbyCR85UB-OexWIcN2pkTV1828bf0M6hUXkfHmu79M50PW3LMjpXkZ4ynRUzf2AOJqQqBA"; let rulesDB = {}; function displayHtmlPage(res) { if (res.responseText.match(/Authorization needed/) || res.responseText.match(/ServiceLogin/)) { alert(scriptName + ":\n" + "Authorization is required for using this script. This is one time action.\n" + "Now you will be redirected to the authorization page, where you'll need to approve request.\n" + "After confirmation, please close the page and reload WME."); } let w = window.open(); w.document.open(); w.document.write(res.responseText); w.document.close(); w.location = res.finalUrl; } function validateHTTPResponse(res) { let result = false, displayError = true; if (res) { switch (res.status) { case 200: displayError = false; if (res.responseHeaders.match(/content-type: application\/json/i)) { result = true; } else if (res.responseHeaders.match(/content-type: text\/html/i)) { displayHtmlPage(res); } break; default: displayError = false; alert(scriptName + " Error: unsupported status code - " + res.status); info(res.responseHeaders); info(res.responseText); break; } } else { displayError = false; alert(scriptName + " error: Response is empty!"); } if (displayError) { alert(scriptName + ": Error processing request. Response: " + res.responseText); } return result; } function requestRules (callbackFunc) { GM_xmlhttpRequest({ url: 'https://script.google.com/macros/s/' + rulesHash + '/exec?func=getStreetRules&user=' + W.loginManager.user.userName, method: 'GET', timeout: requestsTimeout, onload: function (res) { if (validateHTTPResponse(res)) { let out = JSON.parse(res.responseText); if (out.result == "success") { info("Rules format version: " + out.version); if (out.version == supportedRulesVersion) { rulesDB = out.rules; } else { alert(scriptName + ": Table rules format version is not supported!\nPlease, update Assist script to newer version."); } } else { alert(scriptName + ": Error getting rules!"); } } callbackFunc(); }, ontimeout: function (res) { alert(scriptName + ": Sorry, request timeout!"); }, onerror: function (res) { alert(scriptName + ": Sorry, request error!"); } }); } function isWazeApiReady() { let wazeapi = window.W || W; if (!wazeapi) return false; if (!wazeapi.map) return false; if (!wazeapi.model) return false; if (!wazeapi.model.countries) return false; if (!wazeapi.model.countries.top) return false; if (!wazeapi.loginManager) return false; if (!wazeapi.loginManager.user) return false; return true; } var Rule = function (comment, func, variant) { this.comment = comment; this.correct = func; this.variant = variant; }; var CustomRule = function (oldname, newname) { var title = '/' + oldname + '/ ➤ ' + newname; this.oldname = oldname; this.newname = newname; this.custom = true; $.extend(this, new Rule(title, function (text) { return text.replace(new RegExp(oldname), newname); })); }; var ExperimentalRule = function (comment, func) { this.comment = comment; this.correct = func; this.experimental = true; }; var Rules = function (countryName) { var rules_basicCommon = function () { return [ new Rule('Unbreak space in street name', function (text) { return text.replace(/\s+/g, ' '); }), new Rule('ACUTE ACCENT in street name', function (text) { return text.replace(/\u0301|\u0300/g, ''); }), new Rule('Dash in street name', function (text) { return text.replace(/\u2010|\u2011|\u2012|\u2013|\u2014|\u2015|\u2043|\u2212|\u2796/g, '-'); }), new Rule('No space after the word', function (text) { return text.replace(/\.(?!\s)(.+)/g, '. $1'); }), new Rule('No space after the >', function (text) { return text.replace(/>(?!\s)/g, '> '); }), new Rule('Garbage dot', function (text) { return text.replace(/(^|\s+)\./g, '$1'); }), ]; }; var rules_basicRU = function () { return rules_basicCommon().concat([ new Rule('Incorrect street name', function (text) { return text.replace(/улицаица/, 'улица'); }), new Rule('Incorrect street name', function (text) { return text.replace(/скя( |$)/, 'ская$1'); // Волгостроевскя набережная }), new Rule('Incorrect street name', function (text) { return text.replace(/олодеж/, 'олодёж'); // Молодёжная }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(мая)( |$)/, '$1Мая$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(мкрн?\.?|мк?р?-н)( |$)/, '$1микрорайон$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(р-о?н)( |$)/, '$1район$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(им\.?)( |$)/, '$1имени$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(пос\.?)( |$)/i, '$1посёлок$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(д\.?)( |$)/, '$1деревня$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(просп\.?)( |$)/i, '$1проспект$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(ул\.?)( |$)/i, '$1улица$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(р-д)( |$)/i, '$1разъезд$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(з-д)( |$)/i, '$1заезд$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(пер\.?)( |$)/i, '$1переулок$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(пр\.?|пр?-з?д\.?)( |$)/i, '$1проезд$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(пр?-к?т\.?)( |$)/i, '$1проспект$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(тр-т)( |$)/i, '$1тракт$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(пл\.?)( |$)/i, '$1площадь$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(ш\.?)( |$)/, '$1шоссе$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(б-р|бул\.?)( |$)/i, '$1бульвар$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(дор\.)( |$)/i, '$1дорога$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(о\.?)( |$)/, '$1остров$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(наб\.?)( |$)/i, '$1набережная$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(туп\.?)( |$)/i, '$1тупик$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(кв\.?)( |$)/i, '$1квартал$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(набережная р\.?)( |$)/i, '$1набережная реки$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/^На /, 'на '); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)(-ая)( |$)/, '$1-я$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)(-ого|-ое)( |$)/, '$1$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)(-[оыи]й)( |$)/, '$1-й$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)(-ти)( |$)/, '$1-и$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)й/, '$1-й'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)я/, '$1-я'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(\d)(\sЛет)(\s|$)/, '$1 лет$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(№)(\d)/, '$1 $2') .replace(/(Проектируемый проезд)\s+(\d+[A-Я]?)/, '$1 № $2'); }), new Rule('Incorrect street name', function (text) { return text.replace(/([а-яё])-\s+/, '$1 - '); }), new Rule('Incorrect street name', function (text) { return text.replace(/\s+км\./i, ' км'); }), new Rule('Incorrect street name', function (text) { return text.replace(/\[([^P]+)\]/, '$1'); }), new Rule('Incorrect street name', function (text) { return text.replace(/^СНТ\s(.*)/, '$1 снт'); }), new Rule('Incorrect street name', function (text) { return text.replace(/^ЖД$/i, 'ж/д'); }), new Rule('Incorrect highway name', function (text) { return text.replace(/^M-?(\d)/, 'М$1') .replace(/^A-?(\d)/, 'А$1') .replace(/^P-?(\d)/, 'Р$1') .replace(/^([МАР])-(\d)/, '$1$2') .replace(/^(\d{2})A-(\d)/, '$1А-$2') .replace(/^(\d{2})K-(\d)/, '$1К-$2') .replace(/^(\d{2})H-(\d)/, '$1Н-$2') .replace(/^(\d{2})P-(\d)/, '$1Р-$2') .replace(/^(\d+)А(:|\s+|$)/, '$1A$2') .replace(/^(\d+)В(:|\s+|$)/, '$1B$2'); }), ]); }; var rules_RU = function () { return rules_basicRU().concat([ new Rule('Incorrect status position', function (text) { // Неоднозначные улицы, требуется проверка на город // Москва: Козлова, Мишина // Питер: Абросимова, Гусева, Комарова, Панфилова, Тарасова, Старцева, Крюкова, Миронова, Перфильева, Шарова, Зеленина // Великий Новгород: Яковлева, Ильина // Краткие притяжательные прилагательные похожие на фамилии var exAdjW = 'Репищева|Малая Зеленина|Карташихина|Опочинина|Остоумова|Гаврикова|Прасковьина|Усачёва|Бармалеева|Ильмянинова|Остоумова|Плуталова|Подрезова|Полозова|Рашетова|Сегалева|Шамшева|Эсперова|Замшина|Куракина|Ольгина|Опочинина|Осокина|Рюхина|Тосина|Веткина|Жукова|Абросимова'; // Женские статусы var wStatus = 'улица|набережная|дорога|линия|аллея|площадь|просека|автодорога|эстакада|магистраль|дамба|хорда|коса|деревня|переправа|площадка|дорожка|трасса'; // Мужские статусы var mStatus = 'проспект|переулок|проезд|тупик|бульвар|тракт|просек|взвоз|спуск|разъезд|переезд|квартал|путепровод|мост|сад|сквер|тоннель|туннель|парк|проток|канал|остров|микрорайон|район|городок|посёлок|поселок|вал|проулок'; // Средние статусы var nStatus = 'шоссе|кольцо'; // Не нужно добавлять статус или изменять порядок слов var exStatus = 'лестница|зимник|объезд|заезд|съезд|обвод|обход|подъезд|дцать\\s|десят\\s|сорок\\s|closed|^\\d+(:|$)|[Дд]убл[её]р|\\d+[AB]|[МАР]\\d|\\d{2}[АКНР]-|Rail|грунтовка|[Тт]ропа|[Тт]рек|[Пп]лотина|метро|монорельс|ворота|шлагбаум|трамвай|пути$|Транссиб|Мост|подход|подъезд|обход|въезд|выезд|разворот|шлагбаум|слобода|Грейдер|брод|(?: |^)на |(?: |^)в |(?: |^)к |(?: |^)под |(?: |^)с |(?: |^)от |(?: |^)во |(?: |^)из |(?: |^)по |(?: |^)об |(?: |^)у |(?: |^)о |(?: |^)над |(?: |^)около |(?: |^)при |(?: |^)перед |(?: |^)про |(?: |^)до |(?: |^)без |(?: |^)за |(?: |^)через |ж\\/д|ТТК|КАД|ЗСД|АГ?ЗС|^$| - |\\/|,|плотина|снт|станция|:|[Пп]ромзона|паркинг|парковка|Козлова|Абросимова|Гусева|Комарова|Панфилова|Тарасова|Яковлева|Мишина|Старцева|Крюкова|Ильина|Миронова|Перфильева|Шарова|Зеленина|Жукова'; // Названия улиц похожие на прилагательные var exW = 'Нехая|Тукая|Мая|Барклая|Батырая|Маклая|Бикбая|Амантая|Нечая|Эшпая|Орая|Прикамья|Алтая|Ухсая|Хузангая|Галлая|Николая|Гая|Эркая|Камая|Пченушая|Здоровья|Палантая|Ярвенпяя|Гулая|Заполярья|Крылья|Приморья|Калина Красная|Краснолесья|Мазая'; // Названия проспектов похожие на прилагательные var exM = 'Расковой|Дуровой|Космодемьянской|.+?строй|Ковалевской|Борисовой|Давлетшиной|Крупской|Шевцовой|Чайкиной|Богомоловой|Савиной|Попковой|Петровой|Ангелиной|Терешковой|Новоселовой|Красноармейской|Гризодубовой|Красноярский рабочий|Цеткин|Молдагуловой|Чайкиной|Цветаевой|Тимофеевой|Дубровиной|Ульяны Громовой|[Нн]абережной|Давлетшиной|Перовской|Шпаковой|Ульяновой|Гачхой|Исаевой|Бой|Плевицкой'; // Отделить примечания в скобках (дублёр) var brackets = ''; text = text.replace(/\s*(.*?)\s*(\(.*\))/, function (all, s, b) { brackets = b; return s; }); // Игнорируем исключения if ( new RegExp(exStatus).test(text) ) return text + ' ' + brackets; // коттеджный, дачный, клубный посёлок в начало text = text.replace(/(.*?)(?:\s+)((?:.*ый )посёлок)/, '$2 $1'); // Добавляем пропущенный статус if ( ! new RegExp('(^|\\s+)(' + wStatus + '|' + mStatus + '|' + nStatus + ')(\\s+|$)').test(text) ) { if ( text == 'Набережная') { text = 'Набережная улица'; } else if ( new RegExp('(^|\\s+)(' + wStatus + '|' + mStatus + '|' + nStatus + ')(\\s+|$)', 'i' ).test(text) ) { // Если статус есть, но записан с заглавной буквы text = text.replace( new RegExp('(^|\\s+)(' + wStatus + '|' + mStatus + '|' + nStatus + ')(?=\\s+|$)', 'i' ), function (all, space, status) { return space + status.toLowerCase(); }); } else if ( /(^|\s+)[Оо]б[ъь]ездная(\s+|$)/.test(text)) { text = text.replace(new RegExp('(^|\\s+)[Оо]б[ъь]ездная(\\s+|$)(?!=(' + wStatus + '))'), '$1Объездная дорога$2'); } else if ( /(^|\s+)[Оо]кружная$/.test(text)) { text = text.replace(/(^|\s+)[Оо]кружная$/, '$1Окружная дорога'); } else if ( /[-аяь]я$/.test(text)) { // Прилагательное без статуса (Русско-Полянская) text = text + ' улица'; } else if (/[а-я]-[А-Я]/.test(text)) { // Не хватает пробелов вокруг тире (Москва-Петушки) text = text.replace(/([а-я])-([А-Я])/g, '$1 - $2'); } else { text = 'улица ' + text; } } // Голые числительные без склонения if ( ! new RegExp('\\d\\s+мая(\\s|$)', 'i' ).test(text) ) { text = text.replace(new RegExp('(\\d)(?=(\\s+[^\\s]+(?:-я|ая|ья|яя|яся))*\\s+(' + wStatus + ')(\\s|$))', 'g'), '$1-я'); // 1 линия } text = text.replace(new RegExp('(\\d)(?=(\\s+[^\\s]+[-иоы]й|ин|[оеё]в)*\\s+(' + mStatus + ')(\\s|$))', 'g'), '$1-й'); // 2 проезд, 5 Донской проезд // Распространённые сокращения text = text.replace(/М\.\s+(?=Горького)/, 'Максима '); text = text.replace(/К\.\s+(?=Маркса|Либкнехта)/, 'Карла '); text = text.replace(/Р\.\s+(?=Люксембург)/, 'Розы '); text = text.replace(/А\.\s+(?=Невского)/, 'Александра '); text = text.replace(/Б\.\s+(?=Хмельницкого)/, 'Богдана '); // Всё пишем заглавными буквами, кроме статусов, предлогов и гидронимов text = text.replace(/(^|\s+)набережная улица/, '$1Набережная улица'); var foundStatus = false; text = (' ' + text).replace(/([-\s])([^-\s]+)/g, function(all, space, word) { if ( ! foundStatus ) { if ( new RegExp('^(' + wStatus+ '|' + mStatus + '|' + nStatus + ')$').test(word) ) { foundStatus = true; return all; } } if ( /^(летия|лет|года|реч?к[аи]|канала?|острова?|стороны|год|съезда|имени|ручей|канавки|за|из|от|км|километр|де|в|к|о|с|у|на|и)$/i.test(word) || ( space == '-' && /^(лейтенанта|майора|полковника|й|я|ти|го|е|ей|х)$/.test(word) ) ) { return space + word.toLowerCase(); } else return space + word.charAt(0).toUpperCase() + word.substr(1); }).replace(/\s+(.*)/, '$1').replace(/Железная дорога/, 'железная дорога'); // Статусы женского рода if ( new RegExp('(^|\\s)(' + wStatus + ')(\\s|$)').test(text) ) { // Распространённые сокращения text = text.replace(/М\.\s+(?=[^\s]+?(?:ая|ья|яя|яся)( |$))/, 'Малая '); text = text.replace(/Б\.\s+(?=[^\s]+?(?:ая|ья|яя|яся)( |$))/, 'Большая '); // перед статусом могут быть только прилагательные // Строителей 1-я улица -> улица Строителей 1-я text = text.replace(new RegExp('(?:\\s*)(.+?)(?:\\s+)(' + wStatus + ')(?=\\s+|$)'), function (all, adj, s) { if ( new RegExp(exAdjW).test(adj) ) return all; if ( (! new RegExp(exW).test(adj)) && (/^((\s+[^\s]+?(-я|ая|ья|яя|яся))+)$/.test(' ' + adj)) ) return all; return s + ' ' + adj; }); // Прилагательные вперёд if ( ! new RegExp('(^|\\s|-)(' + exW + ')(-|\\s|$)').test(text) ) { // улица Малая Зеленина -> Малая Зеленина улица // улица Мягкая -> Мягкая улица // улица Авиаконструктора Яковлева, улица Малиновая Гора text = text.replace(new RegExp('(' + wStatus + ')((?:\\s+(?:' + exAdjW + ')|\\s+[^\\s]+(?:-я|ая|ья|яя|яся))+)$'), '$2 $1'); // улица *** Малая Набережная -> Малая Набережная улица *** text = text.replace(new RegExp('(' + wStatus + ')(.*?)((?:\\s+[^\\s]+(?:-я|ая|ья|яя|яся))+)$'), '$3 $1$2'); // улица Мягкая 1-й Проезд -> Мягкая улица 1-й Проезд text = text.replace(new RegExp('(' + wStatus + ')(?:\\s+)([^\\s]+(?:-я|ая|ья|яя|яся))(?=\\s+\\d+-й\\s+Проезд|\\s+\\d+-я\\s+Линия)'), '$2 $1'); } // Числительное всегда вначале если оно согласовано с прилагательным // Мягкая 1-я -> 1-я Мягкая text = text.replace(/(.+(?:ая|ья|яя|яся))(?:\s+)(\d+-я)(?! Линия| Ферма| Рота)/, '$2 $1'); // улица 1-я Строителей -> 1-я улица Строителей text = text.replace(new RegExp('(' + wStatus + ')(?:\\s+)(\\d+-я)(?!\\s+[^\\s]+(?:ая|ья|яя|яся|ка)( |$)|\\s+(' + wStatus + '|Ферма|Авеню|Пристань|Рота|Слобода))', 'i'), '$2 $1'); // 1-я улица Лесоперевалка -> улица 1-я Лесоперевалка text = text.replace(new RegExp('(\\d+-я)\\s+(' + wStatus + ')\\s+([^\\s]+(?:лка|ель|аза))$'), '$2 $1 $3'); } else // Статусы мужского рода if ( new RegExp('(^|\\s)(' + mStatus + ')(\\s|$)').test(text) ) { // Распространённые сокращения text = text.replace(/М\.\s+(?=[^\s]+?(?:[-иоы]й|ин|[оеё]в)( |$))/, 'Малый '); text = text.replace(/Б\.\s+(?=[^\s]+?(?:[-иоы]й|ин|[оеё]в)( |$))/, 'Большой '); // перед статусом могут быть только прилагательные text = text.replace(new RegExp('(?:\\s*)(.+?)(?:\\s+)(' + mStatus + ')(?=\\s+|$)'), function (all, adj, s){ // if ( /[а-яё]+([-иоы]й|ин)(\s+|$)/.test(adj) ) return all; if ( (! new RegExp(exM, 'i').test(adj)) && (/^((\s+[^\s]+?([-иоы]й|ин|[оеё]в))+)$/.test(' ' + adj)) ) return all; return s + ' ' + adj; }); // Прилагательное вперёд if (( ! new RegExp('(^|\\s)(' + exM + ')(\\s|$)', 'i').test(text) ) && ( ! new RegExp('^(проезд|переулок)([^\.]*?)((?:\\s+[^\\s]+ой)+)$').test(text) ) ) { // переулок *** 1-й -> 1-й переулок *** text = text.replace(new RegExp('^(' + mStatus + ')([^\.]*?)((?:\\s+[^\\s]+(?:[-иоы]й|ин))+)$'), '$3 $1$2'); text = text.replace( new RegExp('^(' + mStatus + ')((?:\\s+[^\\s]+(?:[-иоы]й|ин))+)$'), '$2 $1'); text = text.replace( new RegExp('^(' + mStatus + ')(?:\\s+)([^\\s]+(?:[-иоы]й|ин))(?=\\s+\\d+-й\\s+Проезд|\\s+\\d+-я\\s+Линия)'), '$2 $1'); } // Числительное всегда вначале если оно согласовано с прилагательным // переулок 1-й Дунаевского -> 1-й переулок Дунаевского //text = text.replace(new RegExp('(' + mStatus + ')(?:\\s+)(\\d+-й)(?!\\s+[^\\s]*(?:' + exM + ')|\\s+[^\\s]+(?:[-иоы]й|ин|[оеё]в)( |$)', 'i'), '$2 $1'); text = text.replace(/(.+[иоы]й)(?:\s+)(\d+-й)/, '$2 $1'); text = text.replace(new RegExp('(' + mStatus + ')(?:\\s+)(\\d+-й)(?!\\s+[^\\s]+[иоык][ий]( |$)|\\s+(' + mStatus + '|Ряд|км))', 'i'), '$2 $1'); } else // Статусы среднего рода if ( new RegExp('(^|\\s)(' + nStatus + ')(\\s|$)').test(text) ) { // Энтузиастов шоссе -> шоссе Энтузиастов text = text.replace(new RegExp('(?:\\s*)(.+?)(?:\\s+)(' + nStatus + ')(?=\\s+|$)'), function (all, adj, s){ if ( /[а-яё]+(ое)(\s+|$)/.test(adj) ) return all; return s + ' ' + adj; }); // шоссе Воткинское -> Воткинское шоссе, Верхнее шоссе text = text.replace( new RegExp('^(' + nStatus + ')(?:\\s+)(.+[ео]е)$'), '$2 $1'); } // Возвращаем скобки в конце return text + ' ' + brackets; }), new Rule('Move status to begin of name', function (text) { return text.replace(/(.*)(улица)(.*)/, '$2 $1 $3'); }, 'Tula'), new ExperimentalRule('Experimental', function (text) { return text.replace(/experimental/, 'corrected_experimental'); }), ]); }; var rules_BY = function () { var isStatus = function (str) { var list = ['улица', 'переулок', 'проспект', 'проезд', 'площадь', 'шоссе', 'бульвар', 'тракт', 'тупик', 'спуск', 'вуліца', 'завулак', 'праспект', 'праезд', 'плошча', 'шаша']; if (list.indexOf(str) > -1) return true; return false; }; var isPseudoStatus = function (str) { var list = ['шоссе', 'тракт', 'площадь', 'шаша', 'плошча', 'спуск']; if (list.indexOf(str) > -1) return true; return false; }; var isNumber = function (str) { return /([0-9])-[іыйя]/.test(str); }; var replaceParts = function (text) { var arr = text.split(' '); var result = []; var status; var number; var previousPart = ''; for (var i = 0; i < arr.length; ++i) { var part = arr[i]; if (isStatus(part) && !isPseudoStatus(part)) { status = part; } else if (isNumber(part) && previousPart.toLowerCase() != 'героев') { number = part; } else { result.push(part); } previousPart = part; } if (status) { result.splice(0, 0, status); } if (number) { result.push(number); } return result.join(' '); }; return rules_basicRU().concat([ new Rule('Delete space in initials', function (text) { return text.replace(/(^|\s+)([А-Я]\.)\s([А-Я]\.)/, '$1$2$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(тр-т)( |$)/, '$1тракт$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(вул\.?)( |$)/, '$1вуліца$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(зав)( |$)/, '$1завулак$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/(^| )(прасп)( |$)/, '$1праспект$3'); }), new Rule('Incorrect street name', function (text) { return text.replace(/-ая/, '-я').replace(/-ой/, '-й'); }), new Rule('Incorrect street name', function (text) { return text.replace(/([РрНнМмPpHM])-?([0-9])/, function (a, p1, p2) { p1 = p1 .replace('р', 'Р') .replace('н', 'Н') .replace('м', 'М') .replace('P', 'Р') .replace('p', 'Р') .replace('H', 'Н') .replace('M', 'М'); return p1 + '-' + p2; }); }), new Rule('Incorrect street name', replaceParts), ]); }; var rules_UA = function () { var hasCyrillic = function(s) {return s.search(/[а-яіїєґ]/i) != -1;}; var hasShortStatus = function(s) { return s.search(/( |^)(вул\.|просп\.|мкрн\.|наб\.|пров\.|ст\.|пр\.|б-р|р-н)( |$)/i) != -1; }; var hasLongStatus = function(s) { return s.search(/( |^)(площа|алея|шосе|тракт|узвіз|тупик|міст|в\'їзд|виїзд|виїзд|розворот|трамвай|залізниця|майдан|заїзд|траса|дорог[аи]|шляхопровід|шлях|завулок|квартал|автомагістраль)( |$)/i) != -1; }; var hasSpecialStatus = function(s) { return s.search(/( |^)([РНТМ](-[0-9]+)+|[EОС][0-9]+)|~|>|\/( |$)|^(|до|на|>) /i) != -1; }; var hasInternationalName = function(s) {return s.search(/^E[0-9]+$/i) != -1; }; var hasStatus = function(s) { return (hasShortStatus(s) || hasLongStatus(s) || hasSpecialStatus(s)); }; var hasAdjName = function(s) { var adjRegex = new RegExp( '( |^)(Балтійська|Кропивницька|Бориславська|Овочева|Спортивна|Дорогобицька|Зарічна|Привокзальна|Клубна|Запречистська|Заставська|Глибока|Японська' + '|Київська|Городоцька|Зелена|Судова|Замкнена|Стрийська|Козельницька|Снопківська|Волоська|Турецька|Скельна|Грецька|Кубанська|Кримська|Водогінна' + '|Аральська|Студентська|Переяславська|Дунайська|Дністерська|Тернопільська|Зубрівська|Сихівська|Райдужна|Вулецька|Соняшникова|Коломийська' + '|Садибна|Демнянська|Наукова|Жасминова|Білоцерківська|Орлина|Кульпарківська|Вітряна|Молдавська|Виноградна|Холодноярська|Керамічна|Кишинівська' + '|Львівська|Урожайна|Садова|Гіпсова|Окружна|Зв\'язкова|Житомирська|Повстанська|Збиральна|Авіаційна|Кондукторська|Полева|Дублянська|Вокзальна' + '|Галицька|Любінська|Спокійна|Народна|Залізнична|Личаківська|Сполучна|Тернова|Конюшинна|Яворівська|Західна|Суховольська|Світла|Озерна|Ряшівська' + '|Коротка|Сосновська|Весняна|Січова|Вузька|Журавлина|Рудненська|Чернівецька|Стародубська|Хотинська|Одеська|Стрілецька|Замарстинівська|Топольна' + '|Інструментальна|Господарська|Волошкова|Сріблиста|Торф\'яна|Городницька|Сінна|Покутська|Заповітна|Малинова|Вербова|Перекопська|Квітова|Корінна' + '|Східна|Крута|Реміснича|Узбецька|Технічна|Половинна|Хімічна|Жовківська|Лемківська|Сорочинська|Джерельна|Батуринська|Замкова|Клепарівська' + '|Смерекова|Золота|Чорноморська|Вугільна|Сянська|Мулярська|Весела|Мукачівська|Ужгородська|Пильникарська|Базарна|Водна|Вагова|Таманська' + '|Театральна|Вірменська|Університетська|Вічева|Руська|Друкарська|Сербська|Ставропігійська|Стара|Насипна|Рівна|Шевська|Староєврейська|Архівна' + '|Підвальна|Валова|Гуцульська|Банківська|Пекарська|Севастопольська|Тиха|Лісна|Слободна|Харківська|Мала|Круп\'ярська|Таджицька|Кутова|Грибова' + '|Ярова|Букова|Ромоданівська|Зимова|Долішня|Яричівська|Копальна|Казахська|Низова|Міжгірна|Грушева|Ялтинська|Чумацька|Богданівська|Глиняна' + '|Переможна|Поетична|Приязна|Визвольна|Бігова|Наступальна|Пластова|Польова|Ковельська|Врізана|Ігорева|Корейська|Теребовлянська|Черкаська' + '|Белзька|Молочна|Корецька|Крайня|Милятинська|Горіхова|Юнацька|Трависта|Бродівська|Старознесенська|Почаївська|Пинська|Миргородська|Поворотна' + '|Потелицька|Новознесеньська|Волинська|Промислова|Опришківська|Механічна|Донецька|Льняна|Полтв\'яна|Селянська|Космічна|Купальська|Кукурудзяна' + '|Бузька|Тарасівська|Бескидська|Лазнева|Підмурна|Рибна|Тролейбусна|Північна|Лугова|Лісова|Сигнальна|Таллінська|Ливарна|Левандівська|Повітряна' + '|Тісна|Кочегарська|Естонська|Олешківська|Ясна|Щекавицька|Алмазна|Слюсарська|Папоротна|Ботанічна|Заболотівська|Мирна|Скромна|Пропелерна' + '|Загородня|Моторна|Широка|Холмська|Лисеницька|Довга|Пасічна|Хлібна|Китайська|Садівнича|Каштанова|Медова|Околична|Відкрита|Бойківська' + '|Куликівська|Червона|Мила|Сарненська|Природна|Перемиська|Моршинська|Конотопська|Похила|Художня|Вишнева|Молодіжна|Дивізійна|Поштова|Тунельна' + '|Білоруська|Яблунева|Творча|Пільна|Шпитальна|Винниківська|Поліська|Загірна|Нагірна|Мурована|Нова|Архітекторська|Грюнвальдська|Політехнічна' + '|Професорська|Бібліотечна|Болгарська|Випасова|Малоголосківська|Монгольська|Скісна|Резедова|Простинна|Бузинова|Порічкова|Осикова|Нарцисова' + '|Розлога|Ряснянська|Паралельна|Південна|Комарнівська|Перемишльська|Заводська|Соборна|Тупікова|Горішня|Шкільна|Українська|Сонячна|Артищівська' + '|Паркова|Равська|Старомостівська|Головна|Травнева|Клюсовська|Сокальська|Крива|Святоюрська|Завадівська|Центральна|Жовтнева|Колгоспна|Больнична' + '|Радянська|Ювілейна|Степова|Порохова|Робітнича|Очеретяна|Жнивна|Буковинська|Луганська|Абхазька|Лижв\'ярська|Гайдамацька|Грабова|Полунична' + '|Томашівська|Каховська|Гіацинтова|Дальня|Дозвільна|Лютнева|Корсунська|Підгаєцька|Дубнівська|Дрогобицька|Мисливська|Бакінська|Чуваська' + '|Скнилівська|Щирецька|Санітарна|Лікувальна|Баштанна|Мостова|Паровозна|Вагонна|Проста|Суха|Фабрична|Солов\'[яї]на|Хорватська|Вільна|Затишна' + '|Крехівська|Сходова|Спадиста|Туркменська|Олійна|Рослинна|Албанська|Азовська|Карпатська|Листопадна|Віденська|Енергетична|Соколина|Латвійська' + '|Земельна|Трускавецька|Росиста|Рядова|Сусідня|Рахівська|Розбіжна|Рівнинна|Керченська|Піскова|Ніжинська|Кошова|Козацька|Гранітна|Дубова' + '|Полуднева|Лебедина|Навколишня|Січнева|Горівська|Поморянська|Кінцева|Курінна|Новознесенська|Міртова|Шполянська|Грунтова|Ґрунтова|Варшавська)( |$)', 'i' ); return s.search(adjRegex) != -1; }; // ATTENTION: Rule order is important! return rules_basicCommon().concat([ new Rule('Check with rules from Google Sheet', function (text, city) { let ruleKey = text + '_' + city; if (rulesDB[ruleKey]) { let matchCity = rulesDB[ruleKey].city ? rulesDB[ruleKey].city == city : true; if (matchCity) { return rulesDB[ruleKey].new_name; } } return text; }, 'GSheets'), new Rule('Fix English characters in name', function (t) { return !hasCyrillic(t) || hasInternationalName(t) ? t : t.replace(/[AaBCcEeHIiKkMOoPpTXxYy]/g, function (c) { return { 'A': 'А', 'a': 'а', 'B': 'В', 'C': 'С', 'c': 'с', 'E': 'Е', 'e': 'е', 'H': 'Н', 'I': 'І', 'i': 'і', 'K': 'К', 'k': 'к', 'M': 'М', 'O': 'О', 'o': 'о', 'P': 'Р', 'p': 'р', 'T': 'Т', 'X': 'Х', 'x': 'х', 'Y': 'У', 'y': 'у' }[c]; }); }), new Rule('Delete space in initials', function (text) { return text.replace(/(^| +)([А-ЯІЇЄҐ]\.) ([А-ЯІЇЄҐ]\.)/, '$1$2$3'); }), new Rule('Incorrect characters in street name', function (t) { // This rule should be before renaming rules or they couldn't see some errors return t .replace(/[@#№$,^!:;*"?<]/g, ' ').replace(/ {2,}/, ' ') .replace(/[`\u02bc]/g, '\''); // replace incorrect apostrophes (`’) }), /* new Rule('Incorrect language', function (t) { // Translate full Russian names to full Ukrainian // and next rules will shorten them if necessary return t .replace(/(^| )в?улица( |$)/i, '$1вулиця$2') .replace(/(^| )спуск( |$)/i, '$1узвіз$2') .replace(/(^| )(т)расса( |$)/i, '$1$2раса$3') .replace(/(^| )(п)ереулок( |$)/i, '$1$2ровулок$3') .replace(/(^| )(п)роезд( |$)/i, '$1$2роїзд$3') .replace(/(^| )(п)лощадь( |$)/i, '$1$2лоща$3') .replace(/(^| )(ш)оссе( |$)/i, '$1$2осе$3') .replace(/(^| )(с)танция( |$)/i, '$1$2танція$3') .replace(/(^| )(а)ллея( |$)/i, '$1$2лея$3') .replace(/(^| )(н)абережная( |$)/i, '$1$2абережна$3') .replace(/(^| )(м)икрорайон( |$)/i, '$1$2ікрорайон$3') .replace(/(^| )(л)иния( |$)/i, '$1$2інія$3') .replace(/(^| )(а)кадемика( |$)/i, '$1$2кадеміка$3') .replace(/(^| )(а)дмирала( |$)/i, '$1$2дмірала$3') .replace(/ и /i, ' та '); }), */ new Rule('Mistake in short status', function (t) { return t .replace(/(^| )(буль?в?\.?|б-р\.)( |$)/i, '$1б-р$3') .replace(/(^| )(?:пр-к?т|п(?:р|о)?сп)\.?( |$)/i, '$1просп.$2') .replace(/(^| )пр-з?д\.?( |$)/i, '$1пр.$2') .replace(/(^| )ул\.?( |$)/i, '$1вул.$2') .replace(/(^| )р-н\.( |$)/i, '$1р-н$2') .replace(/(^| )пер\.?( |$)/i, '$1пров.$2') .replace(/(^| )(пров|просп|пр|вул|ст|мкрн|наб|дор)( |$)/i, '$1$2.$3'); }), new Rule('Long status must be short', function (t) { // Do short status only if there no other shorten statuses in name return hasShortStatus(t) ? t : t .replace(/(^| )район( |$)/i, '$1р-н$2') .replace(/(^| )бульвар( |$)/i, '$1б-р$2') .replace(/(^| )провулок( |$)/i, '$1пров.$2') .replace(/(^| )проспект( |$)/i, '$1просп.$2') .replace(/(^| )проїзд( |$)/i, '$1пр.$2') .replace(/(^| )вулиця( |$)/i, '$1вул.$2') .replace(/(^| )станція( |$)/i, '$1ст.$2') .replace(/(^| )мікрорайон( |$)/i, '$1мкрн.$2') .replace(/(^| )набережна( |$)/i, '$1наб.$2'); }), new Rule('Shorten street name or status must be long', function (t) { return t .replace(/(^| )туп\.?( |$)/i, '$1тупик$2') .replace(/(^| )тр-т\.?( |$)/i, '$1тракт$2') .replace(/(^| )(сп\.?|узв\.?|узвоз)( |$)/i, '$1узвіз$3') .replace(/(^| )пл\.?( |$)/i, '$1площа$2') .replace(/(^| )ал\.?( |$)/i, '$1алея$2') .replace(/(^| )ш\.?( |$)/i, '$1шосе$2') .replace(/(^|а )дор\.?( |$)/i, '$1дорога$2') .replace(/(ї )дор\.?( |$)/i, '$1дороги$2') .replace(/(^| )ген\.?( |$)/i, '$1Генерала$2') .replace(/(^| )див\.?( |$)/i, '$1Дивізії$2') .replace(/(^| )ак\.?( |$)/i, '$1Академіка$2') .replace(/(^| )марш\.?( |$)/i, '$1Маршала$2') .replace(/(^| )адм\.?( |$)/i, '$1Адмірала$2'); }), new Rule('Incorrect number ending', function (t) { return t .replace(/-[гштм]а/, '-а') .replace(/-[ыоиі]й/, '-й') .replace(/-тя/, '-я') .replace(/-ая/, '-а'); }), new Rule('Incorrect highway name', function (text) { return text.replace(/([РрНнМмPpHM])[-\s]*([0-9]{2})/, function (a, p1, p2) { p1 = p1 .replace('р', 'Р') .replace('н', 'Н') .replace('м', 'М') .replace('P', 'Р') .replace('p', 'Р') .replace('H', 'Н') .replace('M', 'М'); return p1 + '-' + p2; }); }), new Rule('Incorrect local street name', function (text) { return text.replace(/([ТтT])[-\s]*([0-9]{2})[-\s]*([0-9]{2})/, function (a, p1, p2, p3) { p1 = p1 .replace('т', 'Т') .replace('T', 'Т'); return p1 + '-' + p2 + '-' + p3; }); }), new Rule('Incorrect international highway name', function (text) { return text.replace(/^ *[eе][- ]*([0-9]+)/i, 'E$1'); }), new Rule('Incorrect local road name', function (text) { return text.replace(/([OoCcОоСс])[-\s]*([0-9]+)[-\s]*([0-9]+)[-\s]*([0-9]+)/, function (a, p1, p2, p3, p4) { p1 = p1 .replace('o', 'О') .replace('O', 'О') .replace('c', 'С') .replace('C', 'С'); return p1 + p2 + p3 + p4; }); }), new Rule('Fix status', function (t) { return hasStatus(t) ? t : 'вул. ' + t; }, 'Ukraine'), new Rule('Detect status absense or incorrect placement', function (t) { return hasStatus(t) ? (hasAdjName(t) ? t.replace(/(.*)(вул\.)(.*)/, '$1 $3 $2') : t) : (hasAdjName(t) ? t + ' вул.' : ''); }, 'Lviv'), new Rule('Move status to begin of name', function (text) { if (!hasSpecialStatus(text)) { return text.replace(/(.*)(вул\.)(.*)/, '$2 $1 $3'); } return text; }, 'Ukraine'), ]); }; var getCountryRules = function (name) { var commonRules = [ // Following rules must be at the end because // previous rules might insert additional spaces new Rule('Redundant space in street name', function (text) { return text.replace(/[ ]+/g, ' '); }), new Rule('Space at the begin of street name', function (text) { return text.replace(/^[ ]*/, ''); }), new Rule('Space at the end of street name', function (text) { return text.replace(/[ ]*$/, ''); }), ]; var countryRules; info('Get rules for country: ' + name); switch (name) { case 'Russia': countryRules = rules_RU(); break; case 'Belarus': countryRules = rules_BY(); break; case 'Ukraine': countryRules = rules_UA(); break; default: info('There are no implemented rules for country: ' + name); countryRules = []; } return countryRules.concat(commonRules); }; var rules = []; var customRulesNumber = 0; var onAdd = function (rule) {}; var onEdit = function (index, rule) {}; var onDelete = function (index) {}; this.onAdd = function (cb) { onAdd = cb; }; this.onEdit = function (cb) { onEdit = cb; }; this.onDelete = function (cb) { onDelete = cb; }; this.onCountryChange = function (name) { info('Country was changed to ' + name); rules.splice(customRulesNumber, rules.length - customRulesNumber); rules = rules.concat(getCountryRules(name)); }; this.get = function (index) { return rules[index]; }; this.correct = function (variant, text, city) { var newtext = text; var experimental = false; var custom_enabled = localStorage.getItem('assist_enable_custom_rules') == 'true'; for (var i = 0; i < rules.length; ++i) { var rule = rules[i]; if (rule.custom && !custom_enabled) continue; if (rule.experimental && !this.experimental) continue; if (rule.variant && rule.variant != variant) continue; var previous = newtext; newtext = rule.correct(newtext, city); var changed = (previous != newtext); if (rule.experimental && previous != newtext) { experimental = true; } previous = newtext; // if (rule.custom && changed) { // // prevent result overwriting by common rules // break; // } } return { value: newtext, experimental: experimental }; }; var save = function (rules) { if (localStorage) { localStorage.setItem('assistRulesKey', JSON.stringify(rules.slice(0, customRulesNumber))); } }; this.load = function () { if (localStorage) { var str = localStorage.getItem('assistRulesKey'); if (str) { var arr = JSON.parse(str); for (var i = 0; i < arr.length; ++i) { var rule = arr[i]; this.push(rule.oldname, rule.newname); } } } rules = rules.concat(getCountryRules(countryName)); }; this.push = function (oldname, newname) { var rule = new CustomRule(oldname, newname); rules.splice(customRulesNumber++, 0, rule); onAdd(rule); save(rules); }; this.update = function (index, oldname, newname) { var rule = new CustomRule(oldname, newname); rules[index] = rule; onEdit(index, rule); save(rules); }; this.remove = function (index) { rules.splice(index, 1); --customRulesNumber; onDelete(index); save(rules); }; }; var ActionHelper = function () { var WazeActionAddAlternateStreet = require("Waze/Action/AddAlternateStreet"); var WazeActionUpdateFeatureAddress = require("Waze/Action/UpdateFeatureAddress"); var WazeActionUpdateObject = require("Waze/Action/UpdateObject"); var ui; var type2repo = function (type) { var map = { 'venue': W.model.venues, 'segment': W.model.segments }; return map[type]; }; this.setUi = function (u) { ui = u; }; this.Select = function (id, type, center, zoom) { var attemptNum = 10; var select = function () { info('select: ' + id); var obj = type2repo(type).getObjectById(id); W.model.events.unregister('mergeend', map, select); if (obj) { W.selectionManager.setSelectedModels([obj]); } else if (--attemptNum > 0) { W.model.events.register('mergeend', map, select); } debug("Attempt number left: " + attemptNum); W.map.setCenter(center, zoom); }; return select; }; this.isObjectVisible = function (obj) { if (!onlyVisible) return true; if (obj.geometry) { return W.map.getExtent().intersectsBounds(obj.geometry.getBounds()); } return false; }; var onlyVisible = false; this.fixProblem = function (problem) { var deferred = $.Deferred(); var attemptNum = 10; // after that we decide that object was removed var setOld2Alt = localStorage.getItem('assist_move_old_to_alt') == 'true'; var fix = function () { var uniqueId = problem.object.id + '_' + problem.streetID; var obj = type2repo(problem.object.type).getObjectById(problem.object.id); W.model.events.unregister('mergeend', map, fix); if (obj) { var addr = obj.getAddress().attributes; var attr = { countryID: addr.country.id, stateID: addr.state.id, cityName: addr.city.attributes.name, emptyCity: addr.city.attributes.name === null || addr.city.attributes.name === '', streetName: problem.newStreetName, emptyStreet: problem.isEmpty }; // check if alternative name if (problem.attrName == 'streetIDs') { // check if still exist if (obj.attributes.streetIDs.indexOf(problem.streetID) > -1) { // remove old street and keep other ones var streets2keep = []; obj.attributes.streetIDs.forEach(function(sid) { if (problem.streetID !== sid) { streets2keep.push(sid); } else { var altStreet = W.model.streets.getObjectById(sid); var city = W.model.cities.getObjectById(altStreet.cityID); attr.cityName = city.attributes.name; attr.emptyCity = city.hasName() ? null : true; } }); W.model.actionManager.add(new WazeActionUpdateObject(obj, { streetIDs: streets2keep})); // add new street W.model.actionManager.add(new WazeActionAddAlternateStreet(obj, attr, { streetIDField: problem.attrName })); } else { ui.updateProblem(uniqueId, '(not found. Deleted?)'); } } else { // protect user manual fix if (problem.reason == addr.street.name) { W.model.actionManager.add(new WazeActionUpdateFeatureAddress(obj, attr, { streetIDField: problem.attrName })); // move old name to alt street, if option enabled if (setOld2Alt && obj.type == 'segment') { var altAttr = { countryID: addr.country.id, stateID: addr.state.id, cityName: addr.city.attributes.name, emptyCity: addr.city.attributes.name === null || addr.city.attributes.name === '', streetName: problem.reason, emptyStreet: false //problem.isEmpty }; W.model.actionManager.add(new WazeActionAddAlternateStreet(obj, altAttr, { streetIDField: problem.attrName })); } } else { ui.updateProblem(uniqueId, '(user fix: ' + addr.street.name + ')'); } } deferred.resolve(uniqueId); } else if (--attemptNum <= 0) { ui.updateProblem(uniqueId, '(was not fixed. Deleted?)'); deferred.resolve(uniqueId); } else { W.model.events.register('mergeend', map, fix); W.map.setCenter(problem.detectPos, problem.zoom); } debug('Attempt number left: ' + attemptNum); }; fix(); return deferred.promise(); }; }; var Ui = function (countryName) { var addon = document.createElement('div'); addon.innerHTML = '<wz-overline>' + scriptName + ' v' + GM_info.script.version + '</wz-overline>'; var section = document.createElement('div'); section.id = "assist_options"; section.className = "form-group"; section.innerHTML = '<wz-label>Options</wz-label>' + '<wz-checkbox name="assist_enabled" id="assist_enabled" value="on">Enable/disable</wz-checkbox>' + '<wz-checkbox name="assist_skip_alt" id="assist_skip_alt" value="on">Skip checking alternative names</wz-checkbox>' + '<wz-checkbox name="assist_move_old_to_alt" id="assist_move_old_to_alt" value="on">Move old name to alternative</wz-checkbox>' + '<wz-checkbox name="assist_debug" id="assist_debug" value="on">Enable debug log</wz-checkbox>'; addon.appendChild(section); var variant = document.createElement('div'); variant.id = 'variant_options'; variant.className = "form-group"; // adopt city names for Ukraine //if (countryName == 'Ukraine') { variant.innerHTML = '<wz-label>Naming Rules <a href="https://wazeopedia.waze.com/wiki/Ukraine/Як_називати_вулиці" target="_blank"><span class="fa fa-question-circle"></span></a></wz-label>' + '<wz-radio-button name="assist_variant" value="Ukraine" checked="">Ukraine (Classic)</wz-radio-button>' + '<wz-radio-button name="assist_variant" value="Lviv">🦁 Lviv (Alternative)</wz-radio-button>'; if (!jQuery.isEmptyObject(rulesDB)) { console.log("WME Assist UA INFO: Downloaded " + Object.keys(rulesDB).length + " rules from Google Sheet"); variant.innerHTML += '<wz-radio-button name="assist_variant" value="GSheets">Rules from Google Sheet ('+ Object.keys(rulesDB).length + ')</wz-radio-button>'; } //} else { // todo: add other countries support if needed // variant.innerHTML = ''; //} addon.appendChild(variant); section = document.createElement('div'); section.id = "assist_custom_rules"; section.className = "form-group"; $(section) .append($('<wz-label>Custom Rules</wz-label>')) .append($('<wz-checkbox name="assist_enable_custom_rules" id="assist_enable_custom_rules" value="on">Enable custom rules</wz-checkbox>')) .append($('<div>').addClass('btn-toolbar') .append($('<button>').prop('id', 'assist_add_custom_rule').addClass('btn btn-default btn-primary').text('Add')) .append($('<button>').prop('id', 'assist_edit_custom_rule').addClass('btn btn-default').text('Edit')) .append($('<button>').prop('id', 'assist_del_custom_rule').addClass('btn btn-default btn-warning').text('Del'))) .append($('<ul>').addClass('result-list').css({"height": "250px", "overflow": "auto"})); addon.appendChild(section); section = document.createElement('div'); section.id = "assist_exceptions"; section.className = "form-group"; $(section) .append($('<wz-label title="Right click on error in list to add">').text('Exceptions')) .append($('<ul>').addClass('result-list').css({"height": "250px", "overflow": "auto"})); addon.appendChild(section); var newtab = document.createElement('li'); newtab.innerHTML = '<a href="#sidepanel-assist" data-toggle="tab">Assist</a>'; $('#user-info #user-tabs .nav-tabs').append(newtab); addon.id = "sidepanel-assist"; addon.className = "tab-pane"; $('#user-info > div > .tab-content').append(addon); var selectedCustomRule = -1; this.selectedCustomRule = function () { return selectedCustomRule; }; this.addCustomRule = function (title) { var thisrule = $('<li>').addClass('result').click(function () { selectedCustomRule = $('#assist_custom_rules li.result').index(thisrule); info('index: ' + selectedCustomRule); $('#assist_custom_rules li.result').css({'background-color': ''}); $('#assist_custom_rules li.result').removeClass('active'); $(this).css({'background-color': 'lightblue'}); $(this).addClass('active'); }).hover(function () { $(this).css({ cursor: 'pointer', 'background-color': 'lightblue' }); }, function () { $(this).css({ cursor: 'auto' }); if (!$(this).hasClass('active')) { $(this).css({ 'background-color': '' }); } }) .append($('<p>').addClass('additional-info clearfix').text(title)) .appendTo($('#assist_custom_rules ul.result-list')); }; this.updateCustomRule = function (index, title) { $('#assist_custom_rules li.result').eq(index).find('p.additional-info').text(title); }; this.removeCustomRule = function (index) { $('#assist_custom_rules li.result').eq(index).remove(); selectedCustomRule = -1; }; this.addException = function (name, del) { var thisrule = $('<li>').addClass('result').click(function () { var index = $('#assist_exceptions li.result').index(thisrule); del(index); }).hover(function () { $(this).css({ cursor: 'pointer', 'background-color': 'lightblue' }); }, function () { $(this).css({ cursor: 'auto' }); if (!$(this).hasClass('active')) { $(this).css({ 'background-color': '' }); } }) .append($('<p>').addClass('additional-info clearfix').text(name)) .appendTo($('#assist_exceptions ul.result-list')); }; this.removeException = function (index) { $('#assist_exceptions li.result').eq(index).remove(); }; this.showMainWindow = function () { $('#WazeMap').css('overflow', 'hidden'); mainWindow.dialog('open'); mainWindow.dialog('option', 'position', { my: 'right top', at: 'right-70 top+50', of: '#WazeMap' }); // Minimize window mainWindow.prev('.ui-dialog-titlebar').find('button').click(); }; this.hideMainWindow = function () { mainWindow.dialog('close'); }; $('<div>', { id: 'WME_AssistWindow', title: scriptName, }) .append($('<div>').css({ padding: 10, }) .append($('<div class="btn-toolbar">') .append($('<button id="assist_fixall_btn" class="btn waze-btn waze-btn-small waze-btn-red">Fix all</button>')) .append($('<button id="assist_fixselected_btn" class="btn waze-btn waze-btn-small waze-btn-red">Fix selected</button>')) .append($('<button id="assist_scanarea_btn" class="btn waze-btn waze-btn-small waze-btn-blue">Scan area</button>')) .append($('<button id="assist_clearfixed_btn" class="btn waze-btn waze-btn-small waze-btn-green">Clear fixed</button>')) .append($('<button id="assist_clearall_btn" class="btn waze-btn waze-btn-small waze-btn-grey" title="Clear all results"><i class="fa fa-close"></i></button>'))) .append($('<h2>Unresolved issues</h2>').css({ 'font-size': '100%', 'font-weight': 'bold', })) .append($('<ol id="assist_unresolved_list"></ol>').css({ border: '1px solid lightgrey', 'padding-top': 2, 'padding-bottom': 2, }))) .append($('<div>').css({ padding: 10, }) .append($('<h2>Fixed issues</h2>').css({ 'font-size': '100%', 'font-weight': 'bold', })) .append($('<ol id="assist_fixed_list"></ol>').css({ border: '1px solid lightgrey', 'padding-top': 2, 'padding-bottom': 2, }))) .appendTo($('#WazeMap')); $('<div>').prop('id', 'assist_custom_rule_dialog') .append($('<p>All form fields are required</p>')) .append($('<form>') .append($('<fieldset>') .append($('<label>').prop('for', 'oldname').text('RegExp')) .append($('<input>', { type: 'text', name: 'oldname', 'class': 'text ui-widget-content ui-corner-all', id: 'oldname', })) .append($('<label>').prop('for', 'newname').text('Replace text')) .append($('<input>', { type: 'text', name: 'newname', 'class': 'text ui-widget-content ui-corner-all', id: 'newname', })) ) ) .appendTo($('#WazeMap')); $('#assist_custom_rule_dialog label').css({display: 'block'}); $('#assist_custom_rule_dialog input').css({display: 'block', width: '100%'}); var customRuleDialog_Ok = function () {}; var customRuleDialog = $('#assist_custom_rule_dialog').dialog({ autoOpen: false, height: 300, width: 350, modal: true, buttons: { Ok: function () { customRuleDialog_Ok(); customRuleDialog.dialog('close'); }, Cancel: function () { customRuleDialog.dialog('close'); } } }); var mainWindow = $('#WME_AssistWindow').dialog({ autoOpen: false, appendTo: $('#waze-map-container'), width: 500, draggable: true, height: 600, dragStop: function () { $('#WME_AssistWindow').parent().css('height', 'auto'); } }); mainWindow.parent('.ui-dialog').css({ 'zIndex': 1040, 'opacity': '0.9', }); mainWindow.prev('.ui-dialog-titlebar').css('background','lightblue'); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title').append($('<span> - </span>')); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title') .append($('<span>', { id: 'assist-error-num', title: 'Number of unresolved issues', text: 0, }).css({color: 'red'})); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title').append($('<span> / </span>')); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title') .append($('<span>', { id: 'assist-fixed-num', title: 'Number of fixed issues', text: 0, }).css({color: 'green'})); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title').append($('<span> - </span>')); mainWindow.prev('.ui-dialog-titlebar').find('.ui-dialog-title') .append($('<span>', { id: 'assist-scan-progress', title: 'Scan progress', text: 0, }).css({color: 'blue'})); // Hack jquery ui dialog var icon = mainWindow.prev('.ui-dialog-titlebar').find('span.ui-icon'); if (!icon.hasClass('ui-icon-minusthick')) { icon.addClass('ui-icon-minusthick'); } if (icon.hasClass('ui-icon-closethick')) { icon.removeClass('ui-icon-closethick'); } var btn = mainWindow.prev('.ui-dialog-titlebar').find('button'); mainWindow.prev('.ui-dialog-titlebar').find('button').unbind('click'); mainWindow.prev('.ui-dialog-titlebar').find('button').click(function () { if ($('#WME_AssistWindow').is(':visible')) { $('#WME_AssistWindow').hide(); btn.prop('title', 'maximize'); icon.removeClass('ui-icon-minusthick'); icon.addClass('ui-icon-arrow-4-diag'); } else { $('#WME_AssistWindow').show(); btn.prop('title', 'minimize'); icon.addClass('ui-icon-minusthick'); icon.removeClass('ui-icon-arrow-4-diag'); } }); var self = this; this.addProblem = function (id, text, selectFunc, editFunc, exception, experimental) { var problem = $('<li>') .prop('id', 'issue-' + id) .append($('<input>', { value: id, type: "checkbox" })) .append($('<a>', { href: "javascript:void(0)", text: text, click: function (event) { selectFunc(event); }, contextmenu: function (event) { exception(event); event.preventDefault(); event.stopPropagation(); }, })) .append(' ') .append($('<span>', { title: "Add custom rule for this problem", class: "fa fa-edit", style: "cursor: pointer;", click: function (event) { editFunc(event); } })) .appendTo($('#assist_unresolved_list')); if (experimental) { problem.children().css({color: 'red'}).prop('title', 'Experimental rule'); } }; this.getCheckedItemsList = function () { var itemsList = []; $('#assist_unresolved_list').find('input').each(function () { if (this.checked) { itemsList.push(this.value); } }); return itemsList; }; this.updateProblem = function (id, text) { var a = $('li#issue-' + escapeId(id) + ' > a'); a.text(a.text() + ' ' + text); }; this.setUnresolvedErrorNum = function (text) { $('#assist-error-num').text(text); }; this.setFixedErrorNum = function (text) { $('#assist-fixed-num').text(text); }; this.setScanProgress = function (text) { $('#assist-scan-progress').text(text); }; var escapeId = function (id) { return String(id).replace(/\./g, "\\."); }; this.moveToFixedList = function (id) { $("#issue-" + escapeId(id)).appendTo($('#assist_fixed_list')).find("span").remove(); $("#issue-" + escapeId(id)).find("input").remove(); }; this.removeError = function (id) { $("#issue-" + escapeId(id)).remove(); }; var fixAllBtn = $('#assist_fixall_btn'); var fixSelectedBtn = $('#assist_fixselected_btn'); var scanAreaBtn = $('#assist_scanarea_btn'); var clearFixedBtn = $('#assist_clearfixed_btn'); var clearAllBtn = $('#assist_clearall_btn'); var unresolvedList = $('#assist_unresolved_list'); var fixedList = $('#assist_fixed_list'); var enableCheckbox = $('#assist_enabled'); var skipAltCheckbox = $('#assist_skip_alt'); var moveOld2AltCheckbox = $('#assist_move_old_to_alt'); var debugCheckbox = $('#assist_debug'); var enableCustomRulesCheckbox = $('#assist_enable_custom_rules'); var addCustomRuleBtn = $('#assist_add_custom_rule'); var editCustomRuleBtn = $('#assist_edit_custom_rule'); var delCustomRuleBtn = $('#assist_del_custom_rule'); this.fixAllBtn = function () { return fixAllBtn; }; this.fixSelectedBtn = function () { return fixSelectedBtn; }; this.scanAreaBtn = function () { return scanAreaBtn; }; this.clearFixedBtn = function () { return clearFixedBtn; }; this.clearAllBtn = function () { return clearAllBtn; }; this.unresolvedList = function () { return unresolvedList; }; this.fixedList = function () { return fixedList; }; this.enableCheckbox = function () { return enableCheckbox; }; this.skipAltCheckbox = function () { return skipAltCheckbox; }; this.moveOld2AltCheckbox = function () { return moveOld2AltCheckbox; }; this.debugCheckbox = function () { return debugCheckbox; }; this.enableCustomRulesCheckbox = function () { return enableCustomRulesCheckbox; }; this.variantRadio = function (value) { if (!value) { return $('[name=assist_variant]'); } return $('[name=assist_variant][value=' + value + ']'); }; this.addCustomRuleBtn = function () { return addCustomRuleBtn; }; this.editCustomRuleBtn = function () { return editCustomRuleBtn; }; this.delCustomRuleBtn = function () { return delCustomRuleBtn; }; this.customRuleDialog = function (title, params) { var deferred = $.Deferred(); if (params) { customRuleDialog.find('#oldname').val(params.oldname); customRuleDialog.find('#newname').val(params.newname); } customRuleDialog_Ok = function () { deferred.resolve({ oldname: customRuleDialog.find('#oldname').val(), newname: customRuleDialog.find('#newname').val(), }); }; customRuleDialog.dialog('option', 'title', title); customRuleDialog.dialog('open'); return deferred.promise(); }; //this.variant = function () { // return $('[name=assist_variant][checked]')[0].value; //}; }; var Scanner = function () { var map = W.map; var ROAD_TYPE = { STREET: 1, PRIMARY_STREET: 2, FREEWAY: 3, RAMP: 4, WALKING_TRAIL: 5, MAJOR_HIGHWAY: 6, MINOR_HIGHWAY: 7, OFF_ROAD: 8, WALKWAY: 9, PEDESTRIAN_BOARDWALK: 10, FERRY: 15, STAIRWAY: 16, PRIVATE_ROAD: 17, RAILROAD: 18, RUNWAY_TAXIWAY: 19, PARKING_LOT_ROAD: 20, ALLEY: 22 }; var zoomToRoadType = function (e) { if (e < 14) { return []; } switch (e) { case 14: return [ROAD_TYPE.PRIMARY_STREET, ROAD_TYPE.FREEWAY, ROAD_TYPE.RAMP, ROAD_TYPE.MAJOR_HIGHWAY, ROAD_TYPE.MINOR_HIGHWAY, ROAD_TYPE.FERRY]; case 15: return [ROAD_TYPE.PRIMARY_STREET, ROAD_TYPE.FREEWAY, ROAD_TYPE.RAMP, ROAD_TYPE.MAJOR_HIGHWAY, ROAD_TYPE.MINOR_HIGHWAY, ROAD_TYPE.OFF_ROAD, ROAD_TYPE.WALKWAY, ROAD_TYPE.PEDESTRIAN_BOARDWALK, ROAD_TYPE.FERRY, ROAD_TYPE.STAIRWAY, ROAD_TYPE.PRIVATE_ROAD, ROAD_TYPE.RAILROAD, ROAD_TYPE.RUNWAY_TAXIWAY, ROAD_TYPE.PARKING_LOT_ROAD, ROAD_TYPE.ALLEY]; default: return Object.values(ROAD_TYPE); } }; var zoomToVenueLevel = function (e) { switch (e) { case 12: return 1; case 13: return 2; case 14: case 15: case 16: return 3; case 17: case 18: case 19: case 20: case 21: case 22: return 4; default: return null; } }; var getData = function (e, cb) { debug(e); $.get(W.Config.paths.features, e).done(cb); }; var splitExtent = function (extent, zoom) { var result = []; var ratio = 1; //map.getResolution() / map.getResolutionForZoom(zoom); //FIXME: temporary commented, because getResolutionForZoom() is gone var dx = extent.getWidth() / ratio; var dy = extent.getHeight() / ratio; var x, y; for (x = extent.left; x < extent.right; x += dx) { for (y = extent.bottom; y < extent.top; y += dy) { var bounds = new OpenLayers.Bounds(); bounds.extend(new OpenLayers.LonLat(x, y)); bounds.extend(new OpenLayers.LonLat(x + dx, y + dy)); result.push(bounds); } } return result; }; this.scan = function (bounds, zoom, analyze, progress) { var boundsArray = splitExtent(bounds, zoom); var completed = 0; if (boundsArray.length > 20 && !confirm('Script will scan ' + boundsArray.length + ' pieces. Are you OK?')) { return; } progress = progress || function () { }; series(boundsArray, 0, function (bounds, next) { var piece = bounds.transform(map.getProjectionObject(), 'EPSG:4326'); var e = { bbox: piece.toBBOX(), language: I18n.locale, venueFilter: '3', venueLevel: zoomToVenueLevel(zoom), }; var z = { roadTypes: zoomToRoadType(zoom).toString() }; OpenLayers.Util.extend(e, z); getData(e, function (data) { analyze(piece, zoom, data); progress(++completed * 100 / boundsArray.length); next(); }); }); }; }; var Analyzer = function () { var Exceptions = function () { var exceptions = []; var onAdd = function (name) { }; var onDelete = function (index) { }; var save = function (exceptions) { if (localStorage) { localStorage.setItem('assistExceptionsKey', JSON.stringify(exceptions)); } }; this.load = function () { if (localStorage) { var str = localStorage.getItem('assistExceptionsKey'); if (str) { var arr = JSON.parse(str); for (var i = 0; i < arr.length; ++i) { var exception = arr[i]; this.add(exception); } } } }; this.contains = function (name) { if (exceptions.indexOf(name) == -1) return false; return true; }; this.add = function (name) { exceptions.push(name); save(exceptions); onAdd(name); }; this.remove = function (index) { exceptions.splice(index, 1); save(exceptions); onDelete(index); }; this.onAdd = function (cb) { onAdd = cb; }; this.onDelete = function (cb) { onDelete = cb; }; }; var analyzedIds = []; var problems = []; var unresolvedIdx = 0; var skippedErrors = 0; var variant; var exceptions = new Exceptions(); var rules; var action; var getUnresolvedErrorNum = function () { return problems.length - unresolvedIdx - skippedErrors; }; var getFixedErrorNum = function () { return unresolvedIdx; }; this.unresolvedErrorNum = getUnresolvedErrorNum; this.fixedErrorNum = getFixedErrorNum; this.setRules = function (r) { rules = r; }; this.setActionHelper = function (a) { action = a; }; this.loadExceptions = function () { exceptions.load(); }; this.onExceptionAdd = function (cb) { exceptions.onAdd(cb); }; this.onExceptionDelete = function (cb) { exceptions.onDelete(cb); }; this.addException = function (reason, cb) { exceptions.add(reason); var i; for (i = 0; i < problems.length; ++i) { var problem = problems[i]; if (problem.reason == reason) { problem.skip = true; ++skippedErrors; cb(problem.object.id); } } }; this.removeException = function (i) { exceptions.remove(i); }; this.setVariant = function (v) { variant = v; }; this.reset = function () { analyzedIds = []; problems = []; unresolvedIdx = 0; skippedErrors = 0; }; this.fixAll = function (oneFixed, allFixed) { series(problems, unresolvedIdx, function (p, next) { if (p.skip) { next(); return; } action.fixProblem(p).done(function (id) { ++unresolvedIdx; oneFixed(id); setTimeout(next, 0); }); }, allFixed); }; this.fixSelected = function (listToFix, oneFixed, allFixed) { series(problems, unresolvedIdx, function (p, next) { if (listToFix.indexOf(p.object.id + '_' + p.streetID) == -1) { next(); return; } if (p.skip) { next(); return; } action.fixProblem(p).done(function (id) { ++unresolvedIdx; oneFixed(id); setTimeout(next, 0); }); }, allFixed); }; var checkStreet = function (bounds, zoom, streetID, obj, attrName, onProblemDetected) { var userlevel = W.loginManager.user.rank + 1; var street = W.model.streets.getObjectById(streetID); if (!street) return; var detected = false; var skip = false; var title = ''; var reason; var newStreetName; if (!street.isEmpty) { if (!exceptions.contains(street.name)) { try { var city = W.model.cities.getObjectById(street.cityID); var result = rules.correct(variant, street.name, city.attributes.name); newStreetName = result.value; detected = (newStreetName != street.name); if (obj.type == 'venue') { title = 'POI: '; } // alternative names if (attrName == 'streetIDs') { title = 'ALT: '; } // if user has lower rank, just show the segment, but no fix allowed if (obj.lockRank && obj.lockRank >= userlevel) { title = '(L' + (obj.lockRank + 1) + ') ' + title; skip = true; } // show segments with closures, but lock them from fixing if (obj.hasClosures) { title = '(🚧) ' + title; skip = true; } title = title + street.name.replace(/\u00A0/g, '■').replace(/^\s|\s$/, '■'); // for "detect only rules" we have no replacement to show if (!newStreetName) { skip = true; } else { title = title + ' ➤ ' + newStreetName; } if (skip) { title = '🔒 ' + title; } reason = street.name; } catch (err) { warning('Street name "' + street.name + '" causes error in rules'); return; } } } if (detected) { var gj = new OpenLayers.Format.GeoJSON(); var geometry = gj.parseGeometry(obj.geometry); var objCenter = geometry.getBounds().getCenterLonLat().transform(W.Config.map.projection.remote, W.map.getProjectionObject()); var boundsCenter = bounds.clone().getCenterLonLat().transform(W.Config.map.projection.remote, W.map.getProjectionObject()); obj.center = objCenter; problems.push({ object: obj, reason: reason, attrName: attrName, detectPos: boundsCenter, zoom: zoom, newStreetName: newStreetName, isEmpty: street.isEmpty, cityId: street.cityID, streetID: streetID, experimental: false, skip: skip, }); onProblemDetected(obj.id + '_' + streetID, obj, title, reason); } }; this.analyze = function (bounds, zoom, data, onProblemDetected) { var startTime = new Date().getTime(); var analyzeAlt = true; info('start analyze'); var subjects = { 'segment': { attr: 'primaryStreetID', name: 'segments' }, 'venue': { attr: 'streetID', name: 'venues' } }; if (localStorage) { if (localStorage.getItem('assist_skip_alt') == 'true') { analyzeAlt = false; } } for (var k in subjects) { var subject = subjects[k]; var subjectData = data[subject.name]; if (!subjectData) continue; var objects = subjectData.objects; for (var i = 0; i < objects.length; ++i) { var obj = objects[i]; var id = obj.id; obj.type = k; if (analyzedIds.indexOf(id) >= 0) continue; if (typeof obj.approved != 'undefined' && !obj.approved) continue; checkStreet(bounds, zoom, obj[subject.attr], obj, subject.attr, onProblemDetected); // add ugly support for alternative names if (subject.name == 'segments' && analyzeAlt) { for (var j = 0, n = obj.streetIDs.length; j < n; j++) { checkStreet(bounds, zoom, obj.streetIDs[j], obj, 'streetIDs', onProblemDetected); } } analyzedIds.push(id); } } info('end analyze: ' + (new Date().getTime() - startTime) + 'ms'); }; }; var Application = function () { var scanner = new Scanner(); var analyzer = new Analyzer(); var FULL_ZOOM_LEVEL = 17; var scanForZoom = function (zoom) { scanner.scan(W.map.calculateBounds(), zoom, function (bounds, zoom, data) { debug(data); //var w = window.open(); //w.document.open(); //w.document.write(JSON.stringify(data)); //w.document.close(); analyzer.analyze(bounds, zoom, data, function (id, obj, title, reason) { ui.addProblem(id, title, action.Select(obj.id, obj.type, obj.center, zoom), function () { ui.customRuleDialog('Add custom rule', { oldname: '(.*)' + reason + '(.*)', newname: reason }).done(function (response) { rules.push(response.oldname, response.newname); ui.scanAreaBtn().click(); }); }, function () { analyzer.addException(reason, function (id) { ui.removeError(id); ui.setUnresolvedErrorNum(analyzer.unresolvedErrorNum()); }); }, false); ui.setUnresolvedErrorNum(analyzer.unresolvedErrorNum()); }); }, function (progress) { ui.setScanProgress(Math.round(progress) + '%'); }); }; var fullscan = function () { scanForZoom(FULL_ZOOM_LEVEL); }; var scan = function () { scanForZoom(W.map.getZoom()); }; var countryName = function () { var id = W.model.countries.top.id; var name = W.model.countries.getObjectById(id).name; return name; }; var country = countryName(); var action = new ActionHelper(); var rules = new Rules(country); var ui = new Ui(country); analyzer.setRules(rules); analyzer.setActionHelper(action); action.setUi(ui); analyzer.onExceptionAdd(function (name) { ui.addException(name, function (index) { if (confirm('Delete exception for ' + name + '?')) { analyzer.removeException(index); } }); }); analyzer.onExceptionDelete(function (index) { ui.removeException(index); }); // rules.experimental = true; rules.onAdd(function (rule) { ui.addCustomRule(rule.comment); }); rules.onEdit(function (index, rule) { ui.updateCustomRule(index, rule.comment); }); rules.onDelete(function (index) { ui.removeCustomRule(index); }); W.model.events.register('mergeend', map, function () { var name = countryName(); if (name != country) { rules.onCountryChange(name); country = name; } }); analyzer.loadExceptions(); rules.load(); this.start = function () { ui.enableCheckbox().change(function () { if (this.checked) { localStorage.setItem('assist_enabled', true); ui.showMainWindow(); info('enabled'); var savedVariant = localStorage.getItem('assist_variant'); if (savedVariant !== null) { ui.variantRadio(savedVariant).prop('checked', true); analyzer.setVariant(savedVariant); } scan(); W.model.events.register('mergeend', map, scan); } else { localStorage.setItem('assist_enabled', false); ui.hideMainWindow(); info('disabled'); W.model.events.unregister('mergeend', map, scan); } }); ui.skipAltCheckbox().change(function () { localStorage.setItem('assist_skip_alt', this.checked); ui.scanAreaBtn().click(); }); ui.moveOld2AltCheckbox().change(function () { localStorage.setItem('assist_move_old_to_alt', this.checked); if (this.checked) { // force enable skip alt option localStorage.setItem('assist_skip_alt', true); ui.skipAltCheckbox().prop('checked', true); ui.skipAltCheckbox().prop('disabled', true); } else { // unblock skip alt option ui.skipAltCheckbox().prop('disabled', false); } }); ui.debugCheckbox().change(function () { localStorage.setItem('assist_debug', this.checked); }); ui.enableCustomRulesCheckbox().change(function () { localStorage.setItem('assist_enable_custom_rules', this.checked); ui.scanAreaBtn().click(); }); ui.variantRadio().change(function (e) { if (e.currentTarget.checked) { localStorage.setItem('assist_variant', this.value); analyzer.setVariant(this.value); ui.scanAreaBtn().click(); } }); if (localStorage.getItem('assist_enabled') == 'true') { ui.enableCheckbox().click(); } if (localStorage.getItem('assist_skip_alt') == 'true') { ui.skipAltCheckbox().click(); } if (localStorage.getItem('assist_move_old_to_alt') == 'true') { ui.moveOld2AltCheckbox().click(); } if (localStorage.getItem('assist_debug') == 'true') { ui.debugCheckbox().click(); } if (localStorage.getItem('assist_enable_custom_rules') == 'true') { ui.enableCustomRulesCheckbox().click(); } ui.fixAllBtn().click(function () { ui.fixAllBtn().hide(); ui.fixSelectedBtn().hide(); ui.scanAreaBtn().hide(); ui.clearFixedBtn().hide(); ui.clearAllBtn().hide(); W.model.events.unregister('mergeend', map, scan); setTimeout(function () { analyzer.fixAll(function (id) { ui.setUnresolvedErrorNum(analyzer.unresolvedErrorNum()); ui.setFixedErrorNum(analyzer.fixedErrorNum()); ui.moveToFixedList(id); }, function () { ui.fixAllBtn().show(); ui.fixSelectedBtn().show(); ui.scanAreaBtn().show(); ui.clearFixedBtn().show(); ui.clearAllBtn().show(); W.model.events.register('mergeend', map, scan); }); }, 0); }); ui.fixSelectedBtn().click(function () { ui.fixAllBtn().hide(); ui.fixSelectedBtn().hide(); ui.scanAreaBtn().hide(); ui.clearFixedBtn().hide(); ui.clearAllBtn().hide(); W.model.events.unregister('mergeend', map, scan); var listToFix = ui.getCheckedItemsList(); setTimeout(function () { analyzer.fixSelected(listToFix, function (id) { ui.setUnresolvedErrorNum(analyzer.unresolvedErrorNum()); ui.setFixedErrorNum(analyzer.fixedErrorNum()); ui.moveToFixedList(id); }, function () { ui.fixAllBtn().show(); ui.fixSelectedBtn().show(); ui.scanAreaBtn().show(); ui.clearFixedBtn().show(); ui.clearAllBtn().show(); W.model.events.register('mergeend', map, scan); }); }, 0); }); ui.clearFixedBtn().click(function () { ui.fixedList().empty(); }); ui.clearAllBtn().click(function () { ui.fixedList().empty(); ui.unresolvedList().empty(); analyzer.reset(); ui.setUnresolvedErrorNum(0); ui.setFixedErrorNum(0); }); ui.scanAreaBtn().click(function () { ui.fixedList().empty(); ui.unresolvedList().empty(); analyzer.reset(); ui.setUnresolvedErrorNum(0); ui.setFixedErrorNum(0); fullscan(); }); ui.addCustomRuleBtn().click(function () { ui.customRuleDialog('Add', { oldname: '', newname: '' }).done(function (response) { rules.push(response.oldname, response.newname); }); }); ui.editCustomRuleBtn().click(function () { var id = ui.selectedCustomRule(); if (id >= 0) { ui.customRuleDialog('Edit', { oldname: rules.get(id).oldname, newname: rules.get(id).newname }).done(function (response) { rules.update(id, response.oldname, response.newname); }); } else { alert('Custom rule is not selected'); } }); ui.delCustomRuleBtn().click(function () { var id = ui.selectedCustomRule(); if (id >= 0) { rules.remove(id); } else { alert('Custom rule is not selected'); } }); window.assist = this; }; }; function waitForWaze(doneFunc) { // Wait for Waze and jQuery.ui if (!isWazeApiReady() || !jQuery.ui) { info("waiting for Waze"); setTimeout(function () { waitForWaze(doneFunc); }, 1500); return; } doneFunc(function () { info("Ready to work!"); var app = new Application(); app.start(); }); } waitForWaze(requestRules); } run_wme_assist();