// ==UserScript==
// @name WME HN NavPoints
// @namespace https://greasyfork.org/users/166843
// @description Shows navigation points of all house numbers in WME
// @version 2024.08.18.01
// @author dBsooner
// @grant GM_xmlhttpRequest
// @connect greasyfork.org
// @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @license GPLv3
// @match http*://*.waze.com/*editor*
// @exclude http*://*.waze.com/user/editor*
// @contributionURL https://github.com/WazeDev/Thank-The-Authors
// ==/UserScript==
/* global _, GM_info, GM_xmlhttpRequest, OpenLayers, W, WazeWrap */
/*
* Original concept and code for WME HN NavPoints was written by MajkiiTelini. After version 0.6.6, this
* script is maintained by the WazeDev team. Special thanks is definitely given to MajkiiTelini for his
* hard work and dedication to the original script.
*
*/
(function () {
'use strict';
// eslint-disable-next-line no-nested-ternary
const _SCRIPT_SHORT_NAME = `HN NavPoints${(/beta/.test(GM_info.script.name) ? ' β' : /\(DEV\)/i.test(GM_info.script.name) ? ' Ω' : '')}`,
_SCRIPT_LONG_NAME = GM_info.script.name,
_IS_ALPHA_VERSION = /[Ω]/.test(_SCRIPT_SHORT_NAME),
_IS_BETA_VERSION = /[β]/.test(_SCRIPT_SHORT_NAME),
_PROD_DL_URL = 'https://greasyfork.org/scripts/390565-wme-hn-navpoints/code/WME%20HN%20NavPoints.user.js',
_FORUM_URL = 'https://www.waze.com/forum/viewtopic.php?f=819&t=269397',
_SETTINGS_STORE_NAME = 'WMEHNNavPoints',
_BETA_DL_URL = 'YUhSMGNITTZMeTluY21WaGMzbG1iM0pyTG05eVp5OXpZM0pwY0hSekx6TTVNRFUzTXkxM2JXVXRhRzR0Ym1GMmNHOXBiblJ6TFdKbGRHRXZZMjlrWlM5WFRVVWxNakJJVGlVeU1FNWhkbEJ2YVc1MGN5VXlNQ2hpWlhSaEtTNTFjMlZ5TG1weg==',
_ALERT_UPDATE = true,
_SCRIPT_VERSION = GM_info.script.version.toString(),
_SCRIPT_VERSION_CHANGES = ['CHANGE: WME beta release v2.242 compatibility.'],
_DEBUG = /[βΩ]/.test(_SCRIPT_SHORT_NAME),
_LOAD_BEGIN_TIME = performance.now(),
_elems = {
div: document.createElement('div'),
h4: document.createElement('h4'),
h6: document.createElement('h6'),
form: document.createElement('form'),
i: document.createElement('i'),
label: document.createElement('label'),
li: document.createElement('li'),
p: document.createElement('p'),
svg: document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
svgText: document.createElementNS('http://www.w3.org/2000/svg', 'text'),
ul: document.createElement('ul'),
'wz-checkbox': document.createElement('wz-checkbox'),
'wz-text-input': document.createElement('wz-text-input')
},
_spinners = {
destroyAllHNs: false,
drawHNs: false,
processSegs: false
},
_timeouts = {
checkMarkersEvents: {},
hideTooltip: undefined,
onWmeReady: undefined,
saveSettingsToStorage: undefined,
stripTooltipHTML: undefined
},
dec = (s = '') => atob(atob(s));
let _settings = {},
_scriptActive = false,
_saveButtonObserver,
_HNNavPointsLayer,
_HNNavPointsNumbersLayer,
_processedSegments = [],
_segmentsToProcess = [],
_segmentsToRemove = [],
_hnNavPointsTooltipDiv,
_popup = {
inUse: false,
hnNumber: -1,
segmentId: -1
};
function log(message, data = '') { console.log(`${_SCRIPT_SHORT_NAME}:`, message, data); }
function logError(message, data = '') { console.error(`${_SCRIPT_SHORT_NAME}:`, new Error(message), data); }
// function logWarning(message, data = '') { console.warn(`${_SCRIPT_SHORT_NAME}:`, message, data); }
function logDebug(message, data = '') {
if (_DEBUG)
log(message, data);
}
function $extend(...args) {
const extended = {},
deep = Object.prototype.toString.call(args[0]) === '[object Boolean]' ? args[0] : false,
merge = function (obj) {
Object.keys(obj).forEach((prop) => {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]')
extended[prop] = $extend(true, extended[prop], obj[prop]);
else if ((obj[prop] !== undefined) && (obj[prop] !== null))
extended[prop] = obj[prop];
}
});
};
for (let i = deep ? 1 : 0, { length } = args; i < length; i++) {
if (args[i])
merge(args[i]);
}
return extended;
}
function createElem(type = '', attrs = {}, eventListener = []) {
const el = _elems[type]?.cloneNode(false) || _elems.div.cloneNode(false),
applyEventListeners = function ([evt, cb]) {
return this.addEventListener(evt, cb);
};
Object.keys(attrs).forEach((attr) => {
if ((attrs[attr] !== undefined) && (attrs[attr] !== 'undefined') && (attrs[attr] !== null) && (attrs[attr] !== 'null')) {
if ((attr === 'disabled') || (attr === 'checked') || (attr === 'selected') || (attr === 'textContent') || (attr === 'innerHTML'))
el[attr] = attrs[attr];
else
el.setAttribute(attr, attrs[attr]);
}
});
if (eventListener.length > 0) {
eventListener.forEach((obj) => {
Object.entries(obj).map(applyEventListeners.bind(el));
});
}
return el;
}
async function loadSettingsFromStorage() {
const defaultSettings = {
disableBelowZoom: 17,
enableTooltip: true,
hnLines: true,
hnNumbers: true,
keepHNLayerOnTop: true,
toggleHNNavPointsShortcut: '',
toggleHNNavPointsNumbersShortcut: '',
lastSaved: 0,
lastVersion: undefined
},
loadedSettings = JSON.parse(localStorage.getItem(_SETTINGS_STORE_NAME));
_settings = $extend(true, {}, defaultSettings, loadedSettings);
const serverSettings = await WazeWrap.Remote.RetrieveSettings(_SETTINGS_STORE_NAME);
if (serverSettings?.lastSaved > _settings.lastSaved)
_settings = $extend(true, _settings, serverSettings);
if (_settings.disableBelowZoom < 11)
_settings.disableBelowZoom += 12;
_timeouts.saveSettingsToStorage = window.setTimeout(saveSettingsToStorage, 5000);
return Promise.resolve();
}
function saveSettingsToStorage() {
checkTimeout({ timeout: 'saveSettingsToStorage' });
if (localStorage) {
['toggleHNNavPointsShortcut', 'toggleHNNavPointsNumbersShortcut'].forEach((k) => {
let keys = '';
const { shortcut } = W.accelerators.Actions[k];
if (shortcut) {
if (shortcut.altKey)
keys += 'A';
if (shortcut.shiftKey)
keys += 'S';
if (shortcut.ctrlKey)
keys += 'C';
if (keys !== '')
keys += '+';
if (shortcut.keyCode)
keys += shortcut.keyCode;
}
_settings[k] = keys;
});
_settings.lastVersion = _SCRIPT_VERSION;
_settings.lastSaved = Date.now();
localStorage.setItem(_SETTINGS_STORE_NAME, JSON.stringify(_settings));
WazeWrap.Remote.SaveSettings(_SETTINGS_STORE_NAME, _settings);
logDebug('Settings saved.');
}
}
function showScriptInfoAlert() {
if (_ALERT_UPDATE && (_SCRIPT_VERSION !== _settings.lastVersion)) {
const divElemRoot = createElem('div');
divElemRoot.appendChild(createElem('p', { textContent: 'What\'s New:' }));
const ulElem = createElem('ul');
if (_SCRIPT_VERSION_CHANGES.length > 0) {
for (let idx = 0, { length } = _SCRIPT_VERSION_CHANGES; idx < length; idx++)
ulElem.appendChild(createElem('li', { innerHTML: _SCRIPT_VERSION_CHANGES[idx] }));
}
else {
ulElem.appendChild(createElem('li', { textContent: 'Nothing major.' }));
}
divElemRoot.appendChild(ulElem);
WazeWrap.Interface.ShowScriptUpdate(_SCRIPT_SHORT_NAME, _SCRIPT_VERSION, divElemRoot.innerHTML, (_IS_BETA_VERSION ? dec(_BETA_DL_URL) : _PROD_DL_URL).replace(/code\/.*\.js/, ''), _FORUM_URL);
}
}
function checkTimeout(obj) {
if (obj.toIndex) {
if (_timeouts[obj.timeout]?.[obj.toIndex]) {
window.clearTimeout(_timeouts[obj.timeout][obj.toIndex]);
delete (_timeouts[obj.timeout][obj.toIndex]);
}
}
else {
if (_timeouts[obj.timeout])
window.clearTimeout(_timeouts[obj.timeout]);
_timeouts[obj.timeout] = undefined;
}
}
function doSpinner(spinnerName = '', spin = true) {
const btn = document.getElementById('hnNPSpinner');
if (!spin) {
_spinners[spinnerName] = false;
if (!Object.values(_spinners).some((a) => a === true)) {
if (btn) {
btn.classList.remove('fa-spin');
document.getElementById('divHnNPSpinner').style.display = 'none';
}
else {
const topBar = document.querySelector('#topbar-container .topbar'),
divElem = createElem('div', {
id: 'divHnNPSpinner', title: 'WME HN NavPoints is currently processing house numbers.', style: 'font-size:20px;background:white;float:left;display:none;'
});
divElem.appendChild(createElem('i', { id: 'hnNPSpinner', class: 'fa fa-spinner' }));
topBar.insertBefore(divElem, topBar.firstChild);
}
}
}
else {
_spinners[spinnerName] = true;
if (!btn) {
_spinners[spinnerName] = true;
const topBar = document.querySelector('#topbar-container .topbar'),
divElem = createElem('div', {
id: 'divHnNPSpinner', title: 'WME HN NavPoints is currently processing house numbers.', style: 'font-size:20px;background:white;float:left;'
});
divElem.appendChild(createElem('i', { id: 'hnNPSpinner', class: 'fa fa-spinner fa-spin' }));
topBar.insertBefore(divElem, topBar.firstChild);
}
else if (!btn.classList.contains('fa-spin')) {
btn.classList.add('fa-spin');
document.getElementById('divHnNPSpinner').style.display = '';
}
}
}
// eslint-disable-next-line default-param-last
function processSegmentsToRemove(force = false, segmentsArr) {
const segmentsToProcess = segmentsArr || _segmentsToRemove;
if (segmentsToProcess.length > 0) {
let linesToRemove = [],
hnsToRemove = [];
const filterMarkers = function (marker) { return marker?.segmentId === this; },
processFilterMarkers = (marker) => hnsToRemove.push(marker);
for (let i = segmentsToProcess.length - 1; i > -1; i--) {
const segId = segmentsToProcess[i];
if (!W.model.segments.getObjectById(segId) || force) {
segmentsToProcess.splice(i, 1);
linesToRemove = linesToRemove.concat(_HNNavPointsLayer.getFeaturesByAttribute('segmentId', segId));
if (!_settings.enableTooltip)
hnsToRemove = hnsToRemove.concat(_HNNavPointsNumbersLayer.getFeaturesByAttribute('segmentId', segId));
else
_HNNavPointsNumbersLayer.markers.filter(filterMarkers.bind(segId)).forEach(processFilterMarkers);
}
}
if (linesToRemove.length > 0)
_HNNavPointsLayer.removeFeatures(linesToRemove);
if (hnsToRemove.length > 0) {
if (!_settings.enableTooltip)
_HNNavPointsNumbersLayer.removeFeatures(hnsToRemove);
else
hnsToRemove.forEach((marker) => _HNNavPointsNumbersLayer.removeMarker(marker));
}
}
}
async function hnLayerToggled(checked) {
_HNNavPointsLayer.setVisibility(checked);
_settings.hnLines = checked;
saveSettingsToStorage();
if (checked) {
if (!_scriptActive)
await initBackgroundTasks('enable');
processSegs('hnLayerToggled', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')));
}
else if (!_settings.hnNumbers && _scriptActive) {
initBackgroundTasks('disable');
}
}
async function hnNumbersLayerToggled(checked) {
_HNNavPointsNumbersLayer.setVisibility(checked);
_settings.hnNumbers = checked;
saveSettingsToStorage();
if (checked) {
if (!_scriptActive)
await initBackgroundTasks('enable');
processSegs('hnNumbersLayerToggled', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')));
}
else if (!_settings.hnLines && _scriptActive) {
initBackgroundTasks('disable');
}
}
function observeHNLayer() {
if (W.editingMediator.get('editingHouseNumbers')) {
_segmentsToProcess = W.selectionManager.getSegmentSelection().segments.map((o) => o.getID());
_segmentsToRemove = [];
}
else {
W.model.segmentHouseNumbers.clear();
processSegmentsToRemove(true, [..._segmentsToProcess]);
processSegs('exithousenumbers', W.model.segments.getByIds([..._segmentsToProcess]), true);
_segmentsToProcess = [];
_segmentsToRemove = [];
_timeouts.checkMarkersEvents = {};
}
_saveButtonObserver.disconnect();
_saveButtonObserver.observe(document.getElementById('save-button'), {
childList: false, attributes: true, attributeOldValue: true, characterData: false, characterDataOldValue: false, subtree: false
});
}
function removeHNs(objArr) {
let linesToRemove = [],
hnsToRemove = [];
const filterMarkers = function (marker) { return marker?.featureId === this.attributes.id; },
processFilterMarkers = (marker) => {
hnsToRemove.push(marker);
};
objArr.forEach((hnObj) => {
linesToRemove = linesToRemove.concat(_HNNavPointsLayer.getFeaturesByAttribute('featureId', hnObj.getID()));
if (!_settings.enableTooltip)
hnsToRemove = hnsToRemove.concat(_HNNavPointsNumbersLayer.getFeaturesByAttribute('featureId', hnObj.getID()));
else
_HNNavPointsNumbersLayer.markers.filter(filterMarkers.bind(hnObj)).forEach(processFilterMarkers);
});
if (linesToRemove.length > 0)
_HNNavPointsLayer.removeFeatures(linesToRemove);
if (hnsToRemove.length > 0) {
if (!_settings.enableTooltip)
_HNNavPointsNumbersLayer.removeFeatures(hnsToRemove);
else
hnsToRemove.forEach((marker) => _HNNavPointsNumbersLayer.removeMarker(marker));
}
}
function drawHNs(houseNumberArr) {
if (houseNumberArr.length === 0)
return;
doSpinner('drawHNs', true);
let svg,
svgText,
hnsToRemove = [],
linesToRemove = [];
const lineFeatures = [],
numberFeatures = [],
invokeTooltip = _settings.enableTooltip ? (evt) => { showTooltip(evt); } : undefined,
mapFeatureId = (marker) => marker.featureId;
if (_settings.enableTooltip) {
svg = createElem('svg', { xlink: 'http://www.w3.org/1999/xlink', xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 40 14' });
svgText = createElem('svgText', { 'text-anchor': 'middle', x: '20', y: '10' });
}
for (let i = 0, { length } = houseNumberArr; i < length; i++) {
const hnObj = houseNumberArr[i],
segmentId = hnObj.getSegmentId();
if (W.model.segments.getObjectById(segmentId)) {
const featureId = hnObj.getID(),
markerIdx = _settings.enableTooltip ? _HNNavPointsNumbersLayer.markers.map(mapFeatureId).indexOf(featureId) : undefined,
// eslint-disable-next-line no-nested-ternary
hnToRemove = _settings.enableTooltip ? (markerIdx > -1) ? _HNNavPointsNumbersLayer.markers[markerIdx] : [] : _HNNavPointsNumbersLayer.getFeaturesByAttribute('featureId', featureId),
rtlChar = /[\u0590-\u083F]|[\u08A0-\u08FF]|[\uFB1D-\uFDFF]|[\uFE70-\uFEFF]/mg,
textDir = (hnObj.getNumber().match(rtlChar) !== null) ? 'rtl' : 'ltr';
linesToRemove = linesToRemove.concat(_HNNavPointsLayer.getFeaturesByAttribute('featureId', featureId));
if (hnToRemove.length > 0) {
if (!_settings.enableTooltip)
hnsToRemove = hnsToRemove.concat(_HNNavPointsNumbersLayer.getFeaturesByAttribute('featureId', featureId));
else
hnsToRemove.push(hnToRemove);
}
//Fix this mess once WME beta v2.188 is released to production.
const betaFractionPoint = (hnObj.getFractionPoint().coordinates)
? WazeWrap.Geometry.ConvertTo900913(hnObj.getFractionPoint().coordinates[0], hnObj.getFractionPoint().coordinates[1])
: undefined,
fractionX = betaFractionPoint ? betaFractionPoint.lon : hnObj.getFractionPoint().x,
fractionY = betaFractionPoint ? betaFractionPoint.lat : hnObj.getFractionPoint().y,
geometryX = hnObj.getOLGeometry ? hnObj.getOLGeometry().x : hnObj.getGeometry().x,
geometryY = hnObj.getOLGeometry ? hnObj.getOLGeometry().y : hnObj.getGeometry().y,
p1 = new OpenLayers.Geometry.Point(fractionX, fractionY),
p2 = new OpenLayers.Geometry.Point(geometryX, geometryY),
// eslint-disable-next-line no-nested-ternary
strokeColor = (hnObj.isForced()
? (!hnObj.getUpdatedBy()) ? 'red' : 'orange'
: (!hnObj.getUpdatedBy()) ? 'yellow' : 'white'
);
let lineString = new OpenLayers.Geometry.LineString([p1, p2]),
lineFeature = new OpenLayers.Feature.Vector(
lineString,
{ segmentId, featureId },
{
strokeWidth: 4, strokeColor: 'black', strokeOpacity: 0.5, strokeDashstyle: 'dash', strokeDashArray: '8, 8'
}
);
lineFeatures.push(lineFeature);
lineString = new OpenLayers.Geometry.LineString([p1, p2]);
lineFeature = new OpenLayers.Feature.Vector(
lineString,
{ segmentId, featureId },
{
strokeWidth: 2, strokeColor, strokeOpacity: 1, strokeDashstyle: 'dash', strokeDashArray: '8, 8'
}
);
lineFeatures.push(lineFeature);
if (_settings.enableTooltip) {
svg.setAttribute('style', `text-shadow:0 0 3px ${strokeColor},0 0 3px ${strokeColor},0 0 3px ${strokeColor},0 0 3px ${strokeColor},0 0 3px ${strokeColor},0 0 3px ${strokeColor};font-size:14px;font-weight:bold;font-family:"Open Sans", "Arial Unicode MS", "sans-serif";direction:${textDir}`);
svgText.textContent = hnObj.getNumber();
svg.replaceChildren(svgText);
const svgIcon = new WazeWrap.Require.Icon(`data:image/svg+xml,${svg.outerHTML}`, { w: 40, h: 18 }),
markerFeature = new OpenLayers.Marker(new OpenLayers.LonLat(p2.x, p2.y), svgIcon);
markerFeature.events.register('mouseover', null, invokeTooltip);
markerFeature.events.register('mouseout', null, hideTooltipDelay);
markerFeature.featureId = featureId;
markerFeature.segmentId = segmentId;
markerFeature.hnNumber = hnObj.getNumber() || '';
numberFeatures.push(markerFeature);
}
else {
// eslint-disable-next-line new-cap
numberFeatures.push(new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon.createRegularPolygon(p2, 1, 20), {
segmentId, featureId, hNumber: hnObj.getNumber(), strokeWidth: 3, Color: strokeColor, textDir
}));
}
}
}
if (linesToRemove.length > 0)
_HNNavPointsLayer.removeFeatures(linesToRemove);
if (hnsToRemove.length > 0) {
if (!_settings.enableTooltip)
_HNNavPointsNumbersLayer.removeFeatures(hnsToRemove);
else
hnsToRemove.forEach((marker) => _HNNavPointsNumbersLayer.removeMarker(marker));
}
if (lineFeatures.length > 0)
_HNNavPointsLayer.addFeatures(lineFeatures);
if (numberFeatures.length > 0) {
if (!_settings.enableTooltip)
_HNNavPointsNumbersLayer.addFeatures(numberFeatures);
else
numberFeatures.forEach((marker) => _HNNavPointsNumbersLayer.addMarker(marker));
}
doSpinner('drawHNs', false);
}
function destroyAllHNs() {
doSpinner('destroyAllHNs', true);
_HNNavPointsLayer.destroyFeatures();
if (_settings.enableTooltip)
_HNNavPointsNumbersLayer.clearMarkers();
else
_HNNavPointsNumbersLayer.destroyFeatures();
_processedSegments = [];
doSpinner('destroyAllHNs', false);
Promise.resolve();
}
function getOLMapExtent() {
let extent = W.map.getExtent();
if (Array.isArray(extent)) {
extent = new OpenLayers.Bounds(extent);
extent.transform('EPSG:4326', 'EPSG:3857');
}
return extent;
}
function processSegs(action, arrSegObjs, processAll = false, retry = 0) {
/* As of 2020.06.08 (sometime before this date) updatedOn does not get updated when updating house numbers. Looking for a new
* way to track which segments have been updated most recently to prevent a total refresh of HNs after an event.
* Changed to using a global to keep track of segmentIds touched during HN edit mode.
*/
if ((action === 'settingChanged') && (W.map.getOLMap().getZoom() < _settings.disableBelowZoom)) {
destroyAllHNs();
return;
}
if (!arrSegObjs || (arrSegObjs.length === 0) || (W.map.getOLMap().getZoom() < _settings.disableBelowZoom) || preventProcess())
return;
doSpinner('processSegs', true);
const eg = getOLMapExtent().toGeometry(),
findObjIndex = (array, fldName, value) => array.map((a) => a[fldName]).indexOf(value),
processError = (err, chunk) => {
logDebug(`Retry: ${retry}`);
if (retry < 5)
processSegs(action, chunk, true, ++retry);
else
logError(`Get HNs for ${chunk.length} segments failed. Code: ${err.status} - Text: ${err.responseText}`);
},
processJSON = (jsonData) => {
if ((jsonData?.error === undefined) && (typeof jsonData?.segmentHouseNumbers?.objects !== 'undefined'))
drawHNs(jsonData.segmentHouseNumbers.objects);
},
mapHouseNumbers = (segObj) => segObj.getID(),
invokeProcessError = function (err) { return processError(err, this); };
if ((action === 'objectsremoved')) {
if (arrSegObjs?.length > 0) {
const removedSegIds = [];
let hnNavPointsToRemove = [],
hnNavPointsNumbersToRemove = [];
arrSegObjs.forEach((segObj) => {
const segmentId = segObj.getID();
if (!eg.intersects(segObj.getAttribute('geometry')) && (segmentId > 0)) {
hnNavPointsToRemove = hnNavPointsToRemove.concat(_HNNavPointsLayer.getFeaturesByAttribute('segmentId', segmentId));
if (!_settings.enableTooltip)
hnNavPointsNumbersToRemove = hnNavPointsNumbersToRemove.concat(_HNNavPointsNumbersLayer.getFeaturesByAttribute('segmentId', segmentId));
else
removedSegIds.push(segmentId);
const segIdx = findObjIndex(_processedSegments, 'segId', segmentId);
if (segIdx > -1)
_processedSegments.splice(segIdx, 1);
}
});
if (hnNavPointsToRemove.length > 0)
_HNNavPointsLayer.removeFeatures(hnNavPointsToRemove);
if (hnNavPointsNumbersToRemove.length > 0)
_HNNavPointsNumbersLayer.removeFeatures(hnNavPointsNumbersToRemove);
if (removedSegIds.length > 0) {
_HNNavPointsNumbersLayer.markers.filter((marker) => removedSegIds.includes(marker.segmentId)).forEach((marker) => {
_HNNavPointsNumbersLayer.removeMarker(marker);
});
}
}
}
else { // action = 'objectsadded', 'zoomend', 'init', 'exithousenumbers', 'hnLayerToggled', 'hnNumbersLayerToggled', 'settingChanged', 'afterSave', 'afterclearactions'
let i = arrSegObjs.length;
while (i--) {
if (arrSegObjs[i].getID() < 0) {
arrSegObjs.splice(i, 1);
}
else {
const segIdx = findObjIndex(_processedSegments, 'segId', arrSegObjs[i].getID());
if (segIdx > -1) {
if (arrSegObjs[i].getUpdatedOn() > _processedSegments[segIdx].updatedOn)
_processedSegments[segIdx].updatedOn = arrSegObjs[i].getUpdatedOn();
else if (!processAll)
arrSegObjs.splice(i, 1);
}
else {
_processedSegments.push({ segId: arrSegObjs[i].getID(), updatedOn: arrSegObjs[i].getUpdatedOn() });
}
}
}
while (arrSegObjs.length > 0) {
let chunk;
if (retry === 1)
chunk = arrSegObjs.splice(0, 250);
else if (retry === 2)
chunk = arrSegObjs.splice(0, 125);
else if (retry === 3)
chunk = arrSegObjs.splice(0, 100);
else if (retry === 4)
chunk = arrSegObjs.splice(0, 50);
else
chunk = arrSegObjs.splice(0, 500);
try {
W.controller.descartesClient.getHouseNumbers(chunk.map(mapHouseNumbers)).then(processJSON).catch(invokeProcessError.bind(chunk));
}
catch (error) {
processError(error, [...chunk]);
}
}
}
doSpinner('processSegs', false);
}
function preventProcess() {
if (!_settings.hnLines && !_settings.hnNumbers) {
if (_scriptActive)
initBackgroundTasks('disable');
destroyAllHNs();
return true;
}
if (W.map.getOLMap().getZoom() < _settings.disableBelowZoom) {
destroyAllHNs();
return true;
}
return false;
}
function segmentsEvent(evt) {
if (!evt || preventProcess())
return;
if ((this.action === 'objectssynced') || (this.action === 'objectsremoved'))
processSegmentsToRemove();
if (this.action === 'objectschanged-id') {
const oldSegmentId = evt.oldID,
newSegmentID = evt.newID;
_HNNavPointsLayer.getFeaturesByAttribute('segmentId', oldSegmentId).forEach((feature) => { feature.attributes.segmentId = newSegmentID; });
if (_settings.enableTooltip)
_HNNavPointsNumbersLayer.markers.filter((marker) => marker.segmentId === oldSegmentId).forEach((marker) => { marker.segmentId = newSegmentID; });
else
_HNNavPointsNumbersLayer.getFeaturesByAttribute('segmentId', oldSegmentId).forEach((feature) => { feature.attributes.segmentId = newSegmentID; });
}
else if (this.action === 'objects-state-deleted') {
evt.forEach((obj) => {
if (!_segmentsToRemove.includes(obj.getID()))
_segmentsToRemove.push(obj.getID());
});
}
else {
processSegs(this.action, evt.filter((o) => o.getAttribute('hasHNs')));
}
}
function objectsChangedIdHNs(evt) {
if (!evt || preventProcess())
return;
const oldFeatureId = evt.oldID,
newFeatureId = evt.newID;
_HNNavPointsLayer.getFeaturesByAttribute('featureId', oldFeatureId).forEach((feature) => { feature.attributes.featureId = newFeatureId; });
if (_settings.enableTooltip)
_HNNavPointsNumbersLayer.markers.filter((marker) => marker.featureId === oldFeatureId).forEach((marker) => { marker.featureId = newFeatureId; });
else
_HNNavPointsNumbersLayer.getFeaturesByAttribute('featureId', oldFeatureId).forEach((feature) => { feature.attributes.featureId = newFeatureId; });
}
function objectsChangedHNs(evt) {
if (!evt || preventProcess())
return;
if ((evt.length === 1) && evt[0].getSegmentId() && !_segmentsToProcess.includes(evt[0].getSegmentId()))
_segmentsToProcess.push(evt[0].getSegmentId());
}
function objectsStateDeletedHNs(evt) {
if (!evt || preventProcess())
return;
if ((evt.length === 1) && evt[0].getSegmentId() && !_segmentsToProcess.includes(evt[0].getSegmentId()))
_segmentsToProcess.push(evt[0].getSegmentId());
removeHNs(evt);
}
function objectsAddedHNs(evt) {
if (!evt || preventProcess())
return;
if ((evt.length === 1) && evt[0].getSegmentId() && !_segmentsToProcess.includes(evt[0].getSegmentId()))
_segmentsToProcess.push(evt[0].getSegmentId());
}
function zoomEndEvent() {
if (preventProcess())
return;
if ((W.map.getOLMap().getZoom() < _settings.disableBelowZoom))
destroyAllHNs();
if ((W.map.getOLMap().getZoom() > (_settings.disableBelowZoom - 1)) && (_processedSegments.length === 0))
processSegs('zoomend', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')), true);
}
function afterActionsEvent(evt) {
if (!evt || preventProcess())
return;
if ((evt.type === 'afterclearactions') || (evt.type === 'noActions')) {
processSegmentsToRemove(true, [..._segmentsToProcess]);
processSegs('afterclearactions', W.model.segments.getByIds([..._segmentsToProcess]), true);
}
else if (evt.action?._description?.includes('Deleted house number')) {
if (evt.type === 'afterundoaction')
drawHNs([evt.action.object]);
else
removeHNs([evt.action.object]);
}
else if (evt.action?._description?.includes('Updated house number')) {
const tempEvt = _.cloneDeep(evt);
if (evt.type === 'afterundoaction') {
if (tempEvt.action.newAttributes?.number)
tempEvt.action.attributes.number = tempEvt.action.newAttributes.number;
}
else if (evt.type === 'afteraction') {
if (tempEvt.action.oldAttributes?.number)
tempEvt.action.attributes.number = tempEvt.action.oldAttributes.number;
}
removeHNs([tempEvt.action.object]);
drawHNs([evt.action.object]);
}
else if (evt.action?._description?.includes('Added house number')) {
if (evt.type === 'afterundoaction')
removeHNs([evt.action.houseNumber]);
else
drawHNs([evt.action.houseNumber]);
}
else if (evt.action?._description?.includes('Moved house number')) {
drawHNs([evt.action.newHouseNumber]);
}
else if (evt.action?.houseNumber) {
drawHNs((evt.action.newHouseNumber ? [evt.action.newHouseNumber] : [evt.action.houseNumber]));
}
}
async function reloadClicked() {
if (preventProcess() || document.querySelector('wz-button.overlay-button.reload-button').classList.contains('disabled'))
return;
await destroyAllHNs();
processSegs('reload', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')));
}
function initBackgroundTasks(status) {
if (status === 'enable') {
_saveButtonObserver = new MutationObserver((mutationsList) => {
if ((W.model.actionManager._redoStack.length === 0)
&& mutationsList.some((mutation) => ((mutation.attributeName === 'disabled')
&& (mutation.oldValue === 'true')
&& (mutation.target.disabled === true)))
) {
if (W.editingMediator.get('editingHouseNumbers'))
processSegs('afterSave', W.model.segments.getByIds([..._segmentsToProcess]), true);
else
processSegmentsToRemove();
}
});
_saveButtonObserver.observe(document.getElementById('save-button'), {
childList: false, attributes: true, attributeOldValue: true, characterData: false, characterDataOldValue: false, subtree: false
});
_saveButtonObserver.observing = true;
W.accelerators.events.on({ reloadData: destroyAllHNs });
document.querySelector('wz-button.overlay-button.reload-button').addEventListener('click', reloadClicked);
W.model.segments.on('objectsadded', segmentsEvent, { action: 'objectsadded' });
W.model.segments.on('objectsremoved', segmentsEvent, { action: 'objectsremoved' });
W.model.segments.on('objectssynced', segmentsEvent, { action: 'objectssynced' });
W.model.segments.on('objects-state-deleted', segmentsEvent, { action: 'objects-state-deleted' });
W.model.segments.on('objectschanged-id', segmentsEvent, { action: 'objectschanged-id' });
W.model.segmentHouseNumbers.on({
objectsadded: objectsAddedHNs,
objectschanged: objectsChangedHNs,
'objectschanged-id': objectsChangedIdHNs,
'objects-state-deleted': objectsStateDeletedHNs
});
W.editingMediator.on({ 'change:editingHouseNumbers': observeHNLayer });
W.map.events.on({
zoomend: zoomEndEvent, addlayer: checkLayerIndex, removelayer: checkLayerIndex
});
WazeWrap.Events.register('afterundoaction', this, afterActionsEvent);
WazeWrap.Events.register('afteraction', this, afterActionsEvent);
WazeWrap.Events.register('afterclearactions', this, afterActionsEvent);
_scriptActive = true;
}
else if (status === 'disable') {
_saveButtonObserver = undefined;
W.accelerators.events.on('reloadData', null, destroyAllHNs);
document.querySelector('wz-button.overlay-button.reload-button').removeEventListener('click', reloadClicked);
W.model.segments.off('objectsadded', segmentsEvent, { action: 'objectsadded' });
W.model.segments.off('objectsremoved', segmentsEvent, { action: 'objectsremoved' });
W.model.segments.off('objectschanged', segmentsEvent, { action: 'objectschanged' });
W.model.segments.off('objects-state-deleted', segmentsEvent, { action: 'objects-state-deleted' });
W.model.segments.off('objectschanged-id', segmentsEvent, { action: 'objectschanged-id' });
W.model.segmentHouseNumbers.off({
objectsadded: objectsAddedHNs,
objectschanged: objectsChangedHNs,
'objectschanged-id': objectsChangedIdHNs,
'objects-state-deleted': objectsStateDeletedHNs,
objectsremoved: removeHNs
});
W.editingMediator.off({ 'change:editingHouseNumbers': observeHNLayer });
W.map.events.unregister('zoomend', null, zoomEndEvent);
W.map.events.unregister('addlayer', null, checkLayerIndex);
W.map.events.unregister('removelayer', null, checkLayerIndex);
WazeWrap.Events.unregister('afterundoaction', this, afterActionsEvent);
WazeWrap.Events.unregister('afteraction', this, afterActionsEvent);
_scriptActive = false;
}
return Promise.resolve();
}
function enterHNEditMode(segment, moveMap) {
if (segment) {
if (moveMap)
W.map.setCenter({ lon: segment.getCenter().x, lat: segment.getCenter().y }, W.map.getOLMap().getZoom());
W.selectionManager.setSelectedModels(segment);
document.querySelector('#segment-edit-general .edit-house-numbers').dispatchEvent(new MouseEvent('click', { bubbles: true }));
}
}
function showTooltip(evt) {
if ((W.map.getOLMap().getZoom() < 16) || W.editingMediator.get('editingHouseNumbers') || !_settings.enableTooltip)
return;
if (evt?.object?.featureId) {
checkTooltip();
let moveMap = false;
const { segmentId, hnNumber } = evt.object;
if (_popup.inUse && (_popup.hnNumber === hnNumber) && (_popup.segmentId === segmentId))
return;
const segment = W.model.segments.getObjectById(segmentId),
street = W.model.streets.getObjectById(segment.getPrimaryStreetID()),
popupPixel = W.map.getPixelFromLonLat(evt.object.lonlat),
divElemRoot = createElem('div', {
id: 'hnNavPointsTooltipDiv-tooltip',
class: 'tippy-box',
'data-state': 'hidden',
tabindex: '-1',
'data-theme': 'light-border',
'data-animation': 'fade',
role: 'tooltip',
'data-placement': 'top',
style: 'max-width: 350px; transition-duration:300ms;'
}),
invokeEnterHNEditMode = () => enterHNEditMode(segment, moveMap),
divElemRootDivDiv = createElem('div', { class: 'house-number-marker-tooltip' });
divElemRootDivDiv.appendChild(createElem('div', { class: 'title', dir: 'auto', textContent: `${hnNumber} ${(street ? street.getName() : '')}` }));
divElemRootDivDiv.appendChild(createElem('div', {
id: 'hnNavPointsTooltipDiv-edit', class: 'edit-button fa fa-pencil', style: segment.canEditHouseNumbers() ? '' : 'display:none;'
}, [{ click: invokeEnterHNEditMode }]));
const divElemRootDiv = createElem('div', {
id: 'hnNavPointsTooltipDiv-content', class: 'tippy-content', 'data-state': 'hidden', style: 'transition-duration: 300ms;'
});
divElemRootDiv.appendChild(divElemRootDivDiv);
divElemRoot.appendChild(divElemRootDiv);
divElemRoot.appendChild(createElem('div', {
id: 'hnNavPointsTooltipDiv-arrow', class: 'tippy-arrow', style: 'position: absolute; left: 0px;'
}));
_hnNavPointsTooltipDiv.replaceChildren(divElemRoot);
popupPixel.origX = popupPixel.x;
const popupWidthHalf = (_hnNavPointsTooltipDiv.clientWidth / 2);
let arrowOffset = (popupWidthHalf - 15),
dataPlacement = 'top';
popupPixel.x = ((popupPixel.x - popupWidthHalf + 5) > 0) ? (popupPixel.x - popupWidthHalf + 5) : 10;
if (popupPixel.x === 10)
arrowOffset = popupPixel.origX - 22;
if ((popupPixel.x + (popupWidthHalf * 2)) > W.map.getEl()[0].clientWidth) {
popupPixel.x = (popupPixel.origX - _hnNavPointsTooltipDiv.clientWidth + 8);
arrowOffset = (_hnNavPointsTooltipDiv.clientWidth - 30);
moveMap = true;
}
if (popupPixel.y - [..._hnNavPointsTooltipDiv.children].reduce((height, elem) => height + elem.getBoundingClientRect().height, 0) < 0) {
popupPixel.y += 14;
dataPlacement = 'bottom';
}
else {
popupPixel.y -= ([..._hnNavPointsTooltipDiv.children].reduce((height, elem) => height + elem.getBoundingClientRect().height, 0) + 14);
}
_hnNavPointsTooltipDiv.style.transform = `translate(${Math.round(popupPixel.x)}px, ${Math.round(popupPixel.y)}px)`;
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-arrow').style.transform = `translate(${Math.max(0, Math.round(arrowOffset))}px, 0px)`;
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-tooltip').setAttribute('data-placement', dataPlacement);
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-tooltip').setAttribute('data-state', 'visible');
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-content').setAttribute('data-state', 'visible');
_popup = { segmentId, hNumber: hnNumber, inUse: true };
}
}
function stripTooltipHTML() {
checkTimeout({ timeout: 'stripTooltipHTML' });
_hnNavPointsTooltipDiv.replaceChildren();
_popup = { segmentId: -1, hnNumber: -1, inUse: false };
}
function hideTooltip() {
checkTimeout({ timeout: 'hideTooltip' });
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-content')?.setAttribute('data-state', 'hidden');
_hnNavPointsTooltipDiv.querySelector('#hnNavPointsTooltipDiv-tooltip')?.setAttribute('data-state', 'hidden');
_timeouts.stripTooltipHTML = window.setTimeout(stripTooltipHTML, 400);
}
function hideTooltipDelay(evt) {
if (!evt)
return;
checkTimeout({ timeout: 'hideTooltip' });
const parentsArr = evt.toElement?.offsetParent ? [evt.toElement.offsetParent, evt.toElement.offsetParent.offSetParent] : [];
if (evt.toElement && (parentsArr.includes(_HNNavPointsNumbersLayer?.div) || parentsArr.includes(_hnNavPointsTooltipDiv)))
return;
_timeouts.hideTooltip = window.setTimeout(hideTooltip, 100, evt);
}
function checkTooltip() {
checkTimeout({ timeout: 'hideTooltip' });
}
function checkLayerIndex() {
const layerIdx = W.map.layers.map((a) => a.uniqueName).indexOf('__HNNavPointsNumbersLayer');
let properIdx;
if (_settings.keepHNLayerOnTop) {
const layersIndexes = [],
layersLoaded = W.map.layers.map((a) => a.uniqueName);
['wmeGISLayersDefault', '__HNNavPointsLayer'].forEach((layerUniqueName) => {
if (layersLoaded.indexOf(layerUniqueName) > 0)
layersIndexes.push(layersLoaded.indexOf(layerUniqueName));
});
properIdx = (Math.max(...layersIndexes) + 1);
}
else {
properIdx = (W.map.layers.map((a) => a.uniqueName).indexOf('__HNNavPointsLayer') + 1);
}
if (layerIdx !== properIdx) {
W.map.layers.splice(properIdx, 0, W.map.layers.splice(layerIdx, 1)[0]);
W.map.getOLMap().resetLayersZIndex();
}
}
function checkHnNavpointsVersion() {
if (_IS_ALPHA_VERSION)
return;
let updateMonitor;
try {
updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(_SCRIPT_LONG_NAME, _SCRIPT_VERSION, (_IS_BETA_VERSION ? dec(_BETA_DL_URL) : _PROD_DL_URL), GM_xmlhttpRequest);
updateMonitor.start();
}
catch (err) {
logError('Upgrade version check:', err);
}
}
async function onWazeWrapReady() {
log('Initializing.');
checkHnNavpointsVersion();
const navPointsNumbersLayersOptions = {
displayInLayerSwitcher: true,
uniqueName: '__HNNavPointsNumbersLayer',
selectable: true,
labelSelect: true,
rendererOptions: { zIndexing: true },
styleMap: new OpenLayers.StyleMap({
default: new OpenLayers.Style({
strokeColor: '${Color}',
strokeOpacity: 1,
strokeWidth: 3,
fillColor: '${Color}',
fillOpacity: 0.5,
pointerEvents: 'visiblePainted',
label: '${hNumber}',
fontSize: '12px',
fontFamily: 'Rubik, Boing-light, sans-serif;',
fontWeight: 'bold',
direction: '${textDir}',
labelOutlineColor: '${Color}',
labelOutlineWidth: 3,
labelSelect: true
})
})
},
handleCheckboxToggle = function () {
const settingName = this.id.substring(14);
if (settingName === 'enableTooltip') {
if (!this.checked)
_HNNavPointsNumbersLayer.clearMarkers();
else
_HNNavPointsNumbersLayer.destroyFeatures();
W.map.removeLayer(_HNNavPointsNumbersLayer);
if (this.checked)
_HNNavPointsNumbersLayer = new OpenLayers.Layer.Markers('HN NavPoints Numbers Layer', navPointsNumbersLayersOptions);
else
_HNNavPointsNumbersLayer = new OpenLayers.Layer.Vector('HN NavPoints Numbers Layer', navPointsNumbersLayersOptions);
W.map.addLayer(_HNNavPointsNumbersLayer);
_HNNavPointsNumbersLayer.setVisibility(_settings.hnNumbers);
}
_settings[settingName] = this.checked;
if (settingName === 'keepHNLayerOnTop')
checkLayerIndex();
saveSettingsToStorage();
if ((settingName === 'enableTooltip') && (W.map.getOLMap().getZoom() > (_settings.disableBelowZoom - 1)) && (_settings.hnLines || _settings.hnNumbers))
processSegs('settingChanged', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')), true, 0);
},
handleTextboxChange = function () {
const newVal = Math.min(22, Math.max(16, +this.value));
if ((newVal !== _settings.disableBelowZoom) || (+this.value !== newVal)) {
if (newVal !== +this.value)
this.value = newVal;
_settings.disableBelowZoom = newVal;
saveSettingsToStorage();
if ((W.map.getOLMap().getZoom() < newVal) && (_settings.hnLines || _settings.hnNumbers))
processSegs('settingChanged', null, true, 0);
else if (_settings.hnLines || _settings.hnNumbers)
processSegs('settingChanged', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')), true, 0);
}
},
buildCheckbox = (id = '', textContent = '', checked = true, title = '', disabled = false) => createElem('wz-checkbox', {
id, title, disabled, checked, textContent
}, [{ change: handleCheckboxToggle }]),
buildTextBox = (id = '', label = '', value = '', placeholder = '', maxlength = 0, autocomplete = 'off', title = '', disabled = false) => createElem('wz-text-input', {
id, label, value, placeholder, maxlength, autocomplete, title, disabled
}, [{ change: handleTextboxChange }]),
toggleHNNavPoints = () => document.getElementById('layer-switcher-item_hn_navpoints').dispatchEvent(new MouseEvent('click', { bubbles: true })),
toggleHNNavPointsNumbers = () => document.getElementById('layer-switcher-item_hn_navpoints_numbers').dispatchEvent(new MouseEvent('click', { bubbles: true }));
await loadSettingsFromStorage();
WazeWrap.Interface.AddLayerCheckbox('display', 'HN NavPoints', _settings.hnLines, hnLayerToggled);
WazeWrap.Interface.AddLayerCheckbox('display', 'HN NavPoints Numbers', _settings.hnNumbers, hnNumbersLayerToggled);
_HNNavPointsLayer = new OpenLayers.Layer.Vector('HN NavPoints Layer', {
displayInLayerSwitcher: true,
uniqueName: '__HNNavPointsLayer'
});
_HNNavPointsNumbersLayer = _settings.enableTooltip
? new OpenLayers.Layer.Markers('HN NavPoints Numbers Layer', navPointsNumbersLayersOptions)
: new OpenLayers.Layer.Vector('HN NavPoints Numbers Layer', navPointsNumbersLayersOptions);
W.map.addLayers([_HNNavPointsLayer, _HNNavPointsNumbersLayer]);
_HNNavPointsLayer.setVisibility(_settings.hnLines);
_HNNavPointsNumbersLayer.setVisibility(_settings.hnNumbers);
window.addEventListener('beforeunload', saveSettingsToStorage, false);
new WazeWrap.Interface.Shortcut(
'toggleHNNavPointsShortcut',
'Toggle HN NavPoints layer',
'layers',
'layersToggleHNNavPoints',
_settings.toggleHNNavPointsShortcut,
toggleHNNavPoints,
null
).add();
new WazeWrap.Interface.Shortcut(
'toggleHNNavPointsNumbersShortcut',
'Toggle HN NavPoints Numbers layer',
'layers',
'layersToggleHNNavPointsNumbers',
_settings.toggleHNNavPointsNumbersShortcut,
toggleHNNavPointsNumbers,
null
).add();
const { tabLabel, tabPane } = W.userscripts.registerSidebarTab('HN-NavPoints');
tabLabel.appendChild(createElem('i', { class: 'w-icon w-icon-location', style: 'font-size:15px;padding-top:4px;' }));
tabLabel.title = _SCRIPT_SHORT_NAME;
const docFrags = document.createDocumentFragment();
docFrags.appendChild(createElem('h4', { style: 'font-weight:bold;', textContent: _SCRIPT_LONG_NAME }));
docFrags.appendChild(createElem('h6', { style: 'margin-top:0px;', textContent: _SCRIPT_VERSION }));
let divElemRoot = createElem('div', { class: 'form-group' });
divElemRoot.appendChild(buildTextBox(
'HNNavPoints_disableBelowZoom',
'Disable when zoom level is (<) less than:',
_settings.disableBelowZoom,
'',
2,
'off',
'Disable NavPoints and house numbers when zoom level is less than specified number.\r\nMinimum: 16\r\nDefault: 17',
false
));
divElemRoot.appendChild(buildCheckbox(
'HNNavPoints_cbenableTooltip',
'Enable tooltip',
_settings.enableTooltip,
'Enable tooltip when mousing over house numbers.\r\nWarning: This may cause performance issues.',
false
));
divElemRoot.appendChild(buildCheckbox('HNNavPoints_cbkeepHNLayerOnTop', 'Keep HN layer on top', _settings.keepHNLayerOnTop, 'Keep house numbers layer on top of all other layers.', false));
const formElem = createElem('form', { class: 'attributes-form side-panel-section' });
formElem.appendChild(divElemRoot);
docFrags.appendChild(formElem);
docFrags.appendChild(createElem('label', { class: 'control-label', textContent: 'Color legend' }));
divElemRoot = createElem('div', { style: 'margin:0 10px 0 10px; width:130px; text-align:center; font-size:12px; background:black; font-weight:600;' });
divElemRoot.appendChild(createElem('div', {
style: 'text-shadow:0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white,0 0 3px white;', textContent: 'Touched'
}));
divElemRoot.appendChild(createElem('div', {
style: 'text-shadow:0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange,0 0 3px orange;',
textContent: 'Touched forced'
}));
divElemRoot.appendChild(createElem('div', {
style: 'text-shadow:0 0 3px yellow,0 0 3px yellow,0 0 3px yellow, 0 0 3px yellow,0 0 3px yellow,0 0 3px yellow,0 0 3px yellow,0 0 3px yellow,0 0 3px yellow,0 0 3px yellow;',
textContent: 'Untouched'
}));
divElemRoot.appendChild(createElem('div', {
style: 'text-shadow:0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red,0 0 3px red;', textContent: 'Untouched forced'
}));
docFrags.appendChild(divElemRoot);
tabPane.appendChild(docFrags);
tabPane.id = 'sidepanel-hn-navpoints';
await W.userscripts.waitForElementConnected(tabPane);
if (!_hnNavPointsTooltipDiv) {
_hnNavPointsTooltipDiv = createElem('div', {
id: 'hnNavPointsTooltipDiv',
style: 'z-index:9999; visibility:visible; position:absolute; inset: auto auto 0px 0px; margin: 0px; top: 0px; left: 0px;',
'data-tippy-root': false
}, [{ mouseenter: checkTooltip }, { mouseleave: hideTooltipDelay }]);
W.map.getEl()[0].appendChild(_hnNavPointsTooltipDiv);
}
await initBackgroundTasks('enable');
checkLayerIndex();
log(`Fully initialized in ${Math.round(performance.now() - _LOAD_BEGIN_TIME)} ms.`);
showScriptInfoAlert();
if (_scriptActive)
processSegs('init', W.model.segments.getObjectArray().filter((o) => o.getAttribute('hasHNs')));
setTimeout(saveSettingsToStorage, 10000);
}
function onWmeReady(tries = 1) {
if (typeof tries === 'object')
tries = 1;
checkTimeout({ timeout: 'onWmeReady' });
if (WazeWrap?.Ready) {
logDebug('WazeWrap is ready. Proceeding with initialization.');
onWazeWrapReady();
}
else if (tries < 1000) {
logDebug(`WazeWrap is not in Ready state. Retrying ${tries} of 1000.`);
_timeouts.onWmeReady = window.setTimeout(onWmeReady, 200, ++tries);
}
else {
logError(new Error('onWmeReady timed out waiting for WazeWrap Ready state.'));
}
}
function onWmeInitialized() {
if (W.userscripts?.state?.isReady) {
logDebug('W is ready and already in "wme-ready" state. Proceeding with initialization.');
onWmeReady(1);
}
else {
logDebug('W is ready, but state is not "wme-ready". Adding event listener.');
document.addEventListener('wme-ready', onWmeReady, { once: true });
}
}
function bootstrap() {
if (!W) {
logDebug('W is not available. Adding event listener.');
document.addEventListener('wme-initialized', onWmeInitialized, { once: true });
}
else {
onWmeInitialized();
}
}
bootstrap();
}
)();