您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Ostrzega przy kopiowaniu permalinku, gdy w panelu „Udostępnij lokalizację” zaznaczono „Uwzględnij ustawienia warstw”, i pozwala skopiować link bez warstw (usuwa parametr `s`).
// ==UserScript== // @name WME Permalink Layer Guard // @namespace https://github.com/majormarcin // @version 0.2.0 // @description Ostrzega przy kopiowaniu permalinku, gdy w panelu „Udostępnij lokalizację” zaznaczono „Uwzględnij ustawienia warstw”, i pozwala skopiować link bez warstw (usuwa parametr `s`). // @author TheMiskov (idea) / ChatGPT (kod) // @include https://*.waze.com/*/editor* // @include https://*.waze.com/editor* // @include https://*.waze.com/map-editor* // @include https://*.waze.com/beta_editor* // @run-at document-start // @grant none // @icon https://www.google.com/s2/favicons?sz=64&domain=waze.com // ==/UserScript== (function () { 'use strict'; // --- TEKSTY UI --- const UI_TEXT = { title: 'Masz zaznaczone własne warstwy', body: 'W panelu „Udostępnij lokalizację” zaznaczono opcję „Uwzględnij ustawienia warstw”. Skopiować permalink bez warstw?', cancel: 'Anuluj', proceed: 'Skopiuj bez warstw', }; // --- NARZĘDZIA --- const isPolish = true; // tylko podpowiedź dla ewentualnych selektorów tekstowych function isWMEPermalink(urlStr) { try { const u = new URL(urlStr); const hostOk = /(^|\.)waze\.com$/i.test(u.hostname.replace(/^www\./, '')); const pathOk = /\/editor(\/|$)/.test(u.pathname); return hostOk && pathOk; } catch (e) { return false; } } function hasLayerParam(urlStr) { try { const u = new URL(urlStr); return u.searchParams.has('s'); } catch (e) { return /[?&]s=/.test(urlStr); } } function stripLayerParam(urlStr) { try { const u = new URL(urlStr); u.searchParams.delete('s'); let out = u.toString(); out = out.replace(/\?$/, ''); return out; } catch (e) { return urlStr .replace(/([?&])s=[^&#]*&?/, '$1') .replace(/\?&/, '?') .replace(/\?$/, ''); } } // --- MODAL --- function ensureStyles() { if (document.getElementById('wme-pl-layer-guard-style')) return; const style = document.createElement('style'); style.id = 'wme-pl-layer-guard-style'; style.textContent = ` .wmePLG-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; z-index: 100000; } .wmePLG-modal { background: #fff; color: #111; border-radius: 14px; width: min(520px, 92vw); box-shadow: 0 10px 30px rgba(0,0,0,.25); overflow: hidden; font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; } .wmePLG-header { padding: 14px 18px; font-weight: 700; font-size: 17px; background: #f3f4f6; border-bottom: 1px solid #e5e7eb; } .wmePLG-body { padding: 16px 18px; font-size: 14px; line-height: 1.4; } .wmePLG-actions { display: flex; gap: 10px; padding: 12px 18px 18px; justify-content: flex-end; } .wmePLG-btn { border: 0; border-radius: 10px; padding: 10px 14px; font-size: 14px; cursor: pointer; } .wmePLG-cancel { background: #eef2ff; color: #1f2937; } .wmePLG-proceed { background: linear-gradient(135deg, #22d3ee, #a78bfa, #f472b6); color: #fff; font-weight: 700; } `; document.documentElement.appendChild(style); } function askUser() { return new Promise((resolve, reject) => { ensureStyles(); const backdrop = document.createElement('div'); backdrop.className = 'wmePLG-backdrop'; const modal = document.createElement('div'); modal.className = 'wmePLG-modal'; modal.innerHTML = ` <div class="wmePLG-header">${UI_TEXT.title}</div> <div class="wmePLG-body">${UI_TEXT.body}</div> <div class="wmePLG-actions"> <button class="wmePLG-btn wmePLG-cancel">${UI_TEXT.cancel}</button> <button class="wmePLG-btn wmePLG-proceed">${UI_TEXT.proceed}</button> </div>`; backdrop.appendChild(modal); document.body.appendChild(backdrop); function cleanup() { backdrop.remove(); } modal.querySelector('.wmePLG-cancel').addEventListener('click', () => { cleanup(); reject(new Error('User cancelled')); }); modal.querySelector('.wmePLG-proceed').addEventListener('click', () => { cleanup(); resolve(true); }); backdrop.addEventListener('click', (e) => { if (e.target === backdrop) { cleanup(); reject(new Error('User cancelled')); } }); }); } // --- WYSZUKIWANIE PANELU „UDOSTĘPNIJ LOKALIZACJĘ” --- function findSharePanel(root = document) { // Szukamy kontenera zawierającego tekst checkboxa const marker = Array.from(root.querySelectorAll('label, div, span, p')) .find(el => /Uwzględnij ustawienia warstw/i.test(el.textContent || '')); if (!marker) return null; // panel = najbliższy dialog/panel let panel = marker.closest('[role="dialog"], [data-testid*="dialog"], .modal, .mat-dialog-container, .mdc-dialog, .waze-dialog, .share-dialog'); if (!panel) panel = marker.closest('div'); return panel || null; } function getPanelParts(panel) { if (!panel) return {}; // Checkbox const checkbox = panel.querySelector('input[type="checkbox"]'); // Pole permalinku (input/textarea) — wybieramy pierwsze w panelu z wartością zaczynającą się od https let linkInput = Array.from(panel.querySelectorAll('input[type="text"], input[type="url"], textarea')) .find(el => /^https?:\/\//.test(el.value || '')) || null; // Przycisk(i) kopiowania w panelu const copyBtns = Array.from(panel.querySelectorAll('button, a, div[role="button"]')).filter(el => { const t = (el.innerText || el.getAttribute('aria-label') || el.title || '').toLowerCase(); return /(copy|kopiuj)/.test(t) || /fa-copy|icon-copy|copy/i.test(el.className || ''); }); return { checkbox, linkInput, copyBtns }; } async function handleShareCopy(panel, evTarget) { const { checkbox, linkInput } = getPanelParts(panel); // jeśli nie znaleziono checkboxa lub inputu, lecimy starym trybem przez schowek let url = linkInput?.value; let checkboxChecked = !!checkbox?.checked; // Jeżeli link jeszcze nie jest w polu (czasem WME wypełnia po kliknięciu), daj mu ułamek sekundy if (!url) { await new Promise(r => setTimeout(r, 50)); if (linkInput) url = linkInput.value; } // Jeżeli checkbox zaznaczony i/lub w linku jest `s=`, pytamy użytkownika const suspect = checkboxChecked || (typeof url === 'string' && hasLayerParam(url)); if (suspect) { // blokujemy domyślne kopiowanie WME if (evTarget) { try { evTarget.preventDefault?.(); evTarget.stopPropagation?.(); evTarget.stopImmediatePropagation?.(); } catch {} } try { await askUser(); // Mamy akcept — budujemy końcowy link let finalUrl = url; if (!finalUrl) { // fallback: odczyt ze schowka (jeśli kliknięcie zdążyło skopiować) try { finalUrl = await navigator.clipboard.readText(); } catch {} } if (finalUrl && isWMEPermalink(finalUrl)) { finalUrl = stripLayerParam(finalUrl); await navigator.clipboard.writeText(finalUrl); } return true; // obsłużone } catch (e) { // Anulowano — nie kopiujemy nic return true; // też traktujemy jako obsłużone, żeby WME nie robił nic dalej } } return false; // nie ingerujemy } // --- GŁÓWNY OBSERWATOR UI --- function hookSharePanel() { const seen = new WeakSet(); const observe = new MutationObserver(() => { const panel = findSharePanel(document); if (!panel || seen.has(panel)) return; seen.add(panel); const { copyBtns } = getPanelParts(panel); copyBtns.forEach(btn => { if (btn._wmePLG_hooked) return; btn._wmePLG_hooked = true; btn.addEventListener('click', async (ev) => { const handled = await handleShareCopy(panel, ev); if (handled) return; // zrobiliśmy swoje }, true); }); }); observe.observe(document.documentElement, { childList: true, subtree: true }); } // --- DODATKOWE BEZPIECZNIKI (poza panelem) --- function wrapClipboardWriteText() { if (!navigator.clipboard || !navigator.clipboard.writeText) return; const orig = navigator.clipboard.writeText.bind(navigator.clipboard); navigator.clipboard.writeText = async function (text) { try { if (typeof text === 'string' && isWMEPermalink(text) && hasLayerParam(text)) { await askUser(); const cleaned = stripLayerParam(text); return orig(cleaned); } } catch {} return orig(text); }; } function interceptCopyEvent() { document.addEventListener('copy', async function (e) { try { const sel = document.getSelection(); const clipText = e.clipboardData?.getData('text/plain') || sel?.toString() || ''; if (clipText && isWMEPermalink(clipText) && hasLayerParam(clipText)) { e.preventDefault(); try { await askUser(); const cleaned = stripLayerParam(clipText); e.clipboardData.setData('text/plain', cleaned); } catch {} } } catch {} }, true); } function init() { hookSharePanel(); wrapClipboardWriteText(); interceptCopyEvent(); console.log('[WME PL Guard] aktywny'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();