سكربت فايبر هوم لتغيير ip بضغطة زر والبحث عن ip معين مع اختبار السرعه

السلام عليكم ورحمة الله وبركاته،
اليوم أقدم لكم سكربت مميز ومخصص لراوترات فايبر هوم، اسمه IP Tools،
أداة بسيطة وسهلة للتحكم الكامل في عنوان IP، سواء في التغيير أو العرض أو حتى البحث عن عنوان خارجي معين، بالإضافة إلى ميزة اختبار السرعة وتسجيل النتائج تلقائيًا.

مميزات السكربت:

1 - عرض عنوان IP الخارجي و WAN IP
يعرض لك بشكل مباشر ومختصر عنوان IP الخارجي و WAN IP .

2 - تغيير عنوان IP بضغطة زر
تقدر تغير عنوان IP الخارجي بسهولة بضغطة زر .

3 - تغيير IP مع اختبار سرعة تلقائي وتسجيل النتائج
عند تفعيل هذا الخيار، السكربت يقوم بتغيير IP تلقائيًا، ثم يجري اختبار سرعة من موقع fast.com،
ويعرض النتائج في جدول مرتب يحتوي على:
عنوان IP الجديد، سرعة التحميل، سرعة الرفع، البنق، ووقت التنفيذ.
وكل محاولة يتم تسجيلها تلقائيًا في جدول مرتب .

4 - البحث عن IP خارجي يبدأ برقم محدد مع أنماط فلترة متقدمة

  • الفكره هنا ب اختصار ابي ip معين يبحث عنه السكربت

مثلا ابي ابحث عن ( IP ) معين يبدا بـ 2 أو 2.90 السكربت يبدأ البحث تلقائيًا حتى يعثر على IP يبدأ بالرقم هذا .

ويدعم نمطين للفلترة:

النمط الأول باستخدام فاصل -
مثال:

لو كان عندنا الـ ip التاليه

51.253.174.158
2.90.195.140

وابيه يبحث فقط عن الـ ip الى يبدا بـ 51 , 2 والخ انفذ الامر :

2-51-141
معناه: ابحث فقط عن IP يبدأ بـ 2 أو 51 أو 141

النمط الثاني باستخدام فاصل x

لو كان عندنا الـ ip التاليه ::

51.253.174.158
2.90.195.140

** النمط الثالث** :

ابيه يبحث عن الـ ip مثلا التالي : 51.253.174.158

وتحديدا ابيه يكون يبدا بـ 51.253 فقط الموضوع بسيط اكتب فقط : 51.253 وهو رايح يبحث عن البدايه هاذي .

ابيه يبحث عن الـ ip بشكل اوسع ولاكن بشرط مايبدا بـ 51 و 2 في البدايه انفذ الامر ::

مثال:
2x51x141
معناه: تجاهل أي IP يبدأ بـ 2 أو 51 أو 141

طبعا تقدر تسوي البحث عن الـ Public IP + WAN IP بنفس الوقت وبنفس الانماط .

وهاذي صوره السكربت :

والشرح كامل هنا كيف التركيب وشرح السكربت ::

في الاخير الحقوق كامله لمنتدى عالم الراوترات اي شخص يبي يعدل او شي عادي لاكن اذكر الحقوق لـ عالم الراوترات

لو واجت مشكله او شي في اختبار السرعه لاول مره تاكد انك سمحت لـ السماح للنوافذ المنبثقة .

تحميل السكربت :

تحديث للسكربت بشكل اخر وكانه من قوائم المودم بهذا الشكل :


تم التعديل و إضافة المشاركة بالأسفل من قبل @khalid للفائدة

++ تحديث جديد للسكربت لمن يعاني من مشكلة عدم ظهور WAN IP أو Public IP, حصلت لي شخصياً و قمت بالتعديل عليها :

// ==UserScript==
// @name         Advanced IP Kha
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  Monitor IPs with automatic speed testing via Fast.com - Modem Interface Style
// @author       Fahad (Twitter: @kkoao1)
// @match        http://192.168.8.1/*
// @match        https://fast.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        window.close
// ==/UserScript==

let speedTestRetryCount = 0;
let lastKnownWAN = "";
let currentLang = GM_getValue('ipMonitorLang', 'en');
let ipPinningAttempts = 0;

