您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Quick House Numbers
当前为
// ==UserScript== // @name WME Quick HN (DaveAcincy fork) // @description Quick House Numbers // @version 2025.03.23.01 // @author Vinkoy (forked by DaveAcincy) // @match https://beta.waze.com/*editor* // @match https://www.waze.com/*editor* // @exclude https://www.waze.com/*user/*editor/* // @exclude https://www.waze.com/discuss/* // @namespace https://greasyfork.org/users/166713 // @homepage https://www.waze.com/discuss/t/script-wme-quick-hn-daveacincy-fork/327021 // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js // @grant GM.addStyle // @grant unsafeWindow // ==/UserScript== /* global W */ /* global I18n */ /* global WazeWrap */ (function () { const debug = false; const scriptName = 'Quick HN'; const scriptId = 'wmeqhn'; let policySafeHTML; let wazeMapObserver; let lastHN; let nextHNs; let interval = 1; let modeMultiplier = 1; let fillnext = false; let { autoSetHN = false, zoomKeys = false, custom = 4 } = JSON.parse(localStorage[scriptId] ?? '{}'); let wmeSDK; unsafeWindow.SDK_INITIALIZED.then(() => { wmeSDK = getWmeSdk({ scriptId, scriptName }); wmeSDK.Events.once({ eventName: 'wme-ready' }).then(async () => { for (let initCount = 1; initCount <= 100; initCount++) { if (WazeWrap?.Ready) return initialiseQHN(); else if (initCount === 1) console.log('Quick HN: Waiting for WazeWrap...'); await new Promise(r => setTimeout(r, 300)); } console.error('Quick HN: WazeWrap loading failed. Giving up.'); }); }); function tlog(message, data = '') { if (!debug) return; const t = new Date; const h = t.getHours(); const m = t.getMinutes(); const s = t.getSeconds(); const ms = `${t.getMilliseconds()}`.padStart(3, '0'); console.log(`QHN: ${h}:${m}:${s}.${ms}: ${message}`, data); } function createShortcut(shortcutId, description, callback, shortcutKeys) { // SDK shortcuts for when that's fixed // wmeSDK.Shortcuts.createShortcut({ callback, description, shortcutId, shortcutKeys }); I18n.translations[wmeSDK.Settings.getLocale().localeCode].keyboard_shortcuts.groups[scriptId].members[shortcutId] = description; W.accelerators.addAction(shortcutId, { group: scriptId }); W.accelerators.events.register(shortcutId, null, callback); W.accelerators._registerShortcuts({ [shortcutKeys]: shortcutId }); } function saveQHNOptions() { localStorage[scriptId] = JSON.stringify({ autoSetHN, zoomKeys, custom }); } function initialiseQHN() { if (typeof trustedTypes !== 'undefined') { policySafeHTML = trustedTypes.createPolicy('policySafeHTML', { createHTML: innerText => innerText }); } W.accelerators.Groups[scriptId] = { members: [] }; I18n.translations[wmeSDK.Settings.getLocale().localeCode].keyboard_shortcuts.groups[scriptId] = { description: scriptName, members: {} }; createShortcut('WME_QHN_newHN01', "Insert next sequential house number", () => addOrZoom(1), 't'); createShortcut('WME_QHN_newHN02', "Insert every 2nd house number", () => addOrZoom(2), 'r'); createShortcut('WME_QHN_newHNcustom', "Insert house number with custom interval", () => addOrZoom(custom), 'e'); for (let key = 1; key <= 10; key++) createShortcut(`WME_QHN_newHN${key}`, `Insert house number ±${key}, or zoom to level ${key + 10}`, () => addOrZoom(key, key + 10), key % 10); GM.addStyle('.qhn-panel { color: var(--content_p1); }'); wmeSDK.Sidebar.registerScriptTab().then(({ tabLabel, tabPane }) => { tabLabel.id = scriptId; tabLabel.innerText = scriptName; tabLabel.title = `${scriptName} Settings`; tabPane.innerHTML = ((text) => policySafeHTML ? policySafeHTML.createHTML(text) : text)(` <div class="qhn-panel"><div><b>Quick House Numbers</b> v${GM_info.script.version}</div><br/> <div><input type='checkbox' id='qhnAutoSetHNCheckbox' name='qhnAutoSetHNCheckbox' title="When enabled, auto set next HN updates the last HN based on the last HN moved" ${autoSetHN ? 'checked' : ''}> <label for='qhnAutoSetHNCheckbox'>Auto set next HN on moved HN</label></div> <div><input type='checkbox' id='qhnZoomKeysCheckbox' name='qhnZoomKeysCheckbox' title="1-9 => Z11-19; 0 => Z20" ${zoomKeys ? 'checked' : ''}> <label for='qhnZoomKeysCheckbox'>Zoom Keys when no segment</label></div> <div>Custom interval (E): <input type='number' id='qhnCustomInput' min='1' value='${custom}' style='width: 50px;'></div><br/> <div>Mode: <button name='qhnModeToggle' id='qhnModeToggle'>Increment ↑</button></div><br/> <div id="qhnTabPane"></div></div>`); document.querySelector('#qhnAutoSetHNCheckbox').addEventListener('change', (e) => { autoSetHN = e.target.checked; saveQHNOptions(); }); document.querySelector('#qhnZoomKeysCheckbox').addEventListener('change', (e) => { zoomKeys = e.target.checked; saveQHNOptions(); updateTabPane(); }); document.querySelector('#qhnCustomInput').addEventListener('change', (e) => { custom = e.target.value; e.target.blur(); saveQHNOptions(); updateNextHNs(); }); document.querySelector('#qhnModeToggle').addEventListener('click', (e) => { modeMultiplier *= -1; e.target.innerHTML = (modeMultiplier > 0 ? 'Increment ↑' : 'Decrement ↓'); e.target.blur(); updateNextHNs(); }); WazeWrap.Events.register('afteraction', null, hnActionCheck); updateNextHNs(); }); wazeMapObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.className === 'house-number is-active') { const hnInput = node.querySelector('input'); if (hnInput) hnInput.onfocus = () => setHN(); } } } }); wmeSDK.Events.on({ eventName: 'wme-selection-changed', eventHandler: () => { if (wmeSDK.Editing.getSelection()?.objectType === 'segment') wazeMapObserver.observe(document.querySelector('#WazeMap'), { childList: true, subtree: true }); else wazeMapObserver.disconnect(); updateTabPane(); } }); console.log("Quick HN: initialize complete"); } // Watches changes for new/moved HNs and updates lastHN function hnActionCheck() { const lastAction = W.model.actionManager.getActions().at(-1); const actionHN = lastAction?.houseNumber?.getAttribute('number'); if (actionHN && (lastAction.actionName === 'ADD_HOUSE_NUMBER' || (lastAction.actionName === 'MOVE_HOUSE_NUMBER' && autoSetHN))) { tlog(`action: ${actionHN}`, lastAction.houseNumber); lastHN = actionHN; updateNextHNs(); } } function addOrZoom(newInterval, zoom) { if (!newInterval) return; if (wmeSDK.Editing.getSelection()?.objectType == 'segment') { interval = Number(newInterval); fillnext = true; tlog('setFocus'); document.querySelector('wz-button:has(.w-icon-home)').click(); document.querySelector('wz-navigation-item[selected="false"] i.w-icon-script').click(); document.querySelector(`#${scriptId}`).click(); } else if (zoomKeys && zoom) wmeSDK.Map.setZoomLevel( { zoomLevel: zoom } ); } async function setHN() { tlog('setHN'); const hnInput = document.querySelector('div.house-number.is-active input:placeholder-shown'); if (!fillnext || !hnInput) return; fillnext = false; // React hack: https://github.com/facebook/react/issues/11488#issuecomment-884790146 hnInput.value = nextHNs[interval][0]; hnInput._valueTracker?.setValue(""); hnInput.dispatchEvent(new Event("input", { bubbles: true })); await new Promise(r => setTimeout(r, 100)); hnInput.blur(); } function updateNextHNs() { nextHNs = {}; for (const interval of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, custom]) { nextHNs[interval] = new Array(3); let baseHN = lastHN ?? '0'; for (let index = 0; index < nextHNs[interval].length; index++) { const nextParts = baseHN.match(/[0-9]+|[a-z]|[A-Z]|\S/g); let thisInterval = interval; for (const [index, part] of nextParts.reverse().entries()) { if (!Number.isNaN(Number(part))) { nextParts[index] = Math.max(1, Number(part) + (thisInterval * modeMultiplier)).toString().padStart(part.length, '0'); break; } if (/[a-z]/i.test(part)) { let nextLetter = part.codePointAt(0) + ((thisInterval % 26) * modeMultiplier); thisInterval = Math.floor(thisInterval / 26); if ((/[a-z]/.test(part) && nextLetter > 'z'.codePointAt(0)) || (/[A-Z]/.test(part) && nextLetter > 'Z'.codePointAt(0))) { nextLetter -= 26; thisInterval++; } if ((/[a-z]/.test(part) && nextLetter < 'a'.codePointAt(0)) || (/[A-Z]/.test(part) && nextLetter < 'A'.codePointAt(0))) { nextLetter += 26; thisInterval++; } nextParts[index] = String.fromCodePoint(nextLetter); if (!thisInterval) break; } } baseHN = nextParts.reverse().join(''); nextHNs[interval][index] = baseHN; } } updateTabPane(); } function updateTabPane() { document.querySelector('#qhnTabPane').innerHTML = lastHN ? `<div>Last house number: <b>${lastHN}</b></div><br/><div>Press...` + [['T', 1], ['R', 2], ['E', custom], ...[...Array(10).keys()].map(key => [(key + 1) % 10, key + 1])].reduce((list, [key, interval]) => `${list}<br/><b>${key}</b> ${zoomKeys && Number.isInteger(key) && wmeSDK.Editing.getSelection()?.objectType !== 'segment' ? `to zoom to level ${interval + 10}` : `for HN${modeMultiplier > 0 ? "+" : "-"}${interval} <i>(${nextHNs[interval].join(", ")}...)</i>`}` , '') : "Manually set a house number to start using Quick HN"; } })();