// ==UserScript==
// @name WME — RPP
// @version 1.8
// @description WME - Add a new place of residence (G). Opens edit (pencil) on LMB+G and auto-fills Street/City + Building number/letter with optional increment.
// @match /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
// @include https://*.waze.com/*/editor*
// @include https://*.waze.com/editor*
// @include https://*.waze.com/map-editor*
// @include https://*.waze.com/beta_editor*
// @copyright 2025, Michalito_78
// @author Michalito_78
// @run-at document-end
// @grant none
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=waze.com
// @namespace https://greasyfork.org/pl/users/1504072
// ==/UserScript==
/*
Changelog:
1.0 New script
1.1 Moving the script to the tab
1.2 Added building number, letter, increment
1.3 Added radiobuttons (number/letter)
1.4 Added checkbox ON/OFF number
1.5 Added checkbox ON/OFF script
1.6 Fix HTML
1.7 Cleaner structure + accurate LOG
1.8 Fix checkbox ON / OFF script
*/
(function () {
'use strict';
// ──────────────────────────────────────────────────────────────────────────────
// KONFIGURACJA / CONFIG
// ──────────────────────────────────────────────────────────────────────────────
/** Identyfikatory niestandardowych elementów sterujących panelem*/
const IDS = {
street: 'wme-rpp-street', // __________________________________________ Input: Nazwa ulicy / Street
building: 'wme-rpp-building', // ______________________________________ Input: Numer budynku / Building number
letter: 'wme-rpp-letter', // __________________________________________ Input: Litera / Letter
increment: 'wme-rpp-increment', // ____________________________________ Input: Przyrost / Increment
copyToggle: 'wme-rpp-copy-toggle', // _________________________________ Checkbox: ON/OFF numeracja / numbering
scriptToggle: 'wme-rpp-script-toggle', // _____________________________ Checkbox: ON/OFF script
radioBuilding: 'wme-rpp-radio-building', // ___________________________ RadioButton numer budynku
radioLetter: 'wme-rpp-radio-letter', // ______________________________ RadioButton litera
tabRoot: 'wme-rpp-tab', // ____________________________________________ Zakładka w dziale skrypty
userName: 'wme-rpp-user-name', // _____________________________________ Nazwa użytkownika
userPoints: 'wme-rpp-user-edits', // __________________________________ ilość punktów
};
/** Indentyfikatory localstore */
const LS = {
street: 'wmeRppStreet', // ____________________________________________ Localstore Nazwa ulicy / Street
building: 'wmeRppBuildingNumber', // __________________________________ Localstore Numer budynku / Building number
letter: 'wmeRppBuildingLetter', // ____________________________________ Localstore Litera / Letter
increment: 'wmeRppIncrement', // ______________________________________ Localstore Przyrost / Increment
};
/** Stan skryptu */
let state = {
isGPressed: false, // _________________________________________________ Czy wciśnięty przycisk G
pencilOpen: false, // _________________________________________________ Czy weszliśmy do edycji (klikniety ołówek)
};
/** Polski Alfabet */
const POLISH_ALPHABET = [
'A','B','C','D','E','F','G','H','I','J','K','L','Ł',
'M','N','O','P','R','S','T','U','W','X','Y','Z','AA','AB','AC'
];
/** Elementy zastępcze autouzupełniania WME. */
const WME_PLACEHOLDERS = {
street: 'Wpisz ulicę', // _____________________________________________ Pole tekstowe ulica
city: 'Wpisz miasto', // ____________________________________________ Pole tekstowe miasto
building: 'Dodaj numer domu', // ______________________________________ Pole tekstowe numer domu
};
/** Skrót klawiszowy używany razem z lewym przyciskiem myszy (LPM) do uruchomienia skryptu */
const HOTKEY = 'g';
// ──────────────────────────────────────────────────────────────────────────────
// LOGGER
// ──────────────────────────────────────────────────────────────────────────────
const LOG_PREFIX = '[WME - RPP]';
const log = {
info: (...a) => console.info(`${LOG_PREFIX} [INFO]`, ...a),
warn: (...a) => console.warn(`${LOG_PREFIX} [WARN]`, ...a),
error: (...a) => console.error(`${LOG_PREFIX} [ERROR]`, ...a),
ok: (...a) => console.log(`${LOG_PREFIX} ✅`, ...a),
};
// ──────────────────────────────────────────────────────────────────────────────
// UTILS
// ──────────────────────────────────────────────────────────────────────────────
/** Pomocnik funkcji do opóźnienia wykonania. */
function debounce(fn, wait = 300) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), wait);
};
}
/** Odczyt pierwszej liczby całkowitej znalezionej w ciągu. */
function readInt(value, def = 0) {
const m = String(value ?? '').trim().match(/-?\d+/);
return m ? parseInt(m[0], 10) : def;
}
/** Przeskok po kolejnych literach alfabetu. */
function incrementLetter(letterRaw, inc) {
const n = POLISH_ALPHABET.length;
let cur = String(letterRaw || '').trim().toUpperCase();
if (!cur) cur = 'A';
let index = POLISH_ALPHABET.indexOf(cur);
if (index === -1) index = POLISH_ALPHABET.indexOf('A');
const newIndex = (index + (inc % n) + n) % n;
return POLISH_ALPHABET[newIndex];
}
/** Wyszukanie pasujących elementów do rozwijanych list. */
function deepQueryAll(selector, root = document) {
const out = [];
try { out.push(...root.querySelectorAll(selector)); } catch (e) { /* ignore */ }
const all = root.querySelectorAll('*');
for (const el of all) {
if (el.shadowRoot) {
try { out.push(...el.shadowRoot.querySelectorAll(selector)); } catch (e) { /* ignore */ }
out.push(...deepQueryAll(selector, el.shadowRoot));
}
}
return Array.from(new Set(out));
}
/** Wybierz pierwszy element z listy. */
function pickVisible(arr) {
return arr.find(el => el && el.offsetParent !== null) || arr[0] || null;
}
/** Znajdywanie podobieństw w liście rozwijanej. */
function setReactValue(input, value) {
if (!input) return false;
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
if (!setter) {
input.value = value;
} else {
setter.call(input, value);
}
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
return true;
}
/** Algorytm dopasowania. */
function levenshtein(a, b) {
const m = [];
for (let i = 0; i <= b.length; i++) m[i] = [i];
for (let j = 0; j <= a.length; j++) m[0][j] = j;
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
m[i][j] = b.charAt(i-1) === a.charAt(j-1)
? m[i-1][j-1]
: Math.min(m[i-1][j-1]+1, m[i][j-1]+1, m[i-1][j]+1);
}
}
return m[b.length][a.length];
}
// ──────────────────────────────────────────────────────────────────────────────
// WME FIELD ACCESSORS
// ──────────────────────────────────────────────────────────────────────────────
/** Wyszukiwanie danych wejściowych z WME (pole miasto, ulica). */
function findWmeInput(which) {
let placeholder = WME_PLACEHOLDERS[which];
if (!placeholder) return null;
return (pickVisible(deepQueryAll(`input[placeholder="${placeholder}"]`))); // ______________________ @type {HTMLInputElement}
}
/** Próba autouzupełnienia pól miasto i ulica. Przy niezgodności stopujemy autouzupełnianie i zostawiamy otwartą rozwijaną listę. */
function pickAutocompleteOption(typedLower, maxTries = 3) {
return new Promise(resolve => {
let tries = 0;
const timer = setInterval(() => {
const options = deepQueryAll('[role="option"]');
if (options.length) {
let best = options.find(opt => (opt.textContent || '').trim().toLowerCase().startsWith(typedLower));
if (!best) {
let bestScore = Infinity;
for (const opt of options) {
const label = (opt.textContent || '').trim().toLowerCase();
const diff = levenshtein(label, typedLower);
if (diff < bestScore) {
bestScore = diff;
best = opt;
}
}
}
if (best) {
best.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
best.click();
clearInterval(timer);
resolve(true);
return;
}
}
if (++tries >= maxTries) {
clearInterval(timer);
resolve(false);
}
}, 220);
});
}
/** Wpisywanie autouzupełnienia. */
async function writeToAutocompleteField(which, value) {
const input = findWmeInput(which);
if (!input) {
log.warn(`Nie znaleziono pola ${which === 'street' ? 'Ulica/Street' : 'Miasto/City'}.`);
return false;
}
setReactValue(input, value);
// Otwórz menu rozwijane
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
input.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown', bubbles: true }));
const ok = await pickAutocompleteOption(String(value).toLowerCase());
if (!ok) {
// Czasami wWME wymaga kliknięcia
setTimeout(() => {
input.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
input.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
input.dispatchEvent(new MouseEvent('click', { bubbles: true }));
input.focus();
}, 80);
log.warn(`Nie udało się zatwierdzić wartości w polu ${which}.`);
} else {
log.ok(`${which === 'street' ? 'Ulica/Street' : 'Miasto/City'} wybrana.`);
}
return ok;
}
/** Kopiowanie numeru budynku. */
function writeToBuildingNumber(value) {
const input = findWmeInput('building');
if (!input) {
log.warn('Nie znaleziono pola Numer budynku / Building number.');
return false;
}
return setReactValue(input, String(value ?? ''));
}
// ──────────────────────────────────────────────────────────────────────────────
// CORE LOGIC
// ──────────────────────────────────────────────────────────────────────────────
/** Pobierz cyfrę z przyrostu + odczyt z locarstore. */
function getIncrementSafe() {
const raw = document.getElementById(IDS.increment)?.value ?? localStorage.getItem(LS.increment) ?? '0';
return readInt(raw, 0);
}
/** Zastosuj przyrost do cyfry + zapis do locarstore. */
function applyNumberIncrement(baseNum, inc) {
if (Number.isNaN(baseNum)) {
log.warn('Numer budynku nie jest liczbą – pomijam przyrost.');
return;
}
const next = baseNum + inc;
const el = /** @type {HTMLInputElement} */ (document.getElementById(IDS.building));
if (el) el.value = String(next);
localStorage.setItem(LS.building, String(next));
}
/** Zastosuj przyrost do litery+ zapis do locarstore. */
function applyLetterIncrement(curLetter, inc) {
const next = incrementLetter(curLetter, inc);
const el = /** @type {HTMLInputElement} */ (document.getElementById(IDS.letter));
if (el) el.value = next;
localStorage.setItem(LS.letter, next);
}
/** Główna akcja wywoływana po otwarciu edycji (ołówek).
- Wypełnia pola zgodnie z ustawieniami panelu.
- Kopiuje wartość panelu „Ulica” do WME Ulica i Miasto (jak w oryginalnym działaniu)
- Kopiuje numer budynku (cyfra / cyfra + litera) do WME „Dodaj numer domu”
- Stosuje inkrementację do cyfry lub litery, jeśli jest włączona. */
async function performFill() {
const onOff = /** @type {HTMLInputElement} */ (document.getElementById(IDS.scriptToggle));
const copyToggle = /** @type {HTMLInputElement} */ (document.getElementById(IDS.copyToggle));
const streetSrc = /** @type {HTMLInputElement} */ (document.getElementById(IDS.street));
const buildingSrc = /** @type {HTMLInputElement} */ (document.getElementById(IDS.building));
const letterSrc = /** @type {HTMLInputElement} */ (document.getElementById(IDS.letter));
const radioBuilding = /** @type {HTMLInputElement} */ (document.getElementById(IDS.radioBuilding));
const radioLetter = /** @type {HTMLInputElement} */ (document.getElementById(IDS.radioLetter));
if (!onOff?.checked) {
log.info('Skrypt jest wyłączony (ON/OFF).');
return;
}
const streetVal = String(streetSrc?.value || '').trim();
if (!streetVal) {
alert('Pole "Nazwa ulicy / Street" w panelu jest puste.');
return;
}
const inc = getIncrementSafe();
const rawBuilding = String(buildingSrc?.value ?? '');
const baseNum = readInt(rawBuilding, NaN);
const curLetter = String(letterSrc?.value || '').toUpperCase();
if (copyToggle?.checked) {
if (radioBuilding?.checked) {
if (!Number.isNaN(baseNum)) {
writeToBuildingNumber(String(baseNum));
} else {
log.warn('Numer budynku nie jest liczbą – nie wpisuję do adresu.');
}
} else if (radioLetter?.checked) {
const numberPart = rawBuilding || '';
const letterPart = curLetter || '';
writeToBuildingNumber(`${numberPart}${letterPart}`);
} else {
// Default: number + letter if present
const numberPart = rawBuilding || '';
const letterPart = curLetter || '';
writeToBuildingNumber(`${numberPart}${letterPart}`);
}
}
if (copyToggle?.checked) {
if (radioBuilding?.checked && !Number.isNaN(baseNum)) {
applyNumberIncrement(baseNum, inc);
}
if (radioLetter?.checked) {
applyLetterIncrement(curLetter, inc);
}
}
await writeToAutocompleteField('street', streetVal);
await writeToAutocompleteField('city', streetVal);
log.ok('Wypełnianie zakończone.');
}
/** Zatwierdzenie całości. */
function openEditAndFill() {
setTimeout(() => {
const button = pickVisible(deepQueryAll("i.w-icon.w-icon-pencil-fill.edit-button[aria-disabled='false']"));
if (!button) {
log.warn('Nie znaleziono przycisku edycji (ołówek).');
return;
}
state.pencilOpen = true;
button.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
button.click();
setTimeout(() => {
const saveBtn = pickVisible(deepQueryAll('.save-button'));
if (saveBtn) {
saveBtn.addEventListener('click', (ev) => {
// We do not block default save; just a hook to observe and reset state
log.info('Kliknięto "Zatwierdź" / Save.');
state.pencilOpen = false;
}, { once: true });
}
}, 500);
setTimeout(performFill, 500);
}, 150);
}
// ──────────────────────────────────────────────────────────────────────────────
// HOTKEY + LPM HANDLERS
// ──────────────────────────────────────────────────────────────────────────────
document.addEventListener('keydown', (e) => {
if ((e.key === HOTKEY || e.key === HOTKEY.toUpperCase()) &&
!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
state.isGPressed = true;
}
});
document.addEventListener('keyup', (e) => {
if ((e.key === HOTKEY || e.key === HOTKEY.toUpperCase())) {
// state.isGPressed = false;
}
});
document.addEventListener('click', (e) => {
if (state.isGPressed && e.button === 0) {
openEditAndFill();
state.isGPressed = false;
e.stopPropagation();
}
});
log.info('Aktywne: LPM + G → otwórz edycję i wstaw Ulica+Miasto+Numer.');
// ──────────────────────────────────────────────────────────────────────────────
// CONTROL PANEL (TAB)
// ──────────────────────────────────────────────────────────────────────────────
/** Pobieranie nazwy użytkownika. */
function readUserInfoSafe() {
try {
const box = document.querySelector('#toolbar > div > div > wz-user-box');
const name = box?.shadowRoot
?.querySelector('div > wz-menu > div.user-details-wrapper > div:nth-child(2) > div > wz-h5')
?.textContent?.trim() || '';
const edits = document.querySelector('#toolbar > div > div > wz-user-box > span > wz-user-box-score:nth-child(2)')?.value || '';
return { name, edits: String(edits) };
} catch (e) {
return { name: '', edits: '' };
}
}
/** Inicjacja panelu. */
function initializeControlPanel() {
const tabs = document.querySelector('#user-info .nav-tabs');
const content = document.querySelector('#user-info .tab-content');
if (!tabs || !content) return;
// unikaj duplikatów
if (document.getElementById(IDS.tabRoot)) return;
// Nazwa nowej zakładki
const li = document.createElement('li');
const a = document.createElement('a');
a.href = `#${IDS.tabRoot}`;
a.setAttribute('data-toggle', 'tab');
a.textContent = '\u2200 RPP';
li.appendChild(a);
tabs.appendChild(li);
// HTML panela
const section = document.createElement('section');
section.id = IDS.tabRoot;
section.className = 'tab-pane';
section.style.padding = '6px 8px';
const { name: USER, edits: NUMBER_EDITS } = readUserInfoSafe();
section.innerHTML = `
<label id="user-label" style="display:block;margin-bottom:5px;font-weight:bold;font-size:12px;">
Ilość edycji użytkownika: <span id="${IDS.userName}"></span>
</label>
<div id="${IDS.userPoints}"
style="width:95%;padding:6px;font-size:10px;border:1px solid #ccc;border-radius:6px;margin-bottom:1px;background-color:#f9f9f9;">
</div>
<hr style="width:100%;text-align:left;margin: 10px 0;">
<div style="display:flex;align-items:center;gap:8px; margin: 10px 70px 20px;font-size:10px;">
<input id="${IDS.scriptToggle}" type="checkbox" checked />
<label for="${IDS.scriptToggle}" style="margin:0;">ON / OFF skrypt / script</label>
</div>
<label for="${IDS.street}" style="display:block;margin-bottom:5px;font-weight:bold;font-size:12px;">
Nazwa ulicy / Wsi <br> Street / Name of the village
</label>
<input id="${IDS.street}" type="text" placeholder="Twoja notatka... / Your note..."
style="width:95%;padding:6px;font-size:18px;border:1px solid #ccc;border-radius:6px;margin-bottom:12px;margin-bottom:1px" />
<hr style="width:100%;text-align:left;margin: 10px 0;">
<div style="display:flex;align-items:center;gap:8px;margin: 10px 40px 20px;font-size:10px;">
<input id="${IDS.copyToggle}" type="checkbox" checked />
<label for="${IDS.copyToggle}" style="margin:0;font-size:12px;">ON / OFF numeracja / numbering</label>
</div>
<div style="display:flex;align-items:flex-end;gap:10px;margin-bottom:12px;">
<div style="margin-top:5px;">
<input type="radio" name="wme-rpp-mode" id="${IDS.radioBuilding}" />
<label style="font-size:14px;" for="${IDS.radioBuilding}">Nr. budynku / Building number</label>
</div>
<div>
<input id="${IDS.building}" type="number" placeholder="np. 10"
style="width:55px;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:6px;" />
</div>
</div>
<div style="display:flex;align-items:flex-end;gap:10px;margin-bottom:12px;color:gray;">
<div style="margin-top:5px;">
<input type="radio" name="wme-rpp-mode" id="${IDS.radioLetter}" />
<label style="font-size:14px;" for="${IDS.radioLetter}">Litera / Letter</label>
</div>
<div>
<input id="${IDS.letter}" type="text" placeholder="np. A" maxlength="2"
style="margin-left:115px;width:55px;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:6px;text-transform:uppercase;" />
</div>
</div>
<hr style="width:100%;text-align:left;margin: 10px 0;">
<label for="${IDS.increment}" style="display:block;font-weight:bold;margin: 10px 0;font-size:14px;">
Przyrost / Increase
</label>
<input id="${IDS.increment}" type="number" value="0"
style="width:55px;padding:6px;font-size:13px;border:1px solid #ccc;border-radius:6px;" />
`;
// Przypisanie użytkownika i jego edycji
content.appendChild(section);
section.querySelector(`#${IDS.userName}`).textContent = USER || '(?)';
section.querySelector(`#${IDS.userPoints}`).textContent = NUMBER_EDITS || '';
/** Ustawienia początkowe po starcie */
const radioBuilding = /** @type {HTMLInputElement} */ (section.querySelector(`#${IDS.radioBuilding}`));
const radioLetter = /** @type {HTMLInputElement} */ (section.querySelector(`#${IDS.radioLetter}`));
const inputLetter = /** @type {HTMLInputElement} */ (section.querySelector(`#${IDS.letter}`));
radioBuilding.checked = true;
inputLetter.disabled = true;
// Przywracanie zapisanych wartości z localstore
const stStreet = localStorage.getItem(LS.street) ?? '';
const stBuilding = localStorage.getItem(LS.building) ?? '';
const stIncrement = localStorage.getItem(LS.increment) ?? '0';
const stLetter = (localStorage.getItem(LS.letter) ?? '').toUpperCase();
/** @type {HTMLInputElement} */
(section.querySelector(`#${IDS.street}`)).value = stStreet;
/** @type {HTMLInputElement} */
(section.querySelector(`#${IDS.building}`)).value = stBuilding;
/** @type {HTMLInputElement} */
(section.querySelector(`#${IDS.increment}`)).value = stIncrement;
/** @type {HTMLInputElement} */
(section.querySelector(`#${IDS.letter}`)).value = stLetter;
// ponowne zapisanie do localstore
const saveStreet = debounce((v) => localStorage.setItem(LS.street, v));
const saveBuilding = debounce((v) => localStorage.setItem(LS.building, v));
const saveIncrement = debounce((v) => localStorage.setItem(LS.increment, v));
const saveLetter = debounce((v) => localStorage.setItem(LS.letter, v.toUpperCase()));
section.querySelector(`#${IDS.street}`).addEventListener('input', (e) => {
saveStreet(/** @type {HTMLInputElement} */(e.target).value);
});
section.querySelector(`#${IDS.building}`).addEventListener('input', (e) => {
saveBuilding(/** @type {HTMLInputElement} */(e.target).value);
});
section.querySelector(`#${IDS.increment}`).addEventListener('input', (e) => {
saveIncrement(/** @type {HTMLInputElement} */(e.target).value);
});
section.querySelector(`#${IDS.letter}`).addEventListener('input', (e) => {
const inp = /** @type {HTMLInputElement} */(e.target);
let v = String(inp.value || '').toUpperCase();
// Zezwolenie na polskie znaki i dwuliterowa litery
v = v.replace(/[^A-ZŁ]/gi, '').toUpperCase().slice(0, 2);
// Wpisanie z ręki innej litery niż w alfabecie jest przyjmowane
inp.value = v;
saveLetter(v);
// Przełaczanie miedzy NR a litera kiedy pojawi się litera
if (v) {
radioLetter.checked = true;
inputLetter.disabled = false;
} else {
radioBuilding.checked = true;
inputLetter.disabled = true;
}
});
// checkbox ON / OFF numeracja / numbering
section.querySelector(`#${IDS.copyToggle}`).addEventListener('change', function () {
const isChecked = /** @type {HTMLInputElement} */(this).checked;
const buildingInput = /** @type {HTMLInputElement} */ (document.getElementById(IDS.building));
const letterInput = /** @type {HTMLInputElement} */ (document.getElementById(IDS.letter));
const buildingLbl = section.querySelector(`label[for="${IDS.radioBuilding}"]`);
const letterLbl = section.querySelector(`label[for="${IDS.radioLetter}"]`);
radioBuilding.disabled = !isChecked;
radioLetter.disabled = !isChecked;
buildingInput.disabled = !isChecked;
radioBuilding.checked = true;
radioLetter.checked = false;
// wyłączenie wprowadzania liter gdy OFF
letterInput.disabled = true;
if(!isChecked) letterInput.value = "A";
if (buildingLbl) buildingLbl.style.color = isChecked ? 'black' : 'gray';
if (letterLbl) letterLbl.style.color = 'gray';
});
// checkbox ON / OFF całego skryptu
section.querySelector(`#${IDS.scriptToggle}`).addEventListener('change', function () {
const isChecked = /** @type {HTMLInputElement} */(this).checked;
const copyToggle = /** @type {HTMLInputElement} */ (section.querySelector(`#${IDS.copyToggle}`));
if(isChecked) {
setTimeout(() => {
copyToggle.checked = true;
copyToggle.dispatchEvent(new Event('change'));
}, 200);
}
else{
copyToggle.checked = false;
copyToggle.dispatchEvent(new Event('change')); // Też uruchomi "OFF" stan
radioLetter.checked = false;
radioBuilding.checked = false;
}
// Disable all inputs except master toggle
const inputs = section.querySelectorAll('input, textarea, select');
inputs.forEach(input => {
if (input.id !== IDS.scriptToggle) input.disabled = !isChecked;
});
// Labels (except user label)
const labels = section.querySelectorAll('label');
labels.forEach(label => {
if (label.getAttribute('for') !== IDS.scriptToggle && label.id !== 'user-label') {
label.style.color = isChecked ? 'black' : 'gray';
}
});
// Elementy div (oprócz pola edycji użytkownika)
const divs = section.querySelectorAll('div');
divs.forEach(div => {
if (div.id !== IDS.userPoints) {
div.style.color = isChecked ? 'black' : 'gray';
}
});
// Elementy span (oprócz nazwy użytkownika)
const spans = section.querySelectorAll('span');
spans.forEach(span => {
if (span.id !== IDS.userName) {
span.style.color = isChecked ? 'black' : 'gray';
}
});
// Kolor przy OFF i ON
section.querySelectorAll('hr').forEach(hr => {
/** @type {HTMLElement} */ (hr).style.borderColor = isChecked ? '#ccc' : '#eee';
});
});
// Radiobutton litera
section.querySelector(`#${IDS.radioLetter}`).addEventListener('change', function () {
const isChecked = /** @type {HTMLInputElement} */(this).checked;
const letterInput = /** @type {HTMLInputElement} */ (document.getElementById(IDS.letter));
const letterLbl = section.querySelector(`label[for="${IDS.radioLetter}"]`);
if (isChecked) {
if (!letterInput.value) letterInput.value = 'A';
if (letterLbl) letterLbl.style.color = 'black';
letterInput.disabled = false;
}
});
// Radiobutton Nmeracja budynków
section.querySelector(`#${IDS.radioBuilding}`).addEventListener('change', function () {
const isChecked = /** @type {HTMLInputElement} */(this).checked;
const letterInput = /** @type {HTMLInputElement} */ (document.getElementById(IDS.letter));
const letterLbl = section.querySelector(`label[for="${IDS.radioLetter}"]`);
if (isChecked) {
if (letterLbl) letterLbl.style.color = 'gray';
letterInput.disabled = true;
letterInput.value = "A";
}
});
log.ok('Panel RPP dodany.');
}
/** Oczekiwanie na wyciągnięcie danych użytkownika i inicjacja panelu. */
function ensurePanelWhenReady() {
function isDomReady() {
const userInfo = document.getElementById('user-info');
if (!userInfo) return false;
return (
userInfo.getElementsByClassName('nav-tabs').length > 0 &&
userInfo.getElementsByClassName('tab-content').length > 0
);
}
function tryInit() {
const tabsList = document.querySelector('#user-tabs ul.nav-tabs');
const tabsContent = document.querySelector('#user-tabs .tab-content');
if (tabsList && tabsContent) {
initializeControlPanel();
return true;
}
return false;
}
const observer = new MutationObserver(() => {
if (tryInit()) observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
const poll = () => {
if (isDomReady()) {
initializeControlPanel();
} else {
setTimeout(poll, 500);
}
};
poll();
}
ensurePanelWhenReady();
// ──────────────────────────────────────────────────────────────────────────────
// ULEPSZENIE WIDOCZNOŚCI LISTBOXA
// ──────────────────────────────────────────────────────────────────────────────
(function improveListboxVisibility() {
const style = document.createElement('style');
style.textContent = `
[role="listbox"] {
display: block !important;
visibility: visible !important;
opacity: 1 !important;
}
`;
document.head.appendChild(style);
})();
})();