// Language translations
const translations = {
    en: {
        pageTitle: "IP Tools & Network Monitor",
        mainTitle: "IP Tools",
        status: {
            connected: "Connected",
            disconnected: "Disconnected",
            connecting: "Connecting"
        },
        buttons: {
            changeIP: "Change IP",
            speedTest: "Speed Test",
            stopAuto: "Stop Auto",
            saveLog: "Save Log",
            close: "Close",
            clearLog: "Clear",
            statsTable: "Speed Table"
        },
        labels: {
            networkStatus: "Network Status",
            autoSpeedTest: "Auto Speed Test Mode",
            autoSpeedDesc: "Automatically test speed for each new IP",
            networkLog: "Network Log",
            ipPinning: "IP Pinning",
            ipPinDesc: "Auto-change IP until match found",
            startPinning: "Start Pinning",
            wanIP: "WAN IP",
            publicIP: "Public IP",
            speed: "Speed",
            upload: "Upload",
            ping: "Ping",
            time: "Time",
            status: "Status",
            currentNetwork: "Current Network Information",
            quickActions: "Quick Actions",
            advancedSettings: "Advanced Settings",
            statistics: "Statistics & Logs"
        },
        messages: {
            noLogs: "No logs yet...",
            openingFast: "Opening Fast.com for speed test...",
            waitingTest: "Waiting for speed test to complete...",
            networkError: "Network error during speed test, retrying in 3 seconds...",
            maxRetries: "Reached maximum retries for speed test. Please check your internet and try again.",
            waitingNext: "Waiting 3 seconds before next IP change...",
            testTimeout: "Speed test timeout - no results obtained",
            processRunning: "Process already running!",
            startingIPChange: "Starting IP change process...",
            loadingAirplane: "Loading airplane mode settings...",
            enablingAirplane: "Enabling airplane mode...",
            airplaneEnabled: "Airplane mode enabled",
            waiting: "Waiting",
            seconds: "seconds",
            disablingAirplane: "Disabling airplane mode...",
            airplaneDisabled: "Airplane mode disabled",
            waitingReconnect: "Waiting for reconnection...",
            ipChangeComplete: "IP change completed!",
            error: "Error",
            testCompleted: "Test completed! Closing...",
            speedTestResults: "Speed Test Results",
            testCount: "Test Count",
            ipFound: "Target IP found! Stopping...",
            searchingIP: "Searching for matching IP...",
            pinningActive: "IP Pinning Active"
        },
        statsTable: {
            title: "Speed Test Results",
            headers: {
                number: "#",
                wanIP: "WAN IP",
                publicIP: "Public IP",
                speed: "Speed",
                upload: "Upload",
                ping: "Ping"
            }
        }
    },
    ar: {
        pageTitle: "أدوات IP ومراقب الشبكة",
        mainTitle: "أدوات IP",
        status: {
            connected: "متصل",
            disconnected: "غير متصل",
            connecting: "جاري الاتصال"
        },
        buttons: {
            changeIP: "تغيير IP",
            speedTest: "اختبار السرعة",
            stopAuto: "إيقاف تلقائي",
            saveLog: "حفظ السجل",
            close: "إغلاق",
            clearLog: "مسح",
            statsTable: "جدول السرعات"
        },
        labels: {
            networkStatus: "حالة الشبكة",
            autoSpeedTest: "وضع اختبار السرعة التلقائي",
            autoSpeedDesc: "اختبار السرعة تلقائياً لكل IP جديد",
            networkLog: "سجل الشبكة",
            ipPinning: "تثبيت IP",
            ipPinDesc: "تغيير IP تلقائياً حتى العثور على التطابق",
            startPinning: "بدء التثبيت",
            wanIP: "IP الشبكة",
            publicIP: "IP العام",
            speed: "السرعة",
            upload: "الرفع",
            ping: "البنج",
            time: "الوقت",
            status: "الحالة",
            currentNetwork: "معلومات الشبكة الحالية",
            quickActions: "إجراءات سريعة",
            advancedSettings: "إعدادات متقدمة",
            statistics: "الإحصائيات والسجلات"
        },
        messages: {
            noLogs: "لا توجد سجلات بعد...",
            openingFast: "فتح Fast.com لاختبار السرعة...",
            waitingTest: "انتظار اكتمال اختبار السرعة...",
            networkError: "خطأ في الشبكة أثناء اختبار السرعة، إعادة المحاولة بعد 3 ثواني...",
            maxRetries: "تم الوصول للحد الأقصى من المحاولات. تحقق من الإنترنت وحاول مرة أخرى.",
            waitingNext: "انتظار 3 ثواني قبل تغيير IP التالي...",
            testTimeout: "انتهت مهلة اختبار السرعة - لم يتم الحصول على نتائج",
            processRunning: "العملية قيد التشغيل بالفعل!",
            startingIPChange: "بدء عملية تغيير IP...",
            loadingAirplane: "تحميل إعدادات وضع الطيران...",
            enablingAirplane: "تفعيل وضع الطيران...",
            airplaneEnabled: "تم تفعيل وضع الطيران",
            waiting: "انتظار",
            seconds: "ثواني",
            disablingAirplane: "إلغاء وضع الطيران...",
            airplaneDisabled: "تم إلغاء وضع الطيران",
            waitingReconnect: "انتظار إعادة الاتصال...",
            ipChangeComplete: "اكتملت عملية تغيير IP!",
            error: "خطأ",
            testCompleted: "اكتمل الاختبار! جاري الإغلاق...",
            speedTestResults: "نتائج اختبارات السرعة",
            testCount: "عدد الاختبارات",
            ipFound: "تم العثور على IP المستهدف! جاري الإيقاف...",
            searchingIP: "البحث عن IP مطابق...",
            pinningActive: "تثبيت IP نشط"
        },
        statsTable: {
            title: "نتائج اختبارات السرعة",
            headers: {
                number: "#",
                wanIP: "IP الشبكة",
                publicIP: "IP العام",
                speed: "السرعة",
                upload: "الرفع",
                ping: "البنج"
            }
        }
    }
};

// Get translation
function t(key) {
    const keys = key.split('.');
    let value = translations[currentLang];
    for (const k of keys) {
        value = value[k];
    }
    return value || key;
}

