Greasy Fork

Greasy Fork is available in English.

Taiwan Traffic Violation Auto-Filler

This script automatically fills in the reporter's personal information on various traffic violation reporting websites in Taiwan. After downloading, simply complete the data in the profile section of the script, and it will auto-fill your information on each website you visit.

// ==UserScript==
// @name:zh-tw      臺灣交通違規檢舉自動輸入助手
// @name            Taiwan Traffic Violation Auto-Filler
// @namespace       com.sherryyue.TrafficFineReportAssist
// @version         1.2
// @description:zh-tw     此腳本能自動填寫臺灣各交通違規檢舉網站上的檢舉人個資。下載後,只需在腳本內的 profile 部分填齊資料,每次訪問網站時,它都會自動幫你填入個資。
// @description     This script automatically fills in the reporter's personal information on various traffic violation reporting websites in Taiwan. After downloading, simply complete the data in the profile section of the script, and it will auto-fill your information on each website you visit.
// @run-at document-end
// @author          SherryYue
// @copyright       SherryYue
// @license         MIT
// @match           *://wos.hpb.gov.tw/*
// @match           *://www.thb.gov.tw/*
// @match           *://polcar.moenv.gov.tw/*
// @match           *://tvrs.ntpd.gov.tw/*
// @match           *://prsweb.tcpd.gov.tw/*
// @match           *://tvrweb.typd.gov.tw:3444/*
// @match           *://traffic.hchpb.gov.tw/*
// @match           *://tra2.hccp.gov.tw/*
// @match           *://trv.mpb.gov.tw/*
// @match           *://suggest.police.taichung.gov.tw/*
// @match           *://jiaowei.ncpd.gov.tw/sc11/*
// @match           *://traffic.chpb.gov.tw/*
// @match           *://trv.ylhpb.gov.tw/*
// @match           *://tptv.klg.gov.tw/*
// @match           *://www.cypd.gov.tw/*
// @match           *://www.ccpb.gov.tw/*
// @match           *://tr.tnpd.gov.tw/*
// @match           *://trafficmailbox.ptpolice.gov.tw/*
// @match           *://ppl.report.ilcpb.gov.tw/*
// @match           *://hlpb.twgov.mobi/*
// @match           *://www.ttcpb.gov.tw/*
// @match           *://tr.ccpb.gov.tw/*
// @match           *://www.phpb.gov.tw/*
// @supportURL      [email protected]
// @icon            https://sherryyuechiu.github.io/card/images/logo/maskable_icon_x96.png
// @supportURL      https://github.com/sherryyuechiu/GreasyMonkeyScripts/issues
// @homepage        https://github.com/sherryyuechiu/GreasyMonkeyScripts
// @require         http://greasyfork.icu/scripts/383527-wait-for-key-elements/code/Wait_for_key_elements.js?version=701631
// @grant           none
// ==/UserScript==
(function () {
    'use strict';
    let GENDER;
    (function (GENDER) {
        GENDER[GENDER["MALE"] = 1] = "MALE";
        GENDER[GENDER["FEMALE"] = 2] = "FEMALE";
    })(GENDER || (GENDER = {}));
    const CITY_CONFIG = {
        GUO_DAO: {
            host: 'wos.hpb.gov.tw',
            handler: guoDao,
            violation: {
                select: '#ViolativeCategoryLevel1',
                input: '#ViolationContent'
            }
        },
        GONG_LU: {
            host: 'www.thb.gov.tw',
            handler: gongluZongJu,
            violation: {
                select: '',
                input: '#ContentPlaceHolder1_c_33'
            }
        },
        WU_ZE: {
            host: 'polcar.moenv.gov.tw',
            handler: wuZeChe
        },
        JI_LONG: {
            host: 'tptv.klg.gov.tw',
            handler: jiLong,
            violation: {
                select: '#IllegalContentList',
                input: '#IllegalContent'
            }
        },
        XIN_BEI: {
            host: 'tvrs.ntpd.gov.tw',
            handler: xinBei,
            violation: {
                select: '#eventsData_vio_content_code',
                input: '#eventsData_vio_content_memo'
            }
        },
        TAI_BEI: {
            host: 'prsweb.tcpd.gov.tw',
            handler: taiBei,
            violation: {
                select: '#input-156',
                input: '#sRuldec'
            }
        },
        TAO_YUAN: {
            host: 'tvrweb.typd.gov.tw:3444',
            handler: taoYuan
        },
        XIN_ZHU_XIAN: {
            host: 'traffic.hchpb.gov.tw',
            handler: xinZhuXian
        },
        XIN_ZHU_SHI: {
            host: 'tra2.hccp.gov.tw',
            handler: xinZhuShi
        },
        MIAO_LI: {
            host: 'trv.mpb.gov.tw',
            handler: miaoLi,
            violation: {
                select: '#ViolationId',
                input: '#ViolationRemark'
            }
        },
        TAI_ZHONG: {
            host: 'suggest.police.taichung.gov.tw',
            handler: taichung,
            violation: {
                select: '#qclass',
                input: '#detailcontent'
            }
        },
        NAN_TOU: {
            host: 'jiaowei.ncpd.gov.tw',
            handler: nanTou,
            violation: {
                select: '#rlid',
                input: '#mcarblack'
            }
        },
        ZHANG_HUA: {
            host: 'traffic.chpb.gov.tw',
            handler: zhanghua,
            violation: {
                select: '',
                input: '#ViolationDescription'
            }
        },
        YUN_LIN: {
            host: 'trv.ylhpb.gov.tw',
            handler: yunLin,
            violation: {
                select: '#ViolationId',
                input: '#ViolationRemark'
            }
        },
        JIA_YI_XIAN: {
            host: 'www.cypd.gov.tw',
            handler: jiaYiXian,
            violation: {
                select: '#TrafficCategoryId',
                input: '#Memo'
            }
        },
        JIA_YI_SHI: {
            host: 'tr.ccpb.gov.tw',
            handler: jiaYiShi,
            violation: {
                select: '',
                input: '[data-bind="value:obsData.formData.CF21O_field1,canedit:modifiable"]'
            }
        },
        TAI_NAN: {
            host: 'tr.tnpd.gov.tw',
            handler: taiNan
        },
        PING_DONG: {
            host: 'trafficmailbox.ptpolice.gov.tw',
            handler: pingDong,
            violation: {
                select: '#qclass',
                input: '#detailcontent'
            }
        },
        YI_LAN: {
            host: 'ppl.report.ilcpb.gov.tw',
            handler: yiLan,
            violation: {
                select: '#select2-legislation44-container',
                input: '#content22'
            }
        },
        HUA_LIAN: {
            host: 'hlpb.twgov.mobi',
            handler: huaLian,
            violation: {
                select: '',
                input: '#20221205203006'
            }
        },
        TAI_DONG: {
            host: 'www.ttcpb.gov.tw',
            handler: taiDong,
            violation: {
                select: '#subject',
                input: '#content1'
            }
        },
        PENG_HU: {
            host: 'www.phpb.gov.tw',
            handler: pengHu
        }
    };
    let profile = {
        fullName: localStorage.getItem('profile_fullName') || '',
        gender: Number(localStorage.getItem('profile_gender')) || GENDER.FEMALE,
        id: localStorage.getItem('profile_id') || '',
        addr: localStorage.getItem('profile_addr') || '',
        tel: localStorage.getItem('profile_tel') || '',
        mail: localStorage.getItem('profile_mail') || '',
    };
    function setupViolationHandlers() {
        const currentHost = location.host;
        const cityConfig = Object.values(CITY_CONFIG).find(city => city.host === currentHost);
        if (cityConfig?.violation) {
            let input = null;
            // 特殊處理花蓮的備註欄位
            if (currentHost === 'hlpb.twgov.mobi') {
                input = document.getElementById('20221205203006');
            }
            else {
                input = document.querySelector(cityConfig.violation.input);
            }
            if (input) {
                // 如果有下拉選單且選擇器不為空,則設置事件監聽
                if (cityConfig.violation.select) {
                    const select = document.querySelector(cityConfig.violation.select);
                    if (select) {
                        // 監聽選擇變更
                        select.addEventListener('change', () => {
                            if (currentHost === 'tvrs.ntpd.gov.tw' ||
                                currentHost === 'trv.mpb.gov.tw' ||
                                currentHost === 'jiaowei.ncpd.gov.tw' ||
                                currentHost === 'trv.ylhpb.gov.tw' ||
                                currentHost === 'www.cypd.gov.tw' ||
                                currentHost === 'ppl.report.ilcpb.gov.tw' ||
                                currentHost === 'www.ttcpb.gov.tw') {
                                // 新北、苗栗、南投、雲林、嘉義縣、宜蘭和台東特殊處理:使用 option textContent
                                let value = '';
                                if (currentHost === 'www.ttcpb.gov.tw') {
                                    // 台東特殊處理:直接獲取選中選項的文字內容
                                    const selectedOption = select.options[select.selectedIndex];
                                    value = selectedOption.textContent || '';
                                }
                                else {
                                    const selectedOption = select.options[select.selectedIndex];
                                    value = selectedOption.textContent || '';
                                }
                                fillField(input, value);
                            }
                            else {
                                // 其他城市處理
                                const value = select.value;
                                fillField(input, value);
                            }
                        });
                    }
                }
                // 添加右鍵選單
                input.addEventListener('contextmenu', (event) => {
                    event.preventDefault();
                    createContextMenu(event.pageX, event.pageY, menuOptions);
                });
            }
        }
    }
    function handleCity() {
        const currentHost = location.host;
        const cityConfig = Object.values(CITY_CONFIG).find(city => city.host === currentHost);
        if (cityConfig) {
            cityConfig.handler();
            setupViolationHandlers();
        }
    }
    const menuOptions = {
        '燈光': ['闖紅燈', '轉彎不打燈', '變換車道不打燈', '靠邊停車不打燈', '起步不打燈', '紅燈迴轉', '不依規定使用燈光'],
        '違停': ['逆向臨停', '併排臨停', '路口停車', '網狀線臨停'],
        '逆向': ['逆向', '逆向臨停'],
        '標線': ['跨越雙黃、白線', '超出停止線', '在左右轉專用道直行', '在直行專用道轉彎', '轉彎不先駛入內外車道', '行駛槽化線', '網狀線臨停'],
        '其他': ['玩手機', '不停讓行人', '不依規定佩戴安全帽', '行駛人行道'],
    };
    const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    const scrollToBottom = async (scrollTarget) => {
        // @ts-ignore
        (scrollTarget || window).scrollTo(0, (scrollTarget || document.body).scrollHeight);
        await sleep(500); // 增加等待時間到 500ms
        // @ts-ignore
        (scrollTarget || window).scrollTo(0, (scrollTarget || document.body).scrollHeight);
        await sleep(500); // 再次等待 500ms
        // @ts-ignore
        (scrollTarget || window).scrollTo(0, (scrollTarget || document.body).scrollHeight);
    };
    function fillField(field, value) {
        if (field) {
            field.value = value;
            // 發送輸入事件
            field.dispatchEvent(new Event('input', { bubbles: true }));
            field.dispatchEvent(new Event('change', { bubbles: true }));
        }
    }
    async function guoDao() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/RV') {
            field.disclaimerRead = document.querySelector('#chkAgree');
            await sleep(200);
            if (field.disclaimerRead)
                field.disclaimerRead.checked = true;
            // 滾動 .info 節點到底部
            const infoElement = document.querySelector('.info');
            if (infoElement) {
                await scrollToBottom(infoElement);
            }
        }
        else if (urlPathName === '/RV/Create') {
            field.fullName = document.querySelector('input[name="ApplicantName"]');
            field.id = document.querySelector('input[name="ApplicantIDNo"]');
            field.tel = document.querySelector('input[name="ApplicantTel"]');
            field.addr = document.querySelector('input[name="ApplicantAddress"]');
            field.mail = document.querySelector('input[name="ApplicantEMail"]');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function gongluZongJu() {
        try {
            let field = {};
            const urlPathName = location.pathname;
            if (urlPathName === '/Message_CarViolation.aspx') {
                field.mail = document.querySelector('#ContentPlaceHolder1_emailVerificationCode_txtMail');
                field.fullName = document.querySelector('#ContentPlaceHolder1_c_29');
                field.id = document.querySelector('#ContentPlaceHolder1_c_30');
                field.tel = document.querySelector('#ContentPlaceHolder1_c_31');
                const verifyCodeInput = document.querySelector('#ContentPlaceHolder1_emailVerificationCode_txtVerCode');
                fillField(field.mail, profile.mail);
                fillField(field.fullName, profile.fullName);
                fillField(field.id, profile.id);
                fillField(field.tel, profile.tel);
                const verifyCode = localStorage.getItem('verifyCode');
                if (verifyCode && verifyCodeInput)
                    verifyCodeInput.value = verifyCode;
                verifyCodeInput?.addEventListener('change', () => {
                    localStorage.setItem('verifyCode', verifyCodeInput.value);
                });
                // 添加備註欄位的右鍵選單
                const remarkField = document.querySelector('#ContentPlaceHolder1_c_33');
                if (remarkField) {
                    remarkField.addEventListener('contextmenu', function (event) {
                        event.preventDefault();
                        createContextMenu(event.pageX, event.pageY, menuOptions);
                    });
                }
            }
        }
        catch (err) {
            console.warn(err);
        }
    }
    function wuZeChe() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/case/Step0.aspx') {
            field.disclaimerRead = document.querySelector('#ctl00_cphBody_rblIsAgree_0');
            if (field.disclaimerRead)
                field.disclaimerRead.click();
            scrollToBottom();
        }
        else if (urlPathName === '/case/Step1.aspx') {
            field.fullName = document.querySelector('input#ctl00_cphBody_tbxAppName');
            field.id = document.querySelector('input#ctl00_cphBody_tbxAppID');
            field.tel = document.querySelector('input#ctl00_cphBody_tbxAppTel3');
            field.addr = document.querySelector('input#ctl00_cphBody_tbxAppAdd');
            field.mail = document.querySelector('input#ctl00_cphBody_tbxAppEmail');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function jiLong() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/reportcase/index.aspx') {
            field.disclaimerRead = document.querySelector('#CheckBox1');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
            }
            scrollToBottom();
        }
        else if (urlPathName === '/reportcase/ReportIndex.aspx') {
            field.fullName = document.querySelector('#ReportName');
            field.id = document.querySelector('#ReportCreditID');
            field.tel = document.querySelector('#ReportMobile');
            field.addr = document.querySelector('#ReportAddress');
            field.mail = document.querySelector('#ReportEmail');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function xinBei() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/Home/Report') {
            field.disclaimerRead = document.querySelector('#ck');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
            }
            scrollToBottom();
        }
        else if (urlPathName === '/Home/Report_Add') {
            field.fullName = document.querySelector('#informerData_informer_name');
            field.id = document.querySelector('#informerData_identity');
            field.tel = document.querySelector('#informerData_Phone');
            field.addr = document.querySelector('#informerData_contact_address');
            field.mail = document.querySelector('#informerData_Email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function taiBei() {
        window.addEventListener('load', async function () {
            let field = {};
            const urlPathName = location.hash;
            if (['#', '#/'].some(_ => _ === urlPathName)) {
                scrollToBottom();
            }
            else if (urlPathName === '#/New') {
                field.fullName = document.querySelector('input[name="sPub_nm"]');
                field.id = document.querySelector('input[name="sPub_id"]');
                field.tel = document.querySelector('input[name="sPubtel"]');
                field.addr = document.querySelector('input[name="sPubadd"]');
                field.mail = document.querySelector('input[name="email"]');
                fillField(field.fullName, profile.fullName);
                fillField(field.id, profile.id);
                fillField(field.tel, profile.tel);
                fillField(field.addr, profile.addr);
                fillField(field.mail, profile.mail);
            }
        }, false);
    }
    function taoYuan() {
        window.addEventListener('load', function () {
            let field = {};
            const urlPathName = location.pathname;
            if (urlPathName == '/') {
                field.disclaimerRead = document.querySelector('#cbox1');
                if (field.disclaimerRead)
                    field.disclaimerRead.checked = true;
                scrollToBottom(document.querySelector('.main-page'));
            }
            else if (urlPathName === '/TTPB/D0101') {
                field.fullName = document.querySelector('input[name="txtName"]');
                field.id = document.querySelector('input[name="txtId"]');
                field.tel = document.querySelector('input[name="txtNum"]');
                field.addr = document.querySelector('input[name="txtAdd"]');
                field.mail = document.querySelector('input[name="txtEmaill"]');
                fillField(field.fullName, profile.fullName);
                fillField(field.id, profile.id);
                fillField(field.tel, profile.tel);
                fillField(field.addr, profile.addr);
                fillField(field.mail, profile.mail);
            }
        }, false);
    }
    function xinZhuXian() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/10/13') {
            field.disclaimerRead = document.querySelector('.agree>input');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
            }
            scrollToBottom();
        }
        else if (urlPathName === '/report') {
            field.fullName = document.querySelector('#name');
            field.id = document.querySelector('#idcard');
            field.tel = document.querySelector('#tel');
            field.addr = document.querySelector('#address2');
            field.mail = document.querySelector('#email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function xinZhuShi() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/new/') {
            field.disclaimerRead = document.querySelector('#agree');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
                scrollToBottom();
            }
        }
        else if (urlPathName === '/new/new.php') {
            field.fullName = document.querySelector('#case_name');
            field.id = document.querySelector('#case_id_number');
            field.tel = document.querySelector('#case_phone');
            field.addr = document.querySelector('#case_contact_address');
            field.mail = document.querySelector('#case_email');
            field.confirmRead = document.querySelector('#case_read_statement');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
            if (field.confirmRead) {
                field.confirmRead.click();
                field.confirmRead.checked = true;
            }
            // @ts-ignore
            waitForKeyElements("[aria-labelledby=select2-case_violated_at_date-container]", () => {
                field.dateTime = document.querySelector('[aria-labelledby=select2-case_violated_at_date-container]');
                if (field.dateTime)
                    field.dateTime.parentNode?.parentNode?.style.setProperty('width', '10em');
                field.dateTime = document.querySelector('[aria-labelledby=select2-case_violated_at_hour-container]');
                if (field.dateTime)
                    field.dateTime.parentNode?.parentNode?.style.setProperty('width', '6em');
                field.dateTime = document.querySelector('[aria-labelledby=select2-case_violated_at_min-container]');
                if (field.dateTime)
                    field.dateTime.parentNode?.parentNode?.style.setProperty('width', '6em');
            });
            // 客製化車牌輸入,可以直接輸入整串
            document.querySelector('#case_first_car_number')?.parentNode?.style.setProperty('display', 'none');
            document.querySelector('#case_last_car_number')?.parentNode?.style.setProperty('display', 'none');
            const customLicenseInput = document.createElement('input');
            customLicenseInput.setAttribute('placeholder', '完整車牌,包含-');
            customLicenseInput.style.setProperty('display', 'block');
            customLicenseInput.style.setProperty('width', 'calc(80%)');
            document.querySelector('#case_first_car_number')?.parentNode?.parentNode?.insertBefore(customLicenseInput, document.querySelector('#case_first_car_number')?.parentNode);
            customLicenseInput.oninput = (() => {
                const licenseInputL = document.querySelector('#case_first_car_number');
                const licenseInputR = document.querySelector('#case_last_car_number');
                const [licenseNumL, licenseNumR] = customLicenseInput.value.split('-');
                if (licenseInputL)
                    licenseInputL.value = licenseNumL || '';
                if (licenseInputR)
                    licenseInputR.value = licenseNumR || '';
            });
        }
    }
    function miaoLi() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/Home/Report') {
            field.fullName = document.querySelector('#Name');
            field.id = document.querySelector('#IdentityNumber');
            field.tel = document.querySelector('#Telphone');
            field.addr = document.querySelector('#Address');
            field.mail = document.querySelector('#Email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function taichung() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/traffic/' || urlPathName === '/traffic/index.jsp') {
            field.disclaimerRead = document.querySelector('#OK');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
                scrollToBottom();
            }
        }
        else if (urlPathName === '/traffic/traffic_write.jsp') {
            const timepickerUnlock = () => {
                field.timepicker = document.querySelector('.ui_tpicker_time_input');
                if (field.timepicker)
                    field.timepicker.removeAttribute('disabled');
            };
            // @ts-ignore
            waitForKeyElements(".ui_tpicker_time_input", timepickerUnlock);
            field.fullName = document.querySelector('#name');
            field.genderMale = document.querySelector('#male');
            field.genderFemale = document.querySelector('#female');
            field.nation = document.querySelector('#taiwan');
            field.id = document.querySelector('#sub');
            field.addr = document.querySelector('#address');
            field.tel = document.querySelector('#liaisontel');
            field.mail = document.querySelector('#email');
            field.actSelect = document.querySelector('#qclass');
            field.dateTime = document.querySelector('#violationdatetime');
            field.detail = document.querySelector('#detailcontent');
            fillField(field.fullName, profile.fullName);
            if (profile.gender === GENDER.FEMALE && field.genderFemale)
                field.genderFemale.click();
            else if (profile.gender === GENDER.MALE && field.genderMale)
                field.genderMale.click();
            if (field.nation)
                field.nation.click();
            fillField(field.id, profile.id);
            fillField(field.addr, profile.addr);
            fillField(field.tel, profile.tel);
            fillField(field.mail, profile.mail);
            if (field.dateTime)
                field.dateTime.removeAttribute('readonly');
            if (field.actSelect) {
                field.actSelect.onchange = () => {
                    if (field.detail)
                        field.detail.value = field.actSelect.value.replace(/^道交[\d-、之第項]+/gi, '');
                };
            }
            document.querySelectorAll('#license1>*').forEach(elm => elm.style?.setProperty('display', 'none'));
            const customLicenseInput = document.createElement('input');
            customLicenseInput.setAttribute('placeholder', '完整車牌,包含-');
            customLicenseInput.style.setProperty('display', 'block');
            customLicenseInput.style.setProperty('width', 'calc(80% - 7px)');
            document.querySelector('#license1')?.insertBefore(customLicenseInput, document.querySelector('#license1 label'));
            customLicenseInput.oninput = (() => {
                const licenseInputL = document.querySelector('#licensenumber2');
                const licenseInputR = document.querySelector('#licensenumber3');
                const [licenseNumL, licenseNumR] = customLicenseInput.value.split('-');
                if (licenseInputL)
                    licenseInputL.value = licenseNumL || '';
                if (licenseInputR)
                    licenseInputR.value = licenseNumR || '';
            });
            if (field.detail) {
                field.detail.addEventListener('contextmenu', function (event) {
                    event.preventDefault();
                    createContextMenu(event.pageX, event.pageY, menuOptions);
                });
            }
        }
    }
    function nanTou() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/sc11/rwd/rincase1.aspx') {
            field.nextBtn = document.querySelector('#Button1');
            if (field.nextBtn)
                field.nextBtn.click();
        }
        else if (urlPathName === '/sc11/rwd/rincase2.aspx') {
            scrollToBottom();
        }
        else if (urlPathName === '/sc11/rwd/rincase3.aspx') {
            field.fullName = document.querySelector('#mname');
            field.id = document.querySelector('#mpid');
            field.tel = document.querySelector('#mtel');
            field.addr = document.querySelector('#maddr');
            field.mail = document.querySelector('#memail');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function zhanghua() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/ViolatePetition/C005400') {
            scrollToBottom();
        }
        else if (urlPathName === '/ViolatePetition/C005400/Form') {
            field.fullName = document.querySelector('#Name');
            field.id = document.querySelector('#IdentityCard');
            field.tel = document.querySelector('#OfficeTel');
            field.addr = document.querySelector('#Address');
            field.mail = document.querySelector('#Mail');
            field.date = document.querySelector('#ViolationDate');
            field.time = document.querySelector('#ViolationTime');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
            if (field.date) {
                field.date.setAttribute('type', 'text');
                field.date.setAttribute('placeholder', 'YYYY-mm-dd');
            }
            if (field.time) {
                field.time.setAttribute('type', 'text');
                field.time.setAttribute('placeholder', 'HH:mm');
            }
        }
    }
    function yunLin() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/Home/Report') {
            field.fullName = document.querySelector('#Name');
            field.id = document.querySelector('#IdentityNumber');
            field.tel = document.querySelector('#Telphone');
            field.addr = document.querySelector('#Address');
            field.mail = document.querySelector('#Email');
            field.date = document.querySelector('[name=SetDateOfOccurrence]');
            field.time = document.querySelector('[name=SetTimeOfOccurrence]');
            field.disclaimerRead = document.querySelector('#che_agree');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
            }
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
            if (field.date) {
                field.date.setAttribute('type', 'text');
                field.date.setAttribute('placeholder', 'YYYY-mm-dd');
            }
            if (field.time) {
                field.time.setAttribute('type', 'text');
                field.time.setAttribute('placeholder', 'HH:mm');
            }
        }
    }
    function jiaYiXian() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName.startsWith('/TrafficMailbox/Create')) {
            field.fullName = document.querySelector('#FromName');
            field.id = document.querySelector('#FromID');
            field.tel = document.querySelector('#ContactPhone');
            field.addr = document.querySelector('#ContactAddress');
            field.mail = document.querySelector('#FromMail');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
        else if (urlPathName.startsWith('/TrafficMailbox/')) {
            field.disclaimerRead = document.querySelector('#checkRead');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
                scrollToBottom();
            }
        }
    }
    function jiaYiShi() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/applicationDoc/Index') {
            field.fullName = document.querySelector('[data-bind="value:obsData.formData.person_name"]');
            field.tel = document.querySelector('[data-bind="value:obsData.formData.CF21L_field2,canedit:modifiable"]');
            field.addr = document.querySelector('[data-bind="value:obsData.formData.CF21K_field3,canedit:modifiable"]');
            field.remark = document.querySelector('[data-bind="value:obsData.formData.CF21O_field1,canedit:modifiable"]');
            fillField(field.fullName, profile.fullName);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            // 添加備註欄位的右鍵選單
            if (field.remark) {
                field.remark.addEventListener('contextmenu', function (event) {
                    event.preventDefault();
                    createContextMenu(event.pageX, event.pageY, menuOptions);
                });
            }
        }
    }
    function taiNan() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName.startsWith('/TrafficMailbox/Index/')) {
            field.disclaimerRead = document.querySelector('#checkRead');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
                scrollToBottom();
            }
        }
        else if (urlPathName.startsWith('/TrafficMailbox/Create')) {
            field.fullName = document.querySelector('#Name');
            field.id = document.querySelector('#Pid');
            field.tel = document.querySelector('#TEL');
            field.addr = document.querySelector('#Address');
            field.mail = document.querySelector('#Email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function pingDong() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName === '/') {
            field.disclaimerRead = document.querySelector('#OK');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
                scrollToBottom();
            }
        }
        else if (urlPathName === '/traffic_write.jsp') {
            field.fullName = document.querySelector('#name');
            field.id = document.querySelector('#sub');
            field.tel = document.querySelector('#liaisontel');
            field.addr = document.querySelector('#address');
            field.mail = document.querySelector('#email');
            field.actSelect = document.querySelector('#qclass');
            field.dateTime = document.querySelector('#violationdatetime');
            field.detail = document.querySelector('#detailcontent');
            field.disclaimerRead = document.querySelector('#isagree');
            if (field.disclaimerRead) {
                field.disclaimerRead.click();
                field.disclaimerRead.click();
                field.disclaimerRead.checked = true;
            }
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
            if (field.dateTime)
                field.dateTime.removeAttribute('readonly');
            if (field.actSelect) {
                field.actSelect.onchange = () => {
                    if (field.detail)
                        field.detail.value = field.actSelect.value;
                };
            }
            document.querySelector('#licensenumber1')?.style.setProperty('display', 'none');
            document.querySelector('#licensenumber2')?.style.setProperty('display', 'none');
            document.querySelector('.carnum span')?.style.setProperty('display', 'none');
            const customLicenseInput = document.createElement('input');
            customLicenseInput.setAttribute('placeholder', '完整車牌,包含-');
            customLicenseInput.style.setProperty('display', 'block');
            customLicenseInput.style.setProperty('width', 'calc(80% - 7px)');
            document.querySelector('.carnum')?.insertBefore(customLicenseInput, document.querySelector('#licensenumber1 label'));
            customLicenseInput.oninput = (() => {
                const licenseInputL = document.querySelector('#licensenumber1');
                const licenseInputR = document.querySelector('#licensenumber2');
                const [licenseNumL, licenseNumR] = customLicenseInput.value.split('-');
                if (licenseInputL)
                    licenseInputL.value = licenseNumL || '';
                if (licenseInputR)
                    licenseInputR.value = licenseNumR || '';
            });
        }
    }
    function yiLan() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName.startsWith('/index.php')) {
            field.fullName = document.querySelector('#name');
            field.id = document.querySelector('#idcard');
            field.tel = document.querySelector('#tel');
            field.addr = document.querySelector('#address2');
            field.mail = document.querySelector('#email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function huaLian() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName.startsWith('/order/iframviolation_list.php')) {
            field.fullName = document.querySelector('[name=name]');
            field.id = document.querySelectorAll('#mform>.input-group>input')[1];
            field.tel = document.querySelector('[name=mobile]');
            field.addr = document.querySelector('[name=address]');
            field.mail = document.querySelector('[name=email]');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function taiDong() {
        let field = {};
        const urlPathName = location.pathname;
        if (urlPathName.startsWith('/chinese/home.jsp')) {
            field.fullName = document.querySelector('#name');
            field.id = document.querySelector('#pid');
            field.tel = document.querySelector('#tel');
            field.addr = document.querySelector('#address');
            field.mail = document.querySelector('#email');
            field.genderMale = document.querySelector('#sex1');
            field.genderFemale = document.querySelector('#sex2');
            if (profile.gender === GENDER.FEMALE && field.genderFemale)
                field.genderFemale.click();
            else if (profile.gender === GENDER.MALE && field.genderMale)
                field.genderMale.click();
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function pengHu() {
        let field = {};
        const urlPathName = location.pathname;
        const urlSearch = location.search;
        // 免責聲明頁面
        if (urlSearch === '?id=404') {
            scrollToBottom();
        }
        // 表單頁面
        else {
            field.fullName = document.querySelector('#name');
            field.id = document.querySelector('#pid');
            field.tel = document.querySelector('#tel');
            field.addr = document.querySelector('#address');
            field.mail = document.querySelector('#email');
            fillField(field.fullName, profile.fullName);
            fillField(field.id, profile.id);
            fillField(field.tel, profile.tel);
            fillField(field.addr, profile.addr);
            fillField(field.mail, profile.mail);
        }
    }
    function createContextMenu(x, y, options) {
        removeExistingMenu();
        const menu = document.createElement('div');
        menu.id = 'custom-context-menu';
        menu.style.position = 'absolute';
        menu.style.left = `${x}px`;
        menu.style.top = `${y}px`;
        menu.style.backgroundColor = 'white';
        menu.style.border = '1px solid black';
        menu.style.padding = '10px';
        menu.style.zIndex = '1000';
        for (const key in options) {
            const item = document.createElement('div');
            item.textContent = key;
            item.style.padding = '5px';
            item.style.cursor = 'pointer';
            item.addEventListener('click', function (event) {
                event.stopPropagation();
                createSubmenu(+getComputedStyle(menu).getPropertyValue('width').replace('px', '') + x, y, options[key]);
            });
            menu.appendChild(item);
        }
        document.body.appendChild(menu);
    }
    function createSubmenu(x, y, options) {
        removeExistingMenu('submenu');
        const submenu = document.createElement('div');
        submenu.id = 'custom-context-submenu';
        submenu.style.cssText = `
      position: absolute;
      left: ${x}px;
      top: ${y}px;
      background-color: white;
      border: 1px solid black;
      padding: 10px;
      z-index: 1000;
    `;
        options.forEach(optionText => {
            const menuItem = document.createElement('div');
            menuItem.textContent = optionText;
            menuItem.style.padding = '5px';
            menuItem.style.cursor = 'pointer';
            menuItem.addEventListener('click', function () {
                const currentHost = location.host;
                const cityConfig = Object.values(CITY_CONFIG).find(city => city.host === currentHost);
                if (cityConfig?.violation) {
                    let input = null;
                    // 特殊處理花蓮的備註欄位
                    if (currentHost === 'hlpb.twgov.mobi') {
                        input = document.getElementById('20221205203006');
                    }
                    else {
                        input = document.querySelector(cityConfig.violation.input);
                    }
                    if (input) {
                        fillField(input, optionText);
                    }
                }
                removeExistingMenu();
            });
            submenu.appendChild(menuItem);
        });
        document.body.appendChild(submenu);
    }
    function removeExistingMenu(type = 'all') {
        if (type === 'all' || type === 'menu') {
            const existingMenu = document.getElementById('custom-context-menu');
            if (existingMenu) {
                existingMenu.remove();
            }
        }
        if (type === 'all' || type === 'submenu') {
            const existingSubmenu = document.getElementById('custom-context-submenu');
            if (existingSubmenu) {
                existingSubmenu.remove();
            }
        }
    }
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.id = 'traffic-fine-settings';
        panel.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      z-index: 9999;
      font-family: Arial, sans-serif;
      min-width: 300px;
    `;
        const title = document.createElement('h3');
        title.textContent = '交通違規檢舉設定';
        title.style.cssText = `
      margin: 0 0 15px 0;
      color: #333;
      font-size: 16px;
    `;
        panel.appendChild(title);
        const form = document.createElement('form');
        form.style.cssText = `
      display: flex;
      flex-direction: column;
      gap: 10px;
    `;
        const fields = [
            { key: 'fullName', label: '姓名', type: 'text' },
            { key: 'id', label: '身分證/居留證號', type: 'text' },
            { key: 'addr', label: '地址', type: 'text' },
            { key: 'tel', label: '電話', type: 'tel' },
            { key: 'mail', label: '電子郵件', type: 'email' },
        ];
        fields.forEach(field => {
            const container = document.createElement('div');
            container.style.cssText = `
        display: flex;
        flex-direction: column;
        gap: 5px;
      `;
            const label = document.createElement('label');
            label.textContent = field.label;
            label.style.cssText = `
        font-size: 14px;
        color: #666;
      `;
            const input = document.createElement('input');
            input.type = field.type;
            input.value = profile[field.key];
            input.style.cssText = `
        padding: 8px;
        border: 1px solid #ddd;
        border-radius: 4px;
        font-size: 14px;
      `;
            input.addEventListener('change', (e) => {
                const target = e.target;
                const key = field.key;
                profile[key] = target.value;
                localStorage.setItem(`profile_${field.key}`, target.value);
            });
            container.appendChild(label);
            container.appendChild(input);
            form.appendChild(container);
        });
        // 性別選擇
        const genderContainer = document.createElement('div');
        genderContainer.style.cssText = `
      display: flex;
      flex-direction: column;
      gap: 5px;
    `;
        const genderLabel = document.createElement('label');
        genderLabel.textContent = '性別';
        genderLabel.style.cssText = `
      font-size: 14px;
      color: #666;
    `;
        const genderSelect = document.createElement('select');
        genderSelect.style.cssText = `
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      font-size: 14px;
    `;
        const maleOption = document.createElement('option');
        maleOption.value = String(GENDER.MALE);
        maleOption.textContent = '男';
        const femaleOption = document.createElement('option');
        femaleOption.value = String(GENDER.FEMALE);
        femaleOption.textContent = '女';
        genderSelect.appendChild(maleOption);
        genderSelect.appendChild(femaleOption);
        genderSelect.value = String(profile.gender);
        genderSelect.addEventListener('change', (e) => {
            const target = e.target;
            profile.gender = Number(target.value);
            localStorage.setItem('profile_gender', target.value);
        });
        genderContainer.appendChild(genderLabel);
        genderContainer.appendChild(genderSelect);
        form.appendChild(genderContainer);
        // 關閉按鈕
        const closeButton = document.createElement('button');
        closeButton.textContent = '關閉';
        closeButton.style.cssText = `
      margin-top: 15px;
      padding: 8px 16px;
      background: #f0f0f0;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
    `;
        closeButton.addEventListener('click', () => {
            panel.remove();
            handleCity(); // 關閉時觸發自動填入
        });
        // 拖曳功能
        let isDragging = false;
        let currentX = 0;
        let currentY = 0;
        let initialX = 0;
        let initialY = 0;
        let xOffset = 0;
        let yOffset = 0;
        title.style.cursor = 'move';
        title.addEventListener('mousedown', dragStart);
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd);
        function dragStart(e) {
            initialX = e.clientX - xOffset;
            initialY = e.clientY - yOffset;
            if (e.target === title) {
                isDragging = true;
            }
        }
        function drag(e) {
            if (isDragging) {
                e.preventDefault();
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;
                xOffset = currentX;
                yOffset = currentY;
                panel.style.transform = `translate(${currentX}px, ${currentY}px)`;
            }
        }
        function dragEnd() {
            initialX = currentX;
            initialY = currentY;
            isDragging = false;
        }
        panel.appendChild(form);
        panel.appendChild(closeButton);
        document.body.appendChild(panel);
    }
    // 添加設定按鈕
    function createSettingsButton() {
        const button = document.createElement('button');
        button.textContent = '設定';
        button.style.cssText = `
      position: fixed;
      bottom: 20px;
      right: 20px;
      padding: 10px 20px;
      background: #4CAF50;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
      z-index: 9998;
      box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    `;
        button.addEventListener('click', createSettingsPanel);
        document.body.appendChild(button);
    }
    createSettingsButton();
    handleCity();
})();