WME Rapid UR Reply

Shortcut keys to answer URs with the F8 key and closes them with the F9 -- also, F8 can approve PURs and lock at L3.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         WME Rapid UR Reply
// @description  Shortcut keys to answer URs with the F8 key and closes them with the F9 -- also, F8 can approve PURs and lock at L3.
// @author       TxAgBQ
// @version      2025120902
// @namespace    <https://greasyfork.org/en/users/820296-txagbq/>
// @icon         <https://www.google.com/s2/favicons?sz=64&domain=waze.com>
// @match        https://*.waze.com/*/editor*
// @match        https://*.waze.com/editor*
// @exclude      https://*.waze.com/user/editor*
// @require      https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
// @grant        none
// ==/UserScript==

/* global W */

(function() {
    'use strict';

    function insertCommentViaShadowDOM(textToInsert) {
        const textareaHosts = document.querySelectorAll('wz-textarea');
        for (const host of textareaHosts) {
            const shadow = host.shadowRoot;
            if (!shadow) continue;

            const textarea = shadow.querySelector('textarea');
            if (!textarea) continue;

            textarea.focus();
            textarea.value = textToInsert;
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            return true;
        }
        return false;
    }

    function clickSendButton() {
        // New method: Use more specific path to find the send button
        const sendBtnHost = document.querySelector('div.conversation-view form.new-comment-form wz-button.send-button');
        if (sendBtnHost && sendBtnHost.shadowRoot) {
            const actualButton = sendBtnHost.shadowRoot.querySelector('button');
            if (actualButton) {
                actualButton.click();
                return true;
            }
        }
        
        // Alternative: Try within conversation context
        const conversationView = document.querySelector('div.conversation-view');
        if (conversationView) {
            const sendBtn = conversationView.querySelector('form.new-comment-form wz-button.send-button');
            if (sendBtn && sendBtn.shadowRoot) {
                const actualButton = sendBtn.shadowRoot.querySelector('button');
                if (actualButton) {
                    actualButton.click();
                    return true;
                }
            }
        }
        
        // Fallback to old selector in case the UI varies
        const sendBtn = document.querySelector("#panel-container .mapUpdateRequest .body .conversation .new-comment-form .send-button");
        if (sendBtn) {
            sendBtn.click();
            return true;
        }
        
        return false;
    }

    function clickNextButton() {
        console.log('clickNextButton called');
        // Try specific path first
        const nextBtn = document.querySelector('div.footer--d1gls.actions div.navigation div.waze-plain-btn.next');
        if (nextBtn) {
            console.log('Clicking Next button (new selector)');
            nextBtn.click();
            return true;
        }
        
        // Fallback to old jQuery selector only if first method failed
        const $nextBtn = $('#panel-container .mapUpdateRequest .actions .navigation .waze-plain-btn');
        if ($nextBtn.length > 0) {
            console.log('Clicking Next button (old jQuery selector)');
            $nextBtn.click();
            return true;
        }
        
        console.log('No Next button found');
        return false;
    }

    function sendAndNext() {
        // Clicks send
        clickSendButton();

        // Clicks Next (for open URs)
        setTimeout(() => {
            clickNextButton();
        }, 100);
    }

    document.addEventListener('keydown', (event) => {
        if (event.key === 'F8') {
            // Inserts this text
            //let textToInsert = "I got your map issue report.  What issue did you experience? \n\nWould you please tell us your destination EXACTLY as selected in Waze? Click on 'Where to?' then scroll down to see recent destinations.  Include both the bold name and address underneath, with city. If it's spelled out, spell it out. If it's abbreviated, abbreviate it.  How to find your recent destination: <https://www.youtube.com/watch?v=PExNTIQfJI8>  \n\nWe don't receive email replies so please respond in the Waze inbox.";

            // First insert the text (if line is not commented out)
            if (typeof textToInsert !== 'undefined') {
                insertCommentViaShadowDOM(textToInsert);
            }

            // Always send and move to next after a short delay (whether text was inserted or not)
            setTimeout(sendAndNext, 250);

            // Approves PUR then sets to lock level 2
            // Click Add and edit
            setTimeout(() => {
                $('#panel-container .place-update-edit .actions .controls-container label[for="approved-true"]').click();
                // Clicks Next (for open PURs) - Commented out because sendAndNext already clicks Next
                // setTimeout(() => {
                //     clickNextButton();
                // }, 100);
            }, 500);
        }

        if (event.key === 'F9') {
            // Inserts this text
            // Comment to close out report
            let textToInsert = "You asked us to help you with a Map Issue report but you didn't respond back with the info we need to fix your problem, so we'll infer everything is okay and close your request. \n\nIf you were reporting a closure, the quickest way to get closures on the map is to use the Report > Closure feature built into the Waze. <https://support.google.com/waze/answer/13753511> \n\nIf you believe Waze should automatically detect closures on its own, rather than routing you into them, you can vote here <https://waze.uservoice.com/forums/59223-waze-suggestion-box/suggestions/47051794-autodetect-closures-and-closed-roads>";

            if (insertCommentViaShadowDOM(textToInsert)) {
                setTimeout(() => {
                    // Clicks send
                    clickSendButton();

                    // Clicks NI
                    setTimeout(() => {
                        // New method: Use specific path to find the NI label
                        let niClicked = false;
                        const niLabel = document.querySelector('div.footer--d1gls.actions form.controls-container label[for="state-not-identified"]');
                        if (niLabel) {
                            niLabel.click();
                            niClicked = true;
                        }
                        
                        // Fallback to old selector only if not clicked yet
                        if (!niClicked) {
                            const $niLabel = $('#panel-container .mapUpdateRequest .actions .controls-container label[for|="state-not-identified"]');
                            if ($niLabel.length > 0) {
                                $niLabel.click();
                            }
                        }

                        // Clicks Next (for open URs)
                        setTimeout(() => {
                            clickNextButton();
                        }, 100);
                    }, 100);
                }, 250);
            }
        }
    });
})();