(function() {
    'use strict';

    // Check if we're on Fast.com
    if (window.location.hostname === 'fast.com') {
        handleFastCom();
        return;
    }

    // Check if we're on IP Tools page
    if (window.location.hash === '#/iptools') {
        setTimeout(createIPToolsPage, 100);
        return;
    }

function addSidebarBtn() {
    let sidebarMenu = document.querySelector('.el-menu');
    if (!sidebarMenu || document.getElementById('fh_router_world_group')) return false;

    let group = document.createElement('div');
    group.className = 'el-submenu';
    group.id = 'fh_router_world_group';
    group.style.marginTop = '10px';

    group.innerHTML = `
        <div class="el-submenu__title" style="cursor:pointer; display: flex; align-items: center; gap: 10px;">
            <i class="icon-fh-apps"></i>
            <span style="font-size: 20px; font-weight: bold; color: #155fa0; letter-spacing: 0.5px; font-family: 'Tajawal', 'Segoe UI', Arial, sans-serif;">
                routers.world
            </span>
            <i class="el-submenu__icon-arrow el-icon-arrow-down" style="margin-right:auto;"></i>
        </div>
        <ul class="el-menu el-menu--inline">
            <li id="fh_ip_tools_item" class="el-menu-item" tabindex="-1" style="padding-left: 40px; color: rgb(100,100,100); font-size:15px;">
                <i class="icon-fh-apps"></i>
                <span title="IP Tools">IP Tools</span>
            </li>
        </ul>
    `;

    sidebarMenu.appendChild(group);

    let submenuTitle = group.querySelector('.el-submenu__title');
    let submenuList = group.querySelector('.el-menu--inline');
    submenuList.style.display = 'none';
    submenuTitle.onclick = () => {
        submenuList.style.display = (submenuList.style.display === 'none') ? 'block' : 'none';
    };

    group.querySelector('#fh_ip_tools_item').onclick = function(e) {
        e.preventDefault();
        e.stopPropagation();
        showIPToolsPage && showIPToolsPage();
        document.querySelectorAll('.el-menu-item').forEach(item => item.classList.remove('is-active'));
        this.classList.add('is-active');
        submenuList.style.display = 'block';
        return false;
    };

    return true;
}



    // دالة لإظهار صفحة IP Tools
function showIPToolsPage() {
    let mainContent = document.querySelector('.el-main');
    if (!mainContent) return; // إذا الصفحة غير مناسبة، لا تسوي شي

    // حفظ المحتوى الأصلي إذا لم يكن محفوظاً
    if (!window._originalMainContent) {
        window._originalMainContent = mainContent.innerHTML;
    }

    // إنشاء صفحة IP Tools
    createIPToolsPage();
}



    // Try to add sidebar button
    if (!addSidebarBtn()) {
        let sidebarInterval = setInterval(()=>{
            if (addSidebarBtn()) clearInterval(sidebarInterval);
        }, 500);
    }

    // Watch for hash changes
    window.addEventListener('hashchange', function() {
        if (window.location.hash === '#/iptools') {
            setTimeout(createIPToolsPage, 100);
        }
    });

    // -- STATE --
    let logList = [];
    let interval = null;
    let watcherActive = false;
    let prev = {status:"", wan:"", pub:""};
    let airplaneProcess = {active: false};
    let speedTestEnabled = false;
    let speedTestCycle = false;
    let speedTestWindow = null;
    let speedTestInProgress = false;
    let ipPinningActive = false;
    let targetWAN = "";
    let targetPublic = "";

    // Create IP Tools Page




function createIPToolsPage() {
    let mainContent = document.querySelector('.el-main');
    if (!mainContent) return;

    let pageContent = document.getElementById('ip_tools_content');
    if (!pageContent) {
        // Clear existing content
        mainContent.innerHTML = '';

        pageContent = document.createElement('div');
        pageContent.id = 'ip_tools_content';
        // هنا لازم تضيفه للـ main
        mainContent.appendChild(pageContent);
    }

    pageContent.innerHTML = `
    <div>
        <div class="page_title" style="display: flex; align-items: center; justify-content: space-between;">
            <div style="display: flex; align-items: center; gap: 10px;">
                <div class="head_line" style="font-size: 1.5rem; font-weight: bold;">
                    ${t('pageTitle')}
                </div>
                <button id="fh_lang_toggle" style="margin-left: 15px; background: #409eff; color: #fff; border: none; border-radius: 5px; padding: 4px 15px; font-weight: bold; cursor: pointer;">
                    ${currentLang === 'ar' ? 'English' : 'العربية'}
                </button>
            </div>
            <img src="https://routers.world/uploads/default/original/1X/237b3e2cbe9195c703d242957c2d4af3061a78ac.svg"
                 alt="routers.world"
                 style="height: 40px; margin-left: 20px; filter: drop-shadow(0 1px 4px #ccc;" />
        </div>
        <div class="page_content">
            <!-- Network Status Bar -->
            <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
                <div style="display: flex; gap: 30px; align-items: center;">
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.status')}:</span>
                        <span id="fh_network_status" style="font-weight: bold; margin-left: 10px;">-</span>
                    </div>
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.wanIP')}:</span>
                        <span id="fh_wan_ip" style="font-weight: bold; color: #409eff; margin-left: 10px;">-</span>
                    </div>
                    <div>
                        <span style="color: #666; font-size: 14px;">${t('labels.publicIP')}:</span>
                        <span id="fh_public_ip" style="font-weight: bold; color: #e6a23c; margin-left: 10px;">-</span>
                    </div>
                </div>
                <div id="fh_process_status" style="display: none; background: #e6a23c; color: #fff; padding: 5px 15px; border-radius: 20px; font-size: 14px; font-weight: bold;"></div>
            </div>

            <!-- Main Content Grid -->
            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
                <!-- Left Column -->
                <div>
                    <!-- Quick Actions -->
                    <div class="page_sec_head">
                        ${t('labels.quickActions')}
                    </div>
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-top: 10px;">
                        <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px;">
                            <button id="fh_change_ip" class="el-button el-button--primary">
                                <span>✈️ ${t('buttons.changeIP')}</span>
                            </button>
                            <button id="fh_speed_test" class="el-button el-button--success">
                                <span>⚡ ${t('buttons.speedTest')}</span>
                            </button>
                            <button id="fh_stats_btn" class="el-button el-button--info">
                                <span>📊 ${t('buttons.statsTable')}</span>
                            </button>
                        </div>
                    </div>

                    <!-- Settings -->
                    <div class="page_sec_head" style="margin-top: 20px;">
                        ${t('labels.advancedSettings')}
                    </div>
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px; margin-top: 10px;">
                        <!-- Auto Speed Test -->
                        <div style="display: flex; align-items: center; justify-content: space-between; padding: 10px; background: #fff; border-radius: 6px; margin-bottom: 10px;">
                            <label style="display: flex; align-items: center; gap: 10px; cursor: pointer; flex: 1;">
                                <input type="checkbox" id="fh_auto_speed" style="width: 18px; height: 18px;">
                                <div>
                                    <div style="font-weight: bold;">🚀 ${t('labels.autoSpeedTest')}</div>
                                    <div style="color: #666; font-size: 12px;">${t('labels.autoSpeedDesc')}</div>
                                </div>
                            </label>
                            <button id="fh_stop_auto" class="el-button el-button--danger el-button--small">
                                ${t('buttons.stopAuto')}
                            </button>
                        </div>

                        <!-- IP Pinning -->
                        <div style="padding: 10px; background: #fff; border-radius: 6px;">
                            <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
                                <div>
                                    <span style="font-weight: bold;">📌 ${t('labels.ipPinning')}</span>
                                    <span id="fh_pinning_counter" style="color: #e6a23c; font-weight: bold; margin-left: 10px; display: none;">
                                        (<span id="fh_pinning_count">0</span>)
                                    </span>
                                    <div style="color: #666; font-size: 12px;">${t('labels.ipPinDesc')}</div>
                                </div>
                                <button id="fh_start_pinning" class="el-button el-button--warning el-button--small">
                                    ${t('labels.startPinning')}
                                </button>
                            </div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                                <input type="text" id="fh_target_wan" placeholder="WAN: 10.193.89" class="el-input__inner">
                                <input type="text" id="fh_target_public" placeholder="Public: 51.253.184" class="el-input__inner">
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Right Column - Network Log -->
                <div>
                    <div class="page_sec_head" style="display: flex; justify-content: space-between; align-items: center;">
                        <span>${t('labels.networkLog')}</span>
                        <div>
                            <button id="fh_clear_log" class="el-button el-button--text" style="padding: 5px 10px;">
                                ${t('buttons.clearLog')}
                            </button>
                            <button id="fh_save_log" class="el-button el-button--text" style="padding: 5px 10px;">
                                💾 ${t('buttons.saveLog')}
                            </button>
                        </div>
                    </div>
                    <div id="fh_ip_log" style="height: 350px; overflow-y: auto; background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 10px; border: 1px solid #e4e7ed;">
                        <div style="color: #999; text-align: center; padding: 20px;">${t('messages.noLogs')}</div>
                    </div>
                </div>
            </div>

            <!-- Footer -->
            <div style="text-align: center; margin-top: 30px; padding: 15px; color: #666; border-top: 1px solid #e4e7ed;">
                <div style="font-size: 14px;">
                    Developed by Fahad |
                    <a href="https://routers.world/" target="_blank" style="color: #409EFF; text-decoration: none;">
                        routers.world
                    </a>
                </div>
            </div>
        </div>
    </div>
    `;

    // زر تبديل اللغة
    let langBtn = document.getElementById('fh_lang_toggle');
    if (langBtn) {
        langBtn.onclick = () => {
            currentLang = currentLang === 'ar' ? 'en' : 'ar';
            GM_setValue('ipMonitorLang', currentLang);
            createIPToolsPage();
        };
    }

    // (اختياري) اتجاه الصفحة عربي أو إنجليزي
    pageContent.dir = (currentLang === 'ar') ? 'rtl' : 'ltr';

    // Add event listeners
    document.getElementById('fh_change_ip').onclick = triggerAirplaneMode;
    document.getElementById('fh_speed_test').onclick = () => runSpeedTest();
    document.getElementById('fh_clear_log').onclick = () => { logList = []; updateLogDisplay(); };
    document.getElementById('fh_save_log').onclick = saveLogToFile;
    document.getElementById('fh_auto_speed').onchange = (e) => { speedTestEnabled = e.target.checked; };
    document.getElementById('fh_stats_btn').onclick = showStatsWindow;
    document.getElementById('fh_start_pinning').onclick = startIPPinning;
    document.getElementById('fh_stop_auto').onclick = () => {
        speedTestEnabled = false;
        ipPinningActive = false;
        let cb = document.getElementById('fh_auto_speed');
        if(cb) cb.checked = false;
        updateProcessStatus("");
        let counter = document.getElementById('fh_pinning_counter');
        if(counter) counter.style.display = 'none';
    };

    // Update sidebar active state
    updateSidebarActiveState();

    // Start monitoring
    updateInfo(true);
    if(interval) clearInterval(interval);
    interval = setInterval(() => updateInfo(), 2000);
}


    function updateSidebarActiveState() {
        // Remove active from all items
        document.querySelectorAll('.el-menu-item').forEach(item => {
            item.classList.remove('is-active');
            item.style.color = '#646464';
        });

        // Remove active from all links
        document.querySelectorAll('.el-menu a').forEach(link => {
            link.classList.remove('router-link-active', 'router-link-exact-active');
        });

        // Add active to IP Tools
        let ipToolsLink = document.querySelector('#fh_ip_monitor_link');
        if (ipToolsLink) {
            ipToolsLink.classList.add('router-link-active', 'router-link-exact-active');
            let menuItem = ipToolsLink.querySelector('.el-menu-item');
            if (menuItem) {
                menuItem.classList.add('is-active');
                menuItem.style.color = '#ff6225 !important';
            }
        }
    }

    // IP Pinning function
    function startIPPinning() {
        targetWAN = document.getElementById('fh_target_wan').value.trim();
        targetPublic = document.getElementById('fh_target_public').value.trim();

        if (!targetWAN && !targetPublic) {
            alert("Please enter at least one IP to search for!");
            return;
        }

        ipPinningActive = true;
        ipPinningAttempts = 0;
        speedTestEnabled = false;
        document.getElementById('fh_auto_speed').checked = false;

        logList.unshift(`<div style="color: #e6a23c; font-weight: bold; background: #fdf6ec; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🔍 ${t('messages.searchingIP')}</div>`);
        updateLogDisplay();

        triggerAirplaneMode();
    }

    function checkIPMatch() {
        if (!ipPinningActive) return false;

        let wanMatch = false;
        let publicMatch = false;

        function checkIPPattern(ip, pattern) {
            if (!ip || ip === "Unknown" || ip === "Failed!") return false;

            const firstOctet = ip.split('.')[0];

            if (pattern.includes('x')) {
                const excludedOctets = pattern.split('x').map(num => num.trim()).filter(num => num);
                return !excludedOctets.includes(firstOctet);
            }
            else if (pattern.includes('-')) {
                const allowedOctets = pattern.split('-').map(num => num.trim());
                return allowedOctets.includes(firstOctet);
            } else {
                return ip.startsWith(pattern);
            }
        }

        if (targetWAN && prev.wan) {
            wanMatch = checkIPPattern(prev.wan, targetWAN);
        }

        if (targetPublic && prev.pub) {
            publicMatch = checkIPPattern(prev.pub, targetPublic);
        }

        if (targetWAN && targetPublic) {
            return wanMatch && publicMatch;
        }
        return wanMatch || publicMatch;
    }

    // Monitor & Log
    async function updateInfo(forceLog=false, retries=1) {
        let modem = await getModemInfo();
        if ((modem.wanip==="Unknown" || modem.status==="Unknown") && retries > 0) {
            await new Promise(r=>setTimeout(r,1200));
            return updateInfo(forceLog, retries-1);
        }

        let publicip = await fetchPublicIP();
        let status = /connected/i.test(modem.status) ? "Connected" :
                     /disconnected/i.test(modem.status) ? "Disconnected" :
                     /connecting/i.test(modem.status) ? "Connecting" :
                     modem.status;
        let wanip = modem.wanip;

        if (wanip && wanip !== "Unknown") lastKnownWAN = wanip;

        // Update status in table
        let statusElem = document.getElementById('fh_network_status');
        let wanElem = document.getElementById('fh_wan_ip');
        let pubElem = document.getElementById('fh_public_ip');

        if (statusElem) {
            statusElem.innerHTML = status === "Connected"
                ? `<span style="color: #67c23a;">📶 ${t('status.connected')}</span>`
                : status === "Disconnected"
                    ? `<span style="color: #f56c6c;">✈️ ${t('status.disconnected')}</span>`
                    : status === "Connecting"
                        ? `<span style="color: #e6a23c;">🔄 ${t('status.connecting')}...</span>`
                        : `<span style="color: #e6a23c;">${status}</span>`;
        }

        if (wanElem) wanElem.textContent = wanip;
        if (pubElem) pubElem.textContent = publicip;

        if (forceLog || prev.status!==status || prev.wan!==wanip || prev.pub!==publicip) {
            logStatus(status, wanip, publicip);
            prev = {status, wan:wanip, pub:publicip};

            if (ipPinningActive && status === "Connected" && checkIPMatch()) {
                ipPinningActive = false;
                updateProcessStatus("");
                document.getElementById('fh_pinning_counter').style.display = 'none';
                logList.unshift(`<div style="color: #67c23a; font-weight: bold; background: #f0f9ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🎯 ${t('messages.ipFound')} (${currentLang === 'ar' ? 'بعد' : 'After'} ${ipPinningAttempts} ${currentLang === 'ar' ? 'محاولة' : 'attempts'})</div>`);
                updateLogDisplay();
                return;
            }

            if (ipPinningActive && status === "Connected" && !airplaneProcess.active) {
                ipPinningAttempts++;
                let counterElem = document.getElementById('fh_pinning_counter');
                let countElem = document.getElementById('fh_pinning_count');
                if (counterElem) counterElem.style.display = 'inline';
                if (countElem) countElem.textContent = ipPinningAttempts;

                setTimeout(()=>{
                    if (ipPinningActive && prev.status === "Connected" && !airplaneProcess.active) {
                        updateProcessStatus(`${t('messages.pinningActive')} - ${t('messages.searchingIP')}`);
                        triggerAirplaneMode();
                    }
                }, 3000);
            }

            if (speedTestEnabled && !speedTestInProgress && status === "Connected" &&
                publicip !== "Failed!" && publicip !== "Unknown" &&
                (prev.pub !== publicip || speedTestCycle)) {
                setTimeout(()=>{
                    if (prev.status === "Connected" && !speedTestInProgress) {
                        runSpeedTest(true);
                    }
                }, 3000);
            }
        }
    }

    function logStatus(status, wan, pub, speedData = null) {
        let time = new Date().toLocaleTimeString(currentLang === 'ar' ? 'ar-SA' : 'en-US');
        let entry = `<div style="background: #f5f5f5; padding: 12px; border-radius: 4px; margin-bottom: 8px; border: 1px solid #e4e7ed;">`;
        entry += `<span style="color: #e6a23c; font-weight: bold;">${time}</span> | `;
        entry += `<b>${t('labels.status')}:</b> <span style="color: ${status=="Connected"?'#67c23a':'#f56c6c'}; font-weight: bold">${t(`status.${status.toLowerCase()}`)}</span>`;
        entry += `<br><b>${t('labels.wanIP')}:</b> <span style="color: #409eff">${wan}</span>`;
        entry += `<br><b>${t('labels.publicIP')}:</b> <span style="color: #e6a23c">${pub}</span>`;
        if (speedData) {
            entry += `<br><b>${t('labels.speed')}:</b> <span style="color: #67c23a">${speedData.download} Mbps</span>`;
            entry += ` | <b>${t('labels.ping')}:</b> <span style="color: #e6a23c">${speedData.ping} ms</span>`;
            entry += ` | <b>${t('labels.upload')}:</b> <span style="color: #909399">${speedData.upload} Mbps</span>`;
        }
        entry += `</div>`;
        logList.unshift(entry);
        updateLogDisplay();
    }

    function updateLogDisplay() {
        let el = document.getElementById('fh_ip_log');
        if(el) el.innerHTML = logList.join("") || `<div style="color: #999; text-align: center; padding: 20px;">${t('messages.noLogs')}</div>`;
    }

    // GET WAN IP + STATUS
function getModemInfo() {
    return new Promise(resolve => {
        let ifr = document.getElementById('fh_net_iframe');
        if (ifr) ifr.remove();

        ifr = document.createElement('iframe');
        ifr.id = 'fh_net_iframe';
        ifr.style.display = 'none';
        ifr.src = "/main.html#/networkInfo"; // ✅ رابط صفحة المعلومات
        document.body.appendChild(ifr);

        setTimeout(() => {
            try {
                let doc = ifr.contentDocument || ifr.contentWindow.document;
                let netStatus = "Unknown", wanIP = "Unknown";

                doc.querySelectorAll("table tr").forEach(tr => {
                    let th = tr.querySelector("th");
                    let td = tr.querySelector("td");
                    if (!th || !td) return;

                    let label = th.textContent.trim();
                    let value = td.textContent.trim();

                    if (label === "WAN IP Address" || label === "عنوان IP للـ WAN") {
                        wanIP = value;
                    }

                    if (label === "Network Status" || label === "حالة الشبكة") {
                        netStatus = value;
                    }
                });

                if (wanIP === "" || wanIP === "-") wanIP = "Unknown";
                if (netStatus === "" || netStatus === "-") netStatus = "Unknown";

                resolve({ status: netStatus, wanip: wanIP });
            } catch (e) {
                resolve({ status: "Unknown", wanip: "Unknown" });
            }

            setTimeout(() => { ifr.remove(); }, 500);
        }, 1200);
    });
}

    // GET PUBLIC IP
    async function fetchPublicIP() {
        let sources = [
            async()=>{ let r=await fetch("https://api.ipify.org?format=json"); return (await r.json()).ip; },
            async()=>{ let r=await fetch("https://ipv4.icanhazip.com/"); return (await r.text()).trim(); },
            async()=>{ let r=await fetch("https://ipinfo.io/json"); return (await r.json()).ip; },
        ];
        for (let fn of sources) {
            try {
                let ip = await fn();
                if (/^\d+\.\d+\.\d+\.\d+$/.test(ip)) return ip;
            } catch(e){}
        }
        return "Failed!";
    }

    // SPEED TEST
    function runSpeedTest(auto = false) {
        if (speedTestInProgress) {
            console.log('Speed test already in progress, skipping...');
            return;
        }

        if (speedTestWindow && !speedTestWindow.closed) {
            speedTestWindow.close();
        }

        speedTestInProgress = true;
        updateProcessStatus(`🚀 ${t('messages.openingFast')}`);
        GM_setValue('speedTestRequested', true);
        GM_setValue('currentPublicIP', prev.pub);

        speedTestWindow = window.open('https://fast.com/', '_blank');

        setTimeout(()=>{
            if (speedTestInProgress) {
                updateProcessStatus(`⏳ ${t('messages.waitingTest')}`);
            }
        }, 3000);

        let timeout = null;

        let checkInterval = setInterval(()=>{
            if (GM_getValue('speedTestShouldRetry', false)) {
                GM_setValue('speedTestShouldRetry', false);
                speedTestInProgress = false;
                if (speedTestWindow && !speedTestWindow.closed) {
                    speedTestWindow.close();
                }
                speedTestWindow = null;
                speedTestRetryCount++;

                logList.unshift(`<div style="color: #f56c6c;">🔁 ${t('messages.networkError')} (Retry #${speedTestRetryCount})</div>`);
                updateLogDisplay();

                clearInterval(checkInterval);
                if (timeout) clearTimeout(timeout);

                if (speedTestRetryCount >= 5) {
                    logList.unshift(`<div style="color: #f56c6c;">❌ ${t('messages.maxRetries')}</div>`);
                    updateLogDisplay();
                    speedTestRetryCount = 0;
                    return;
                }
                setTimeout(()=>{ runSpeedTest(auto); }, 3000);
                return;
            }

            let result = GM_getValue('speedTestResult', null);
            if (result) {
                GM_setValue('speedTestResult', null);
                clearInterval(checkInterval);
                if (timeout) clearTimeout(timeout);

                speedTestInProgress = false;
                speedTestRetryCount = 0;

                logStatus("Connected", lastKnownWAN, prev.pub, result);
                updateProcessStatus("");

                if (speedTestWindow && !speedTestWindow.closed) {
                    speedTestWindow.close();
                }
                speedTestWindow = null;

                if (auto && speedTestCycle && speedTestEnabled) {
                    logList.unshift(`<div style="color: #e6a23c;">⏳ ${t('messages.waitingNext')}</div>`);
                    updateLogDisplay();
                    setTimeout(()=>{
                        if (!airplaneProcess.active) {
                            triggerAirplaneMode();
                        }
                    }, 3000);
                }
            }
        }, 1000);

        timeout = setTimeout(()=>{
            clearInterval(checkInterval);
            speedTestInProgress = false;
            updateProcessStatus("");

            if (speedTestWindow && !speedTestWindow.closed) {
                speedTestWindow.close();
            }
            speedTestWindow = null;

            GM_setValue('speedTestRequested', false);

            if (!auto) {
                alert("Speed test timeout! Please try again.");
            } else {
                logList.unshift(`<div style="color: #f56c6c;">⚠️ ${t('messages.testTimeout')}</div>`);
                updateLogDisplay();

                if (speedTestCycle && speedTestEnabled) {
                    logList.unshift(`<div style="color: #e6a23c;">⏳ ${t('messages.waitingNext')}</div>`);
                    updateLogDisplay();
                    setTimeout(()=>{
                        if (!airplaneProcess.active) {
                            triggerAirplaneMode();
                        }
                    }, 3000);
                }
            }
        }, 90000);
    }

    // AIRPLANE MODE
    function updateProcessStatus(msg) {
        let el = document.getElementById('fh_process_status');
        if (el) {
            el.style.display = msg ? 'block' : 'none';
            el.textContent = msg;
        }
    }

    function triggerAirplaneMode() {
        if (airplaneProcess.active) {
            alert(t('messages.processRunning'));
            return;
        }

        airplaneProcess.active = true;
        watcherActive = true;
        speedTestCycle = speedTestEnabled;

        let btn = document.getElementById('fh_change_ip');
        if (btn) {
            btn.disabled = true;
            btn.classList.add('is-disabled');
        }

        logList.unshift(`<div style="color: #409eff; font-weight: bold; background: #ecf5ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">🔄 ${t('messages.startingIPChange')}</div>`);
        updateLogDisplay();
        updateProcessStatus(`📡 ${t('messages.loadingAirplane')}`);

        let ifr = document.getElementById('fh_air_iframe');
        if (ifr) ifr.remove();
        ifr = document.createElement('iframe');
        ifr.id = 'fh_air_iframe';
        ifr.style.display = 'none';
        ifr.src = "/main.html#/mobileNetwork/networkSet";
        document.body.appendChild(ifr);

        executeAirplaneSequence(ifr);

        if(interval) clearInterval(interval);
        interval = setInterval(()=>updateInfo(), 2000);
    }

    async function executeAirplaneSequence(ifr) {
        try {
            await new Promise(r => setTimeout(r, 1500));

            updateProcessStatus(`✈️ ${t('messages.enablingAirplane')}`);
            let doc = ifr.contentDocument || ifr.contentWindow.document;
            let sw = doc.querySelector('input[type=checkbox].el-switch__input');

            if (sw && !sw.checked) {
                sw.click();
                logList.unshift(`<div style="color: #67c23a;">✅ ${t('messages.airplaneEnabled')}</div>`);
                updateLogDisplay();
            }

            for(let i = 8; i > 0; i--) {
                updateProcessStatus(`⏱️ ${t('messages.waiting')} ${i} ${t('messages.seconds')}...`);
                await new Promise(r => setTimeout(r, 1000));
            }

            updateProcessStatus(`📡 ${t('messages.disablingAirplane')}`);
            sw = doc.querySelector('input[type=checkbox].el-switch__input');
            if (sw && sw.checked) {
                sw.click();
                logList.unshift(`<div style="color: #67c23a;">✅ ${t('messages.airplaneDisabled')}</div>`);
                updateLogDisplay();
            }

            updateProcessStatus(`🔄 ${t('messages.waitingReconnect')}`);
            await new Promise(r => setTimeout(r, 3000));

            updateProcessStatus("");
            ifr.remove();
            logList.unshift(`<div style="color: #67c23a; font-weight: bold; background: #f0f9ff; padding: 8px; border-radius: 4px; margin-bottom: 8px;">✅ ${t('messages.ipChangeComplete')}</div>`);
            updateLogDisplay();

            setTimeout(()=>{
                // Speed test or IP pinning will be handled by updateInfo
            }, 1000);

        } catch(e) {
            logList.unshift(`<div style="color: #f56c6c;">❌ ${t('messages.error')}: ${e.message}</div>`);
            updateLogDisplay();
            updateProcessStatus("");
        } finally {
            airplaneProcess.active = false;
            watcherActive = false;
            let btn = document.getElementById('fh_change_ip');
            if (btn) {
                btn.disabled = false;
                btn.classList.remove('is-disabled');
            }
        }
    }

    // SAVE LOG
    function saveLogToFile() {
        let text = `${t('mainTitle')} - ${t('labels.networkLog')}\n`;
        text += "Generated: " + new Date().toLocaleString(currentLang === 'ar' ? 'ar-SA' : 'en-US') + "\n";
        text += "=====================================\n\n";

        let tempDiv = document.createElement('div');
        logList.forEach(log => {
            tempDiv.innerHTML = log;
            text += tempDiv.textContent.trim() + "\n\n";
        });

        let blob = new Blob([text], {type: 'text/plain'});
        let a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = `IP_Monitor_Log_${new Date().toISOString().slice(0,10)}.txt`;
        a.click();
    }

    // STATS WINDOW
    function showStatsWindow() {
        if (document.getElementById('fh_stats_window')) return;
        let rows = extractSpeedRows(logList);

        let sortBy = 'speed', sortDir = -1;

        function sortRows() {
            rows.sort((a, b) => {
                let va = a[sortBy], vb = b[sortBy];
                if (sortBy === "ping") return (parseFloat(va) - parseFloat(vb)) * sortDir;
                else return (parseFloat(vb) - parseFloat(va)) * sortDir;
            });
        }
        sortRows();

        let win = document.createElement('div');
        win.id = 'fh_stats_window';
        win.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            min-width: 700px;
            max-width: 900px;
            z-index: 2000;
            box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
            border: 1px solid #e4e7ed;
            direction: ${currentLang === 'ar' ? 'rtl' : 'ltr'};
        `;

        win.innerHTML = `
            <div style="margin-bottom: 20px; border-bottom: 1px solid #e4e7ed; padding-bottom: 15px;">
                <h2 style="margin: 0; color: #303133; font-size: 20px; display: inline-block;">
                    ${t('statsTable.title')}
                </h2>
                <button id="fh_stats_close" class="el-button el-button--default el-button--small" style="float: ${currentLang === 'ar' ? 'left' : 'right'};">
                    ${t('buttons.close')}
                </button>
            </div>
            <div style="text-align: center; margin-bottom: 20px;">
                <span style="background: #f4f4f5; color: #606266; padding: 8px 16px; border-radius: 4px; font-size: 14px;">
                    ${t('messages.testCount')}: <strong style="color: #409eff;">${rows.length}</strong>
                </span>
            </div>
            <div id="fh_stats_table_wrap" style="overflow-x: auto;"></div>
        `;
        document.body.appendChild(win);

        document.getElementById('fh_stats_close').onclick = () => { win.remove(); };

        function renderTable() {
            let getArrow = (key) => {
                if (sortBy !== key) return "";
                return sortDir === 1 ? " ▲" : " ▼";
            };
            let table = `
            <table class="el-table" style="width: 100%;">
                <thead>
                    <tr style="background: #f5f7fa;">
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.number')}</th>
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.wanIP')}</th>
                        <th style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${t('statsTable.headers.publicIP')}</th>
                        <th id="sort_speed" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.speed')}${getArrow('speed')}
                        </th>
                        <th id="sort_upload" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.upload')}${getArrow('upload')}
                        </th>
                        <th id="sort_ping" style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; cursor: pointer; color: #409eff;">
                            ${t('statsTable.headers.ping')}${getArrow('ping')}
                        </th>
                    </tr>
                </thead>
                <tbody>
                    ${rows.map((r, i) => `
                    <tr style="background: ${i%2 ? '#fafafa' : '#fff'};">
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5;">${i+1}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #409eff; font-family: monospace;">${r.wan}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #e6a23c; font-family: monospace;">${r.pub}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; font-weight: bold; color: #67c23a;">${r.speed}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #909399;">${r.upload}</td>
                        <td style="padding: 12px; text-align: center; border-bottom: 1px solid #ebeef5; color: #e6a23c;">${r.ping}</td>
                    </tr>
                    `).join('')}
                </tbody>
            </table>
            `;
            document.getElementById('fh_stats_table_wrap').innerHTML = table;

            document.getElementById('sort_speed').onclick = () => {
                if (sortBy == 'speed') sortDir *= -1; else { sortBy = 'speed'; sortDir = -1; }
                sortRows(); renderTable();
            };
            document.getElementById('sort_upload').onclick = () => {
                if (sortBy == 'upload') sortDir *= -1; else { sortBy = 'upload'; sortDir = -1; }
                sortRows(); renderTable();
            };
            document.getElementById('sort_ping').onclick = () => {
                if (sortBy == 'ping') sortDir *= -1; else { sortBy = 'ping'; sortDir = 1; }
                sortRows(); renderTable();
            };
        }
        renderTable();
    }

    function extractSpeedRows(logArr) {
        let out = [];
        let tempDiv = document.createElement('div');
        for (let i = 0; i < logArr.length; i++) {
            tempDiv.innerHTML = logArr[i];
            let plain = tempDiv.textContent || tempDiv.innerText || "";
            let m = plain.match(
                /(?:WAN IP|IP الشبكة):\s*([0-9\.]+).*?(?:Public IP|IP العام):\s*([0-9\.]+).*?(?:Speed|السرعة):\s*(\d+)\s*Mbps\s*\|\s*(?:Ping|البنج):\s*(\d+)\s*ms\s*\|\s*(?:Upload|الرفع):\s*(\d+)\s*Mbps/i
            );
            if (m) {
                out.push({
                    wan: m[1].trim(),
                    pub: m[2].trim(),
                    speed: m[3].trim(),
                    ping: m[4].trim(),
                    upload: m[5].trim()
                });
            }
        }
        return out;
    }

    // FAST.COM HANDLER
    function handleFastCom() {
        if (!GM_getValue('speedTestRequested', false)) return;

        let resultFound = false, detailsLoaded = false, retryCount = 0;
        let startZeroTime = null;

        let checkInterval = setInterval(() => {
            let speedElem   = document.querySelector('#speed-value');
            let uploadElem  = document.querySelector('#upload-value');
            let latencyElem = document.querySelector('#latency-value');

            let download = speedElem   ? speedElem.textContent.trim()   : "0";
            let upload   = uploadElem  ? uploadElem.textContent.trim()  : "0";
            let ping     = latencyElem ? latencyElem.textContent.trim() : "0";

            let noConnElem = document.querySelector('div#error-results-msg[loc-str="no_connection"]');
            let noConnVisible = noConnElem && (noConnElem.offsetParent !== null || noConnElem.style.display === 'block' || noConnElem.style.display === '');

            if (noConnVisible && download === "0" && upload === "0" && ping === "0") {
                if (!startZeroTime) startZeroTime = Date.now();
                if (Date.now() - startZeroTime >= 5000) {
                    retryCount = (retryCount || 0) + 1;
                    console.log(`Fast.com: Real failure (all zero for 5+ sec), will close and retry. (Retry #${retryCount})`);
                    GM_setValue('speedTestRequested', false);
                    GM_setValue('speedTestShouldRetry', true);
                    setTimeout(() => { window.close(); }, 500);
                    clearInterval(checkInterval);
                    return;
                }
            } else {
                startZeroTime = null;
            }

            let progressIndicator = document.querySelector('#speed-progress-indicator');
            if (
                progressIndicator && progressIndicator.classList.contains('succeeded') &&
                !progressIndicator.classList.contains('in-progress') &&
                speedElem && download !== "0" &&
                !detailsLoaded
            ) {
                detailsLoaded = true;
                let moreLink = document.querySelector('#show-more-details-link');
                if (moreLink && moreLink.style.display !== 'none') {
                    moreLink.click();
                }

                let uploadSuccessCheck = setInterval(() => {
                    let uploadElemDone = document.querySelector('#upload-value.extra-measurement-value-container.succeeded');
                    if (uploadElemDone && uploadElemDone.textContent && uploadElemDone.textContent !== '0') {
                        clearInterval(uploadSuccessCheck);

                        setTimeout(() => {
                            let latencyElem = document.querySelector('#latency-value');
                            let speedValue  = document.querySelector('#speed-value');
                            let upload      = uploadElemDone.textContent;
                            let download    = speedValue ? speedValue.textContent : null;
                            let ping        = latencyElem ? latencyElem.textContent : null;

                            if (
                                upload && upload !== '0' &&
                                download && download !== '0' &&
                                ping && ping !== '0'
                            ) {
                                saveFastComResult({download, ping, upload});
                                clearInterval(checkInterval);
                                resultFound = true;
                            }
                        }, 3000);
                    }
                }, 500);
            }
        }, 800);

        setTimeout(() => {
            clearInterval(checkInterval);
            if (!resultFound) {
                console.log('Test timeout, closing window...');
                GM_setValue('speedTestRequested', false);
                window.close();
            }
        }, 90000);

        function saveFastComResult(result) {
            let data = {
                download: result.download,
                ping: result.ping,
                upload: result.upload,
                ip: GM_getValue('currentPublicIP', 'Unknown'),
                timestamp: new Date().toISOString()
            };
            GM_setValue('speedTestResult', data);
            GM_setValue('speedTestRequested', false);

            let body = document.body;
            let msg = document.createElement('div');
            msg.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#67c23a;color:#fff;padding:20px;border-radius:10px;font-size:20px;font-weight:bold;z-index:9999;';
            msg.textContent = `✅ ${t('messages.testCompleted')}`;
            body.appendChild(msg);

            setTimeout(() => { window.close(); }, 2000);
        }
    }

 // دالة تعيد الصفحة لحالتها الأصلية إذا طلع المستخدم
