您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.
// ==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); })(); })();