// ==UserScript==
// @name WME Road Name Helper NP
// @description Check suffix and common word abbreviations without leaving WME
// @version 2025.06.18.02
// @author Kid4rm90s
// @license MIT
// @match *://*.waze.com/*editor*
// @exclude *://*.waze.com/user/editor*
// @connect greasyfork.org
// @grant GM_xmlhttpRequest
// @namespace https://greasyfork.org/users/1087400
// @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// ==/UserScript==
(function () {
'use strict';
const updateMessage = `
- Restored AH02 to AH2.
`;
const SCRIPT_VERSION = GM_info.script.version.toString();
const SCRIPT_NAME = GM_info.script.name;
const DOWNLOAD_URL = GM_info.script.downloadURL;
const GreasyFork_URL = 'https://greasyfork.org/en/scripts/538171-wme-road-name-helper-np';
const forumURL = 'https://greasyfork.org/en/scripts/538171-wme-road-name-helper-np/feedback';
let sdk;
// Suffix Abbreviation Data (Abbreviation: FullWord)
// This is for suffixes that have standard abbreviations
const wmessa_approvedAbbr = {
Ally: 'Alley',
App: 'Approach',
Arc: 'Arcade',
Av: 'Avenue',
Bwlk: 'Boardwalk',
Bvd: 'Boulevard',
Brk: 'Break',
Bypa: 'Bypass',
Ch: 'Chase',
Cct: 'Circuit',
Cl: 'Close',
Con: 'Concourse',
Ct: 'Court',
Cr: 'Crescent',
Crst: 'Crest',
Dr: 'Drive',
Ent: 'Entrance',
Esp: 'Esplanade',
Exp: 'Expressway',
Ftrl: 'Firetrail',
Fwy: 'Freeway',
Glde: 'Glade',
Gra: 'Grange',
Gr: 'Grove',
Hwy: 'Highway',
Mwy: 'Motorway',
Pde: 'Parade',
Pwy: 'Parkway',
Psge: 'Passage',
Pl: 'Place',
Plza: 'Plaza',
Prom: 'Promenade',
Qys: 'Quays',
Rtt: 'Retreat',
Rdge: 'Ridge',
Rd: 'Road',
Sq: 'Square',
Stps: 'Steps',
St: 'Street',
Sbwy: 'Subway',
Tce: 'Terrace',
Trk: 'Track',
Trl: 'Trail',
Vsta: 'Vista',
};
// Suffix Suggestion Data (UserTyped/FullWord: CorrectAbbreviation)
// This is for suffixes that have specific suggestions
const wmessa_suggestedAbbr = {
Alley: 'Ally',
Approach: 'App',
Arcade: 'Arc',
Avenue: 'Av',
Boardwalk: 'Bwlk',
Boulevard: 'Bvd',
Blvd: 'Bvd',
Break: 'Brk',
Bypass: 'Bypa',
Chase: 'Ch',
Circuit: 'Cct',
Close: 'Cl',
Concourse: 'Con',
Court: 'Ct',
Crescent: 'Cr',
Crest: 'Crst',
Drive: 'Dr',
Entrance: 'Ent',
Esplanade: 'Esp',
Expressway: 'Exp',
Firetrail: 'Ftrl',
Freeway: 'Fwy',
Glade: 'Glde',
Grange: 'Gra',
Grove: 'Gr',
Highway: 'Hwy',
Ln: 'Lane',
Marg: 'Marga',
Motorway: 'Mwy',
Parade: 'Pde',
Parkway: 'Pwy',
Passage: 'Psge',
Place: 'Pl',
Plaza: 'Plza',
Promenade: 'Prom',
Quays: 'Qys',
Retreat: 'Rtt',
Ridge: 'Rdge',
Road: 'Rd',
Square: 'Sq',
Steps: 'Stps',
Street: 'St',
Subway: 'Sbwy',
Terrace: 'Tce',
Track: 'Trk',
Trail: 'Trl',
Vista: 'Vsta',
रा१: 'रारा०१',
रा२: 'रारा०२',
रा३: 'रारा०३',
रा४: 'रारा०४',
रा५: 'रारा०५',
रा६: 'रारा०६',
रा७: 'रारा०७',
रा८: 'रारा०८',
रा९: 'रारा०९',
AH02: 'AH2',
};
// Suffixes that should be preserved in title case (case-insensitive)
// These words will not be converted to lowercase in title case
// This is useful for words that are proper nouns or have specific casing requirements.
const wmessa_preserveCaseWords = [
'NH01',
'NH02',
'NH03',
'NH04',
'NH05',
'NH06',
'NH07',
'NH08',
'NH09',
'NH10',
'NH11',
'NH12',
'NH13',
'NH14',
'NH15',
'NH16',
'NH17',
'NH18',
'NH19',
'NH20',
'NH21',
'NH22',
'NH23',
'NH24',
'NH25',
'NH26',
'NH27',
'NH28',
'NH29',
'NH30',
'NH31',
'NH32',
'NH33',
'NH34',
'NH35',
'NH36',
'NH37',
'NH38',
'NH39',
'NH40',
'NH41',
'NH42',
'NH43',
'NH44',
'NH45',
'NH46',
'NH47',
'NH48',
'NH49',
'NH50',
'NH51',
'NH52',
'NH53',
'NH54',
'NH55',
'NH56',
'NH57',
'NH58',
'NH59',
'NH60',
'NH61',
'NH62',
'NH63',
'NH64',
'NH65',
'NH66',
'NH67',
'NH68',
'NH69',
'NH70',
'NH71',
'NH72',
'NH73',
'NH74',
'NH75',
'NH76',
'NH77',
'NH78',
'NH79',
'NH80',
'AH1',
'AH2',
'AH3',
'AH4',
'AH5',
'AH6',
'AH7',
'AH8',
'AH9',
'AH10',
];
// Highway Suffix Suggestion Data (EXACT match only)
// This is for highway abbreviations that have specific suggestions
const wmessa_suggestedHwyAbbr = {
'NH01-': 'NH01 - रारा०१',
'NH02-': 'NH02 - रारा०२',
'NH03-': 'NH03 - रारा०३',
'NH04-': 'NH04 - रारा०४',
'NH05-': 'NH05 - रारा०५',
'NH06-': 'NH06 - रारा०६',
'NH07-': 'NH07 - रारा०७',
'NH08-': 'NH08 - रारा०८',
'NH09-': 'NH09 - रारा०९',
'NH10-': 'NH10 - रारा१०',
'NH11-': 'NH11 - रारा११',
'NH12-': 'NH12 - रारा१२',
'NH13-': 'NH13 - रारा१३',
'NH14-': 'NH14 - रारा१४',
'NH15-': 'NH15 - रारा१५',
'NH16-': 'NH16 - रारा१६',
'NH17-': 'NH17 - रारा१७',
'NH18-': 'NH18 - रारा१८',
'NH19-': 'NH19 - रारा१९',
'NH20-': 'NH20 - रारा२०',
'NH21-': 'NH21 - रारा२१',
'NH22-': 'NH22 - रारा२२',
'NH23-': 'NH23 - रारा२३',
'NH24-': 'NH24 - रारा२४',
'NH25-': 'NH25 - रारा२५',
'NH26-': 'NH26 - रारा२६',
'NH27-': 'NH27 - रारा२७',
'NH28-': 'NH28 - रारा२८',
'NH29-': 'NH29 - रारा२९',
'NH30-': 'NH30 - रारा३०',
'NH31-': 'NH31 - रारा३१',
'NH32-': 'NH32 - रारा३२',
'NH33-': 'NH33 - रारा३३',
'NH34-': 'NH34 - रारा३४',
'NH35-': 'NH35 - रारा३५',
'NH36-': 'NH36 - रारा३६',
'NH37-': 'NH37 - रारा३७',
'NH38-': 'NH38 - रारा३८',
'NH39-': 'NH39 - रारा३९',
'NH40-': 'NH40 - रारा४०',
'NH41-': 'NH41 - रारा४१',
'NH42-': 'NH42 - रारा४२',
'NH43-': 'NH43 - रारा४३',
'NH44-': 'NH44 - रारा४४',
'NH45-': 'NH45 - रारा४५',
'NH46-': 'NH46 - रारा४६',
'NH47-': 'NH47 - रारा४७',
'NH48-': 'NH48 - रारा४८',
'NH49-': 'NH49 - रारा४९',
'NH50-': 'NH50 - रारा५०',
'NH51-': 'NH51 - रारा५१',
'NH52-': 'NH52 - रारा५२',
'NH53-': 'NH53 - रारा५३',
'NH54-': 'NH54 - रारा५४',
'NH55-': 'NH55 - रारा५५',
'NH56-': 'NH56 - रारा५६',
'NH57-': 'NH57 - रारा५७',
'NH58-': 'NH58 - रारा५८',
'NH59-': 'NH59 - रारा५९',
'NH60-': 'NH60 - रारा६०',
'NH61-': 'NH61 - रारा६१',
'NH62-': 'NH62 - रारा६२',
'NH63-': 'NH63 - रारा६३',
'NH64-': 'NH64 - रारा६४',
'NH65-': 'NH65 - रारा६५',
'NH66-': 'NH66 - रारा६६',
'NH67-': 'NH67 - रारा६७',
'NH68-': 'NH68 - रारा६८',
'NH69-': 'NH69 - रारा६९',
'NH70-': 'NH70 - रारा७०',
'NH71-': 'NH71 - रारा७१',
'NH72-': 'NH72 - रारा७२',
'NH73-': 'NH73 - रारा७३',
'NH74-': 'NH74 - रारा७४',
'NH75-': 'NH75 - रारा७५',
'NH76-': 'NH76 - रारा७६',
'NH77-': 'NH77 - रारा७७',
'NH78-': 'NH78 - रारा७८',
'NH79-': 'NH79 - रारा७९',
'NH80-': 'NH80 - रारा८०',
};
// Suffixes with No Standard Abbreviation
const wmessa_knownNoAbbr = ['Lane', 'Loop', 'Mall', 'Mews', 'Path', 'Ramp', 'Rise', 'View', 'Walk', 'Way'];
// --- NEW DATA FOR GENERAL WORDS (PRE-SUFFIX) ---
// General Word Suggestion Data (WordToAbbreviate: Abbreviation)
const wmessa_generalWordSuggestions = {
Mount: 'Mt',
Saint: 'St', // Note: "St" for Saint. Suffix logic handles "St" for Street.
Fort: 'Ft',
Marg: 'Marga', // Example: "Marg Bryant Drive" -> "Marga Bryant Dr"
// Add other common words like "North": "N", "South": "S", etc., if standard for pre-suffix words.
};
// General Word Approved Abbreviation Data (Abbreviation: FullWord) - for validation
const wmessa_generalWordApprovedAbbr = {
Mt: 'Mount',
St: 'Saint',
Ft: 'Fort',
Marga: 'Marg', // Example
// e.g. "N": "North", "S": "South"
};
function wmessa_titleCase(str) {
return str
.split(/\s+/)
.map(function (txt) {
// If word matches a preserve-case word (case-insensitive), use the preserved version
const preserve = wmessa_preserveCaseWords.find((w) => w.toLowerCase() === txt.toLowerCase());
if (preserve) return preserve;
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
})
.join(' ');
}
let wmessa_valueObserver;
function wmessa_init() {
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.removedNodes.forEach((node) => {
if (node.classList && node.classList.contains('address-edit-card')) {
if (wmessa_valueObserver) {
wmessa_valueObserver.disconnect();
}
}
});
mutation.addedNodes.forEach((node) => {
if (node.classList && node.classList.contains('address-edit-card')) {
setTimeout(() => {
// Main street name
const streetNameInput = node.querySelector('wz-autocomplete.street-name');
if (streetNameInput && streetNameInput.shadowRoot) {
const wzTextInput = streetNameInput.shadowRoot.querySelector('wz-text-input');
if (wzTextInput) {
wmessa_monitor(wzTextInput);
} else {
console.warn('WMESSA: wz-text-input not found in street-name shadowRoot.');
}
} else {
console.warn('WMESSA: street-name input or its shadowRoot not found.');
}
// Alt street name(s)
const altStreetInputs = node.querySelectorAll('wz-autocomplete.alt-street-name');
altStreetInputs.forEach((altInput) => {
if (altInput && altInput.shadowRoot) {
const altWzTextInput = altInput.shadowRoot.querySelector('wz-text-input');
if (altWzTextInput) {
wmessa_monitor(altWzTextInput);
} else {
console.warn('WMESSA: wz-text-input not found in alt-street-name shadowRoot.');
}
} else {
console.warn('WMESSA: alt-street-name input or its shadowRoot not found.');
}
});
}, 250);
}
});
}
});
});
const editPanel = document.getElementById('edit-panel');
if (editPanel) {
observer.observe(editPanel, { childList: true, subtree: true });
} else {
console.warn('WMESSA: Edit panel not found for observer.');
}
WazeWrap.Interface.ShowScriptUpdate('WME Road Name Helper NP', GM_info.script.version, updateMessage, GreasyFork_URL, forumURL);
}
// Also observe for alt street card (for alt names)
const altStreetPanelObserver = new MutationObserver((mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.classList && node.classList.contains('edit-alt-street-card')) {
setTimeout(() => {
const altStreetInput = node.querySelector('wz-autocomplete.alt-street-name');
if (altStreetInput && altStreetInput.shadowRoot) {
const altWzTextInput = altStreetInput.shadowRoot.querySelector('wz-text-input');
if (altWzTextInput) {
wmessa_monitor(altWzTextInput);
} else {
console.warn('WMESSA: wz-text-input not found in alt-street-name shadowRoot (alt card).');
}
} else {
console.warn('WMESSA: alt-street-name input or its shadowRoot not found (alt card).');
}
}, 250);
}
});
}
});
});
// Observe the whole document for alt street cards
altStreetPanelObserver.observe(document.body, { childList: true, subtree: true });
function wmessa_monitor(element) {
let abbrContainer = document.createElement('div');
abbrContainer.id = 'WMESSA_container';
abbrContainer.innerHTML =
'<div class="WMESSA_icon" title="WME Standard Suffix Abbreviations"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor"><path fill-rule="evenodd" d="M4.5 2A2.5 2.5 0 0 0 2 4.5v2.879a2.5 2.5 0 0 0 .732 1.767l4.5 4.5a2.5 2.5 0 0 0 3.536 0l2.878-2.878a2.5 2.5 0 0 0 0-3.536l-4.5-4.5A2.5 2.5 0 0 0 7.38 2H4.5ZM5 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" /></svg></div>' +
'<div id="WMESSA_output">Loading...</div>';
const statusTextContainer = element.shadowRoot.querySelector('.status-text-container');
if (!statusTextContainer) {
console.warn('WMESSA: .status-text-container not found. UI will not be displayed.');
return;
}
statusTextContainer.insertBefore(abbrContainer, statusTextContainer.firstChild);
let abbrOutput = abbrContainer.querySelector('#WMESSA_output');
const css = [
'.status-text-container {width: calc(100% + ' +
(document.querySelector('#edit-panel .address-edit-card .street-name-row .tts-playback')
? document.querySelector('#edit-panel .address-edit-card .street-name-row .tts-playback').offsetWidth
: 0) +
'px); display: flex; flex-direction: column-reverse;}',
'#WMESSA_container {display: flex; align-items: center; flex-grow: 1; margin-top: var(--wz-label-margin, 8px); padding: 0 2px; border-radius: 5px; background: #ffffff; color: #ffffff; gap: 5px; cursor: default; transition: background 0.25s linear, color 0.25s linear; font-size: 0.9em;}',
'#WMESSA_output {color: #000000; white-space: pre-wrap; flex-grow: 1;}',
'.WMESSA_icon {display: inline-flex; padding: 2px; height: 12px; background: rgba(0,0,0,0.5); border-radius: 3px; flex-shrink: 0; margin-right: 5px;}',
'.WMESSA_icon svg {height: 100%;}',
'#WMESSA_container.info {background: #e0f2fe; color: #e0f2fe;}',
'#WMESSA_container.check {background: #fef3c7; color: #fef3c7; cursor: pointer;}',
'#WMESSA_container.check:hover {background: #fde68a; color: #fde68a;}',
'#WMESSA_container.valid {background: #d1fae5; color: #d1fae5;}',
].join(' ');
const styleElement = document.createElement('style');
styleElement.type = 'text/css';
styleElement.textContent = css;
element.shadowRoot.appendChild(styleElement);
if (wmessa_valueObserver) {
wmessa_valueObserver.disconnect();
}
wmessa_valueObserver = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
wmessa_update(element, abbrContainer, abbrOutput);
}
}
});
wmessa_valueObserver.observe(element, { attributes: true });
wmessa_update(element, abbrContainer, abbrOutput);
// Add Tab key support for applying suggestion
element.addEventListener('keydown', function (e) {
if (e.key === 'Tab') {
const abbrContainer = element.shadowRoot.querySelector('#WMESSA_container');
if (abbrContainer && abbrContainer.classList.contains('check')) {
const abbrOutput = abbrContainer.querySelector('#WMESSA_output');
// Simulate click to apply suggestion
abbrContainer.click();
e.preventDefault();
}
}
});
}
function wmessa_analyzeSuffix(suffix) {
const suffixLower = suffix.toLowerCase();
let result = { status: 'info', message: 'No match for suffix.', proposed: suffix, original: suffix };
const isKnownNoAbbrExact = (sLower) => wmessa_knownNoAbbr.some((kna) => kna.toLowerCase() === sLower);
const getKnownNoAbbrCased = (sLower) => wmessa_knownNoAbbr.find((kna) => kna.toLowerCase() === sLower);
// 0. Exact match for highway abbreviations (special case)
const hwyKey = Object.keys(wmessa_suggestedHwyAbbr).find((key) => key.toLowerCase() === suffixLower);
if (hwyKey) {
const suggestedHwy = wmessa_suggestedHwyAbbr[hwyKey];
if (suggestedHwy.toLowerCase() !== suffixLower) {
return { status: 'check', message: `Use ${suggestedHwy} for ${hwyKey}`, proposed: suggestedHwy, original: suffix };
} else {
return { status: 'valid', message: `${suggestedHwy}`, proposed: suggestedHwy, original: suffix };
}
}
// 1. Exact match: Typed IS an approved abbreviation (e.g., "Rd")
if (wmessa_approvedAbbr.hasOwnProperty(suffix)) {
return { status: 'valid', message: `${suffix} for ${wmessa_approvedAbbr[suffix]}`, proposed: suffix, original: suffix };
}
const approvedKeyCi = Object.keys(wmessa_approvedAbbr).find((k) => k.toLowerCase() === suffixLower);
if (approvedKeyCi) {
return { status: 'valid', message: `${approvedKeyCi} for ${wmessa_approvedAbbr[approvedKeyCi]}`, proposed: approvedKeyCi, original: suffix };
}
// 2. Exact match: Typed IS a known non-abbreviated suffix (e.g., "Lane")
if (isKnownNoAbbrExact(suffixLower)) {
const casedNoAbbr = getKnownNoAbbrCased(suffixLower) || suffix;
return { status: 'valid', message: casedNoAbbr, proposed: casedNoAbbr, original: suffix };
}
const escapedSuffix = suffix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const suffixRegex = new RegExp(`^${escapedSuffix}`, 'i');
// 3. Suggestion: Typed is (prefix of) a full word that should be abbreviated (e.g., "Street" or "Stre" -> "St")
let suggestFromFullKey = Object.keys(wmessa_suggestedAbbr).find((key) => key.toLowerCase() === suffixLower);
if (!suggestFromFullKey) {
suggestFromFullKey = Object.keys(wmessa_suggestedAbbr).find((key) => suffixRegex.test(key));
}
if (suggestFromFullKey) {
const suggestedAbbr = wmessa_suggestedAbbr[suggestFromFullKey];
if (suggestedAbbr.toLowerCase() !== suffixLower) {
return { status: 'check', message: `Use ${suggestedAbbr} for ${suggestFromFullKey}`, proposed: suggestedAbbr, original: suffix };
} else {
// Typed the same as the suggestion (e.g. typed "Lane", suggested "Lane" because "Ln":"Lane")
if (isKnownNoAbbrExact(suggestedAbbr.toLowerCase())) {
const casedNoAbbr = getKnownNoAbbrCased(suggestedAbbr.toLowerCase()) || suggestedAbbr;
return { status: 'valid', message: casedNoAbbr, proposed: casedNoAbbr, original: suffix };
}
const finalApprovedKeyCi = Object.keys(wmessa_approvedAbbr).find((k) => k.toLowerCase() === suggestedAbbr.toLowerCase());
if (finalApprovedKeyCi) {
return { status: 'valid', message: `${finalApprovedKeyCi} for ${wmessa_approvedAbbr[finalApprovedKeyCi]}`, proposed: finalApprovedKeyCi, original: suffix };
}
}
}
// 4. Suggestion: Typed is (prefix of) a known non-abbreviated word (e.g., "Lan" -> "Lane")
const knownNoAbbrCompletion = wmessa_knownNoAbbr.find((key) => suffixRegex.test(key));
if (knownNoAbbrCompletion && knownNoAbbrCompletion.toLowerCase() !== suffixLower) {
return { status: 'check', message: `Use ${knownNoAbbrCompletion}`, proposed: knownNoAbbrCompletion, original: suffix };
}
// 5. Suggestion: Typed is (prefix of) an approved abbreviation (e.g., "Al" -> "Ally")
const approvedAbbrCompletionKey = Object.keys(wmessa_approvedAbbr).find((key) => suffixRegex.test(key));
if (approvedAbbrCompletionKey && approvedAbbrCompletionKey.toLowerCase() !== suffixLower) {
return { status: 'check', message: `Use ${approvedAbbrCompletionKey} for ${wmessa_approvedAbbr[approvedAbbrCompletionKey]}`, proposed: approvedAbbrCompletionKey, original: suffix };
}
return result;
}
function wmessa_update(element, abbrContainer, abbrOutput) {
abbrContainer.classList.remove('valid', 'check', 'info');
abbrContainer.onclick = null;
abbrOutput.innerText = 'Awaiting input...';
const currentValue = element.value.trim();
if (!currentValue) {
return;
}
if (currentValue.match(/^The [a-zA-Z0-9\s'-]+$/i) && currentValue.split(/\s+/).length <= 3) {
// "The x" or "The x y"
abbrContainer.classList.add('info');
abbrOutput.innerText = "Do not abbreviate 'The x' names";
return;
}
let currentWords = currentValue.split(/\s+/);
let proposedWords = [...currentWords];
let preSuffixChangesMade = false;
let overallStatus = 'info';
let messages = [];
// --- Process Pre-Suffix Words ---
if (currentWords.length > 1) {
for (let i = 0; i < currentWords.length - 1; i++) {
const word = currentWords[i];
const wordLower = word.toLowerCase();
const generalApprovedKeyCi = Object.keys(wmessa_generalWordApprovedAbbr).find((k) => k.toLowerCase() === wordLower);
if (generalApprovedKeyCi) {
// Word is already an approved general abbreviation
if (word !== generalApprovedKeyCi) {
// Correct casing if needed
proposedWords[i] = generalApprovedKeyCi;
preSuffixChangesMade = true;
}
continue;
}
const generalSuggestionKeyCi = Object.keys(wmessa_generalWordSuggestions).find((k) => k.toLowerCase() === wordLower);
if (generalSuggestionKeyCi) {
const suggestedGeneralAbbr = wmessa_generalWordSuggestions[generalSuggestionKeyCi];
if (suggestedGeneralAbbr.toLowerCase() !== wordLower) {
proposedWords[i] = suggestedGeneralAbbr;
preSuffixChangesMade = true;
}
}
}
}
// --- Process Suffix (last word) ---
let suffixAnalysis = {
status: 'info',
message: currentWords.length > 0 ? 'Awaiting valid suffix.' : 'Awaiting input.',
proposed: currentWords.length > 0 ? currentWords[currentWords.length - 1] : '',
original: currentWords.length > 0 ? currentWords[currentWords.length - 1] : '',
};
if (currentWords.length > 0) {
const potentialSuffix = currentWords[currentWords.length - 1];
suffixAnalysis = wmessa_analyzeSuffix(potentialSuffix);
proposedWords[currentWords.length - 1] = suffixAnalysis.proposed;
}
// --- Combine Results and Determine UI ---
const finalProposedString = wmessa_titleCase(proposedWords.join(' '));
const suffixChanged = currentWords.length > 0 && suffixAnalysis.proposed.toLowerCase() !== suffixAnalysis.original.toLowerCase();
const capitalizationChanged = currentValue !== finalProposedString;
let anyChangeProposed = preSuffixChangesMade || suffixChanged || capitalizationChanged;
if (anyChangeProposed) {
overallStatus = 'check';
let suggestionDetails = [];
if (preSuffixChangesMade) suggestionDetails.push('word(s) before suffix');
if (suffixChanged) suggestionDetails.push('suffix');
messages.push(`Suggest: "${finalProposedString}"`);
if (suggestionDetails.length > 0) {
messages.push(`(Changes to ${suggestionDetails.join(' & ')})`);
}
if (suffixChanged && suffixAnalysis.message && suffixAnalysis.message.startsWith('Use ')) {
messages.push(suffixAnalysis.message); // Add specific suffix suggestion message
}
abbrContainer.onclick = function () {
element.value = finalProposedString;
};
} else {
// No changes proposed, evaluate if current input is valid or just no rules hit
let allWordsAreStandard = true; // Assume true unless a known full word (that can be abbreviated) is found
if (currentWords.length > 1) {
for (let i = 0; i < currentWords.length - 1; i++) {
const word = currentWords[i];
const wordLower = word.toLowerCase();
// Check if it's a full word that *could* be abbreviated, but isn't
const canBeAbbreviatedKey = Object.keys(wmessa_generalWordSuggestions).find((k) => k.toLowerCase() === wordLower);
// And it's not already an abbreviation of itself or something else
const isAbbreviation = Object.keys(wmessa_generalWordApprovedAbbr).find((k) => k.toLowerCase() === wordLower);
if (canBeAbbreviatedKey && !isAbbreviation) {
allWordsAreStandard = false;
break;
}
}
}
if (suffixAnalysis.status === 'valid' && allWordsAreStandard) {
overallStatus = 'valid';
messages.push(suffixAnalysis.message || `"${currentValue}" is standard.`);
} else {
overallStatus = 'info'; // Default to info if not perfectly valid or no suggestions
if (!allWordsAreStandard) {
messages.push('Consider standard abbreviations for words before suffix.');
}
if (suffixAnalysis.status === 'info') {
messages.push(suffixAnalysis.message || 'Check suffix standards.');
} else if (suffixAnalysis.status === 'valid' && !allWordsAreStandard) {
// Suffix is fine, but pre-words are not optimal
messages.push(`Suffix "${suffixAnalysis.proposed}" is standard.`);
} else {
messages.push(suffixAnalysis.message || `Review standards for "${currentValue}"`);
}
}
}
abbrContainer.classList.add(overallStatus);
abbrOutput.innerText = messages.filter((m) => m).join('\n') || 'Awaiting input or check standards.';
}
function wmessa_bootstrap() {
const wmeSdk = getWmeSdk({ scriptId: 'wme-road-name-helper-np', scriptName: 'WME Road Name Helper NP' });
sdk = wmeSdk;
sdk.Events.once({ eventName: 'wme-ready' }).then(() => {
loadScriptUpdateMonitor();
wmessa_init();
});
}
function waitForWME() {
if (!unsafeWindow.SDK_INITIALIZED) {
setTimeout(waitForWME, 500);
return;
}
unsafeWindow.SDK_INITIALIZED.then(wmessa_bootstrap);
}
waitForWME();
function loadScriptUpdateMonitor() {
try {
const updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
updateMonitor.start();
} catch (ex) {
// Report, but don't stop if ScriptUpdateMonitor fails.
console.error(`${SCRIPT_NAME}:`, ex);
}
}
/*
Changelog:
2025.06.18.01
- Restored AH02 to AH2.
2025.06.15.02
- Added: Typing "AH2" now suggests "AH02"
- Added: Typing "NH01 - रा१" or using suffix "रा१" now suggests "NH01 - रारा०१"
- Improved: Custom suffix and highway code mapping logic for Nepali and highway abbreviations
- Fixed: Suffix suggestion logic for Nepali abbreviations
*/
})();