function restoreOriginalPageIfNeeded() {
    // إذا المستخدم خرج من صفحة الأدوات لأي صفحة ثانية
    if (window.location.hash !== "#/iptools") {
        window.location.reload();
    }
}


// راقب أي تغيير في الهاش (أي انتقال بين صفحات)
window.addEventListener('hashchange', restoreOriginalPageIfNeeded);

// أو حتى راقب كل ضغطة في القائمة (احتياط)
document.addEventListener('click', function(e) {
    let el = e.target.closest('.el-menu-item');
    if (el) {
        setTimeout(restoreOriginalPageIfNeeded, 200); // أعطيه مهلة قصيرة حتى تتغير الصفحة
    }
});


})();

++ تفعيل هذي الخيارات من إعدادات متصفح كروم لإضافة Tampermonkey :

7 إعجابات

شرح مميز وتشكر عليه استاذ فهد ، شكراً لك على هذا السكربت والتول الرائع والمفيد جدا جزاك الله خير :folded_hands:

العفو الله يحفطك اقل شي نقدمة لكم

إعجاب واحد (1)

ماشاءالله تبارك الله
الله يجزاك خير ولايضيع لك تعب
رحم الله والديك :+1:

و والديك الله يحفطك العفو

ماشاء الله عليك يا مبدع ! أستمر و كلنا معاك و ندعمك يابطل.

و بإذن الله نطور فيه و نسوي أشياء جميلة للراوتر العظيم فايبرهوم.

منور استاذ خالد ، باذن الله القادم اجمل

لما انسخ السكربت يطلع معي غير عن المقطع المقطع نهاية السكربت ١١٩٢ و لما انسخ ذا و الصقه يطلع ١١٩٣ في شيء غلط عجزت اعرفه

عمل جبااااار شكرا من الاعماق❤️

جميل جداً
لكن ما يشتغل على فايبرهوم الخارجي
ياليت لو يدعمه

إعجاب واحد (1)

بارك الله فيك اخوي خالد والله عمل جبار وتشكر عليه
لكن الحقيقة ما اشتغل معي السكربت مشيت على نفس الخطوات ولا طلع خيار السكربت للاسف
اذا عندكم حل اتمنى تفيدوني لا هنتم


إعجاب واحد (1)

فايبرهوم داخلي او خارجي ؟
هل داخل متصفح كروم بالوضع العادي او الخفي؟
هل عندك مانع اعلانات او كوكيز؟

فايبرهوم داخلي
متصخ كروم وبريف ولا ضبط
ما عندي مانع اعلانات ولا كوكيز

السطر هذا مو مهم اساسا وانا عدلته بعد المقطع والتعديل اساسا ماله علاقه بالأكواد نهائيا هو في اول ١٠ اسطر :sweat_smile:

الواضح انك م اضفت السكربت نهائيا ، حط اظافه برنامح نصي جديد وتفتح معك صفحه الصق السكربت ثم قبل تطلع تاكد انك حطيت حفظ وارجع وحدث الصحفه حثت المودم

ابوحمد متاكد كل شي سويته صح ؟ اذا كل شي سويته صح ولا اشتغل تواصل معي على الخاص .

طبقت الخطوات كلها لكن السكريبت ما طلعلي مع انه طالع ف اضافة الtampermonkey

الله يبيض وجهك ما تقصر لكن انا مو بحاجته مره بس قلت انزله دامه متوفر

طيب ياخي ما ضبط معي نسخة و لصقت و مشيت على كل الخطوات ولا يطلع لي

جرب متصفح فايرفوكس