Via Ultimate Optimizer & DevTools Suite

Extreme all-in-one optimizer for Via Browser: speed boost, ad/tracker block, content spoofing, Chrome-like inspector (Eruda + Quick Inspect), live media URL grabber, turbo parallel downloader, dark mode, video speed controller, custom CSS injector.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Via Ultimate Optimizer & DevTools Suite
// @namespace    https://via-ultimate-optimizer.local/
// @version      1.0.0
// @description  Extreme all-in-one optimizer for Via Browser: speed boost, ad/tracker block, content spoofing, Chrome-like inspector (Eruda + Quick Inspect), live media URL grabber, turbo parallel downloader, dark mode, video speed controller, custom CSS injector.
// @author       You
// @match        *://*/*
// @run_at       document-start
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  /* ============================================================
     0. CONFIG / SETTINGS STORE
  ============================================================ */
  var STORAGE_KEY = 'via_ultimate_opt_settings_v1';
  var DEFAULTS = {
    speedBoost: true,
    preconnect: true,
    lazyImages: true,
    reduceMotion: false,
    blockAds: true,
    darkMode: false,
    videoSpeedController: true,
    mediaGrabber: true,

    spoofUA: false,
    uaPreset: 'pixel',
    spoofCanvas: false,
    spoofWebGL: false,
    spoofTimezone: false,
    timezoneOffset: 0,
    spoofGeo: false,
    geoLat: 40.7128,
    geoLng: -74.0060,
    spoofReferrer: false,
    customReferrer: '',
    blockWebRTC: false,

    customCSS: '',
    fabSide: 'right'
  };

  function loadSettings() {
    try {
      var raw = localStorage.getItem(STORAGE_KEY);
      return Object.assign({}, DEFAULTS, raw ? JSON.parse(raw) : {});
    } catch (e) { return Object.assign({}, DEFAULTS); }
  }
  function saveSettings() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(CFG)); } catch (e) {} }

  var CFG = loadSettings();

  var UA_PRESETS = {
    pixel: { ua: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36', platform: 'Linux armv8l', vendor: 'Google Inc.' },
    iphone: { ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1', platform: 'iPhone', vendor: 'Apple Computer, Inc.' },
    windows: { ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', platform: 'Win32', vendor: 'Google Inc.' },
    mac: { ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15', platform: 'MacIntel', vendor: 'Apple Computer, Inc.' },
    bot: { ua: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', platform: 'Linux', vendor: '' }
  };

  var AD_PATTERNS = [
    /doubleclick\.net/i, /googlesyndication/i, /google-analytics\.com/i, /googletagmanager/i,
    /googleadservices/i, /adservice\./i, /adnxs\.com/i, /taboola/i, /outbrain/i, /criteo/i,
    /hotjar/i, /scorecardresearch/i, /moatads/i, /pubmatic/i, /adsystem/i, /amazon-adsystem/i,
    /facebook\.net\/.*fbevents/i, /yandex\.ru\/metrika/i, /adform\.net/i, /rubiconproject/i
  ];

  /* ============================================================
     1. TOAST HELPER
  ============================================================ */
  function toast(msg, ms) {
    ms = ms || 2200;
    var t = document.getElementById('via-opt-toast');
    if (!t) {
      t = document.createElement('div');
      t.id = 'via-opt-toast';
      (document.body || document.documentElement).appendChild(t);
    }
    t.textContent = msg;
    t.classList.add('show');
    clearTimeout(t._timer);
    t._timer = setTimeout(function () { t.classList.remove('show'); }, ms);
  }

  /* ============================================================
     2. CONTENT SPOOFING (must run as early as possible)
  ============================================================ */
  function defineGetter(obj, prop, getter) {
    try { Object.defineProperty(obj, prop, { get: getter, configurable: true }); } catch (e) {}
  }

  function applyUASpoof() {
    if (!CFG.spoofUA) return;
    var p = UA_PRESETS[CFG.uaPreset];
    if (!p) return;
    defineGetter(navigator, 'userAgent', function () { return p.ua; });
    defineGetter(navigator, 'platform', function () { return p.platform; });
    defineGetter(navigator, 'vendor', function () { return p.vendor; });
    defineGetter(navigator, 'appVersion', function () { return p.ua; });
  }

  function applyCanvasSpoof() {
    if (!CFG.spoofCanvas) return;
    try {
      var origToDataURL = HTMLCanvasElement.prototype.toDataURL;
      HTMLCanvasElement.prototype.toDataURL = function () {
        try {
          var ctx = this.getContext('2d');
          if (ctx) {
            var d = ctx.getImageData(0, 0, this.width, this.height);
            for (var i = 0; i < d.data.length; i += 101) { d.data[i] = d.data[i] ^ 1; }
            ctx.putImageData(d, 0, 0);
          }
        } catch (e) {}
        return origToDataURL.apply(this, arguments);
      };
      var origGetImageData = CanvasRenderingContext2D.prototype.getImageData;
      CanvasRenderingContext2D.prototype.getImageData = function () {
        var d = origGetImageData.apply(this, arguments);
        for (var i = 0; i < d.data.length; i += 149) { d.data[i] = d.data[i] ^ 1; }
        return d;
      };
    } catch (e) {}
  }

  function applyWebGLSpoof() {
    if (!CFG.spoofWebGL) return;
    try {
      [window.WebGLRenderingContext, window.WebGL2RenderingContext].forEach(function (Ctx) {
        if (!Ctx) return;
        var orig = Ctx.prototype.getParameter;
        Ctx.prototype.getParameter = function (param) {
          if (param === 37445) return 'Intel Inc.';
          if (param === 37446) return 'Intel Iris OpenGL Engine';
          return orig.apply(this, arguments);
        };
      });
    } catch (e) {}
  }

  function applyTimezoneSpoof() {
    if (!CFG.spoofTimezone) return;
    try {
      var offset = Number(CFG.timezoneOffset) || 0;
      var OrigDate = Date;
      Date.prototype.getTimezoneOffset = function () { return offset; };
    } catch (e) {}
  }

  function applyGeoSpoof() {
    if (!CFG.spoofGeo || !navigator.geolocation) return;
    try {
      var fakePos = {
        coords: {
          latitude: Number(CFG.geoLat), longitude: Number(CFG.geoLng),
          accuracy: 10, altitude: null, altitudeAccuracy: null, heading: null, speed: null
        },
        timestamp: Date.now()
      };
      navigator.geolocation.getCurrentPosition = function (success) { success && success(fakePos); };
      navigator.geolocation.watchPosition = function (success) { success && success(fakePos); return 1; };
    } catch (e) {}
  }

  function applyReferrerSpoof() {
    if (!CFG.spoofReferrer) return;
    defineGetter(document, 'referrer', function () { return CFG.customReferrer || ''; });
  }

  function applyWebRTCBlock() {
    if (!CFG.blockWebRTC) return;
    try {
      ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection'].forEach(function (k) {
        if (window[k]) {
          window[k] = function () { throw new Error('WebRTC disabled by Via Ultimate Optimizer'); };
        }
      });
    } catch (e) {}
  }

  function initSpoofing() {
    applyUASpoof();
    applyCanvasSpoof();
    applyWebGLSpoof();
    applyTimezoneSpoof();
    applyGeoSpoof();
    applyReferrerSpoof();
    applyWebRTCBlock();
  }
  initSpoofing();

  /* ============================================================
     3. AD / TRACKER BLOCKER
  ============================================================ */
  function initAdBlock() {
    if (!CFG.blockAds) return;
    try {
      var style = document.createElement('style');
      style.textContent = '[class*="ad-banner"],[id*="google_ads"],.adsbygoogle,.ad-container,.sponsored-content,ins.adsbygoogle,[id^="div-gpt-ad"]{display:none !important;}';
      document.documentElement.appendChild(style);
    } catch (e) {}

    function purge(root) {
      if (!root.querySelectorAll) return;
      root.querySelectorAll('script[src],iframe[src]').forEach(function (el) {
        var src = el.src || '';
        if (AD_PATTERNS.some(function (p) { return p.test(src); })) el.remove();
      });
    }
    if (document.body) purge(document);
    var mo = new MutationObserver(function (muts) {
      muts.forEach(function (m) {
        m.addedNodes.forEach(function (n) {
          if (n.nodeType !== 1) return;
          var src = n.src || '';
          if ((n.tagName === 'SCRIPT' || n.tagName === 'IFRAME') && AD_PATTERNS.some(function (p) { return p.test(src); })) {
            n.remove();
          } else {
            purge(n);
          }
        });
      });
    });
    mo.observe(document.documentElement, { childList: true, subtree: true });
  }

  /* ============================================================
     4. SPEED / PAGE LOAD OPTIMIZATION
  ============================================================ */
  function initSpeedBoost() {
    if (!CFG.speedBoost) return;

    if (CFG.preconnect) {
      var hosts = new Set();
      document.querySelectorAll('a[href],link[href],script[src],img[src]').forEach(function (el) {
        var url = el.href || el.src;
        if (!url) return;
        try {
          var u = new URL(url, location.href);
          if (u.origin !== location.origin) hosts.add(u.origin);
        } catch (e) {}
      });
      var count = 0;
      hosts.forEach(function (origin) {
        if (count++ > 12) return;
        try {
          var l1 = document.createElement('link'); l1.rel = 'dns-prefetch'; l1.href = origin; document.head.appendChild(l1);
          var l2 = document.createElement('link'); l2.rel = 'preconnect'; l2.href = origin; l2.crossOrigin = 'anonymous'; document.head.appendChild(l2);
        } catch (e) {}
      });
    }

    if (CFG.lazyImages) {
      function lazify(img) {
        if (!img.hasAttribute('loading')) img.loading = 'lazy';
        img.decoding = 'async';
      }
      var imgs = document.querySelectorAll('img');
      imgs.forEach(function (img, idx) {
        if (idx === 0) { try { img.setAttribute('fetchpriority', 'high'); img.loading = 'eager'; } catch (e) {} }
        else lazify(img);
      });
      var mo2 = new MutationObserver(function (muts) {
        muts.forEach(function (m) {
          m.addedNodes.forEach(function (n) {
            if (n.nodeType !== 1) return;
            if (n.tagName === 'IMG') lazify(n);
            if (n.querySelectorAll) n.querySelectorAll('img').forEach(lazify);
          });
        });
      });
      mo2.observe(document.documentElement, { childList: true, subtree: true });
    }

    if (CFG.reduceMotion) {
      var s = document.createElement('style');
      s.textContent = '*{animation-duration:.001s !important;transition-duration:.001s !important;scroll-behavior:auto !important;}';
      document.documentElement.appendChild(s);
    }
  }

  /* ============================================================
     5. DARK MODE
  ============================================================ */
  function initDarkMode() {
    if (!CFG.darkMode) return;
    var s = document.createElement('style');
    s.id = 'via-dark-mode-style';
    s.textContent = 'html{filter:invert(1) hue-rotate(180deg) !important;background:#fff;}' +
      'img,video,picture,canvas,iframe,svg,[style*="background-image"]{filter:invert(1) hue-rotate(180deg) !important;}';
    document.documentElement.appendChild(s);
  }
  function toggleDarkModeLive(on) {
    var s = document.getElementById('via-dark-mode-style');
    if (on && !s) { CFG.darkMode = true; initDarkMode(); }
    else if (!on && s) { s.remove(); CFG.darkMode = false; }
  }

  /* ============================================================
     6. VIDEO SPEED CONTROLLER
  ============================================================ */
  function initVideoSpeedController() {
    if (!CFG.videoSpeedController) return;
    document.addEventListener('keydown', function (e) {
      var v = document.querySelector('video');
      if (!v) return;
      if (e.key === '>' || e.key === '.') { v.playbackRate = Math.min(4, +(v.playbackRate + 0.25).toFixed(2)); toast('Speed: ' + v.playbackRate + 'x'); }
      if (e.key === '<' || e.key === ',') { v.playbackRate = Math.max(0.25, +(v.playbackRate - 0.25).toFixed(2)); toast('Speed: ' + v.playbackRate + 'x'); }
    });
  }
  function setAllVideoSpeed(rate) {
    document.querySelectorAll('video').forEach(function (v) { v.playbackRate = rate; });
    toast('Video speed set to ' + rate + 'x');
  }

  /* ============================================================
     7. CUSTOM CSS INJECTOR
  ============================================================ */
  function applyCustomCSS() {
    var el = document.getElementById('via-custom-css');
    if (!el) {
      el = document.createElement('style');
      el.id = 'via-custom-css';
      document.documentElement.appendChild(el);
    }
    el.textContent = CFG.customCSS || '';
  }

  /* ============================================================
     8. LIVE MEDIA URL GRABBER
  ============================================================ */
  var capturedMedia = new Map();
  var mediaListEl = null;

  function formatBytes(bytes) {
    if (!bytes) return '?';
    var units = ['B', 'KB', 'MB', 'GB'];
    var i = 0;
    while (bytes >= 1024 && i < units.length - 1) { bytes /= 1024; i++; }
    return bytes.toFixed(1) + ' ' + units[i];
  }

  function registerMedia(url, type, extra) {
    if (!url || url.indexOf('data:') === 0) return;
    if (!capturedMedia.has(url)) {
      capturedMedia.set(url, Object.assign({ type: type, ts: Date.now() }, extra || {}));
      renderMediaList();
    }
  }

  function scanMedia(root) {
    root = root || document;
    if (!root.querySelectorAll) return;
    root.querySelectorAll('video,audio').forEach(function (m) {
      if (m.currentSrc) registerMedia(m.currentSrc, m.tagName.toLowerCase());
      if (m.src) registerMedia(m.src, m.tagName.toLowerCase());
      m.querySelectorAll('source[src]').forEach(function (s) { registerMedia(s.src, m.tagName.toLowerCase()); });
    });
    root.querySelectorAll('img[src]').forEach(function (img) {
      if (img.naturalWidth > 80 || !img.complete) registerMedia(img.src, 'image');
    });
    try {
      root.querySelectorAll('*').forEach(function (el) {
        var bg = getComputedStyle(el).backgroundImage;
        if (bg && bg.indexOf('url(') !== -1) {
          var m = bg.match(/url\(["']?([^"')]+)["']?\)/);
          if (m) { try { registerMedia(new URL(m[1], location.href).href, 'background'); } catch (e) {} }
        }
      });
    } catch (e) {}
  }

  (function hookCreateObjectURL() {
    try {
      var orig = URL.createObjectURL.bind(URL);
      URL.createObjectURL = function (obj) {
        var url = orig(obj);
        try {
          if (obj instanceof Blob && obj.type && /^(video|audio|image)/.test(obj.type)) {
            registerMedia(url, obj.type.split('/')[0], { size: obj.size, mime: obj.type, blob: true });
          }
        } catch (e) {}
        return url;
      };
    } catch (e) {}
  })();

  (function hookPerformanceObserver() {
    try {
      var po = new PerformanceObserver(function (list) {
        list.getEntries().forEach(function (entry) {
          if (['video', 'audio', 'img'].indexOf(entry.initiatorType) !== -1 ||
              /\.(mp4|m3u8|webm|mp3|m4a|ogg|mov|ts|flac|wav)(\?|$)/i.test(entry.name)) {
            registerMedia(entry.name, entry.initiatorType || 'media', { size: entry.transferSize });
          }
        });
      });
      po.observe({ type: 'resource', buffered: true });
    } catch (e) {}
  })();

  function initMediaGrabber() {
    if (!CFG.mediaGrabber) return;
    scanMedia(document);
    var mo3 = new MutationObserver(function () { scanMedia(document); });
    mo3.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'style'] });
  }

  function renderMediaList() {
    if (!mediaListEl) return;
    mediaListEl.innerHTML = '';
    if (capturedMedia.size === 0) {
      mediaListEl.innerHTML = '<div style="color:#6b7280;font-size:12px;">No media detected yet. Scroll/play media on the page.</div>';
      return;
    }
    var arr = Array.from(capturedMedia.entries()).reverse();
    arr.forEach(function (entry) {
      var url = entry[0], data = entry[1];
      var row = document.createElement('div');
      row.className = 'via-opt-media-item';
      row.innerHTML =
        '<div style="flex:1;overflow:hidden;">' +
          '<div style="color:#a78bfa;font-weight:600;">[' + data.type + ']' + (data.size ? ' · ' + formatBytes(data.size) : '') + '</div>' +
          '<div style="word-break:break-all;color:#d1d5db;">' + url + '</div>' +
        '</div>';
      var btns = document.createElement('div');
      btns.style.cssText = 'display:flex;flex-direction:column;gap:4px;';

      var copyBtn = document.createElement('button');
      copyBtn.className = 'via-opt-btn secondary'; copyBtn.textContent = 'Copy';
      copyBtn.onclick = function () { copyText(url); toast('URL copied'); };

      var openBtn = document.createElement('button');
      openBtn.className = 'via-opt-btn secondary'; openBtn.textContent = 'Open';
      openBtn.onclick = function () { window.open(url, '_blank'); };

      var dlBtn = document.createElement('button');
      dlBtn.className = 'via-opt-btn'; dlBtn.textContent = 'Download';
      dlBtn.onclick = function () { goToDownloadTab(url); };

      btns.appendChild(copyBtn); btns.appendChild(openBtn); btns.appendChild(dlBtn);
      row.appendChild(btns);
      mediaListEl.appendChild(row);
    });
  }

  function copyText(text) {
    try {
      navigator.clipboard.writeText(text);
    } catch (e) {
      var ta = document.createElement('textarea');
      ta.value = text; document.body.appendChild(ta); ta.select();
      try { document.execCommand('copy'); } catch (e2) {}
      ta.remove();
    }
  }

  /* ============================================================
     9. TURBO PARALLEL DOWNLOADER
  ============================================================ */
  function filenameFromUrl(url) {
    try {
      var u = new URL(url);
      var parts = u.pathname.split('/');
      return decodeURIComponent(parts[parts.length - 1]) || 'download';
    } catch (e) { return 'download_' + Date.now(); }
  }

  function saveBlob(blob, filename) {
    var a = document.createElement('a');
    var u = URL.createObjectURL(blob);
    a.href = u; a.download = filename;
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(function () { URL.revokeObjectURL(u); }, 15000);
  }

  async function simpleDownload(url, onProgress) {
    var res = await fetch(url);
    var len = +res.headers.get('content-length') || 0;
    var reader = res.body.getReader();
    var loaded = 0; var parts = [];
    while (true) {
      var r = await reader.read();
      if (r.done) break;
      parts.push(r.value); loaded += r.value.length;
      if (len && onProgress) onProgress(loaded / len);
    }
    saveBlob(new Blob(parts), filenameFromUrl(url));
  }

  async function turboDownload(url, threads, onProgress) {
    threads = threads || 4;
    try {
      var head = await fetch(url, { method: 'HEAD' });
      var len = +head.headers.get('content-length');
      var acceptRanges = head.headers.get('accept-ranges');
      if (!len || acceptRanges !== 'bytes') { return simpleDownload(url, onProgress); }

      var chunkSize = Math.ceil(len / threads);
      var chunks = new Array(threads);
      var loaded = 0;

      await Promise.all(Array.from({ length: threads }).map(async function (_, i) {
        var start = i * chunkSize;
        var end = Math.min(len - 1, start + chunkSize - 1);
        var res = await fetch(url, { headers: { Range: 'bytes=' + start + '-' + end } });
        var reader = res.body.getReader();
        var parts = [];
        while (true) {
          var r = await reader.read();
          if (r.done) break;
          parts.push(r.value); loaded += r.value.length;
          if (onProgress) onProgress(loaded / len);
        }
        chunks[i] = new Blob(parts);
      }));

      saveBlob(new Blob(chunks), filenameFromUrl(url));
    } catch (err) {
      console.error('Turbo download failed, falling back to simple download', err);
      simpleDownload(url, onProgress);
    }
  }

  /* ============================================================
     10. INSPECTOR: FULL DEVTOOLS (Eruda) + QUICK INSPECT (offline)
  ============================================================ */
  function loadEruda() {
    if (window.eruda) { window.eruda.show(); toast('Inspector shown'); return; }
    var s = document.createElement('script');
    s.src = 'https://cdn.jsdelivr.net/npm/eruda';
    s.onload = function () {
      try {
        window.eruda.init({ tool: ['console', 'elements', 'network', 'resources', 'info', 'snippets'] });
        window.eruda.show();
        toast('DevTools ready');
      } catch (e) { toast('Inspector init failed'); }
    };
    s.onerror = function () { toast('Failed to load DevTools (offline?)'); };
    document.body.appendChild(s);
  }

  var quickInspectActive = false, qiOverlay = null, qiInfoBox = null;
  function qiEnsure() {
    if (!qiOverlay) { qiOverlay = document.createElement('div'); qiOverlay.className = 'via-opt-qi-overlay'; document.body.appendChild(qiOverlay); }
    if (!qiInfoBox) { qiInfoBox = document.createElement('div'); qiInfoBox.className = 'via-opt-qi-info'; document.body.appendChild(qiInfoBox); }
  }
  function qiHighlight(e) {
    if (!quickInspectActive) return;
    qiEnsure();
    var el = e.target;
    if (el.closest && el.closest('.via-opt-panel,.via-opt-fab,.via-opt-qi-info')) return;
    var r = el.getBoundingClientRect();
    qiOverlay.style.cssText = 'position:fixed;left:' + r.left + 'px;top:' + r.top + 'px;width:' + r.width + 'px;height:' + r.height + 'px;background:rgba(59,130,246,.3);border:1px solid #3b82f6;z-index:2147483646;pointer-events:none;display:block;';
    var cs = getComputedStyle(el);
    qiInfoBox.textContent = '<' + el.tagName.toLowerCase() + '> id="' + el.id + '" class="' + el.className + '" | ' + Math.round(r.width) + 'x' + Math.round(r.height) + ' | font:' + cs.fontSize + ' color:' + cs.color;
    qiInfoBox.style.display = 'block';
  }
  function qiClick(e) {
    if (!quickInspectActive) return;
    e.preventDefault(); e.stopPropagation();
    var el = e.target;
    var val = prompt('Edit inline style for <' + el.tagName.toLowerCase() + '>', el.style.cssText || '');
    if (val !== null) el.style.cssText = val;
  }
  function toggleQuickInspect() {
    quickInspectActive = !quickInspectActive;
    if (quickInspectActive) {
      document.addEventListener('mouseover', qiHighlight, true);
      document.addEventListener('click', qiClick, true);
      toast('Quick Inspect ON — tap any element');
    } else {
      document.removeEventListener('mouseover', qiHighlight, true);
      document.removeEventListener('click', qiClick, true);
      if (qiOverlay) qiOverlay.style.display = 'none';
      if (qiInfoBox) qiInfoBox.style.display = 'none';
      toast('Quick Inspect OFF');
    }
  }

  /* ============================================================
     11. FLOATING UI: FAB + TABBED PANEL
  ============================================================ */
  var goToDownloadTabUrl = null;
  function goToDownloadTab(url) {
    goToDownloadTabUrl = url;
    openPanel('download');
  }

  function injectStyles() {
    var css = document.createElement('style');
    css.textContent =
      '.via-opt-fab{position:fixed;' + (CFG.fabSide === 'left' ? 'left:16px;' : 'right:16px;') + 'bottom:16px;width:54px;height:54px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;font-size:24px;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px rgba(0,0,0,.5);z-index:2147483000;cursor:pointer;border:none;}' +
      '.via-opt-panel{position:fixed;left:0;right:0;bottom:0;height:84vh;max-height:84vh;background:#0f1420;color:#e5e7eb;border-radius:18px 18px 0 0;box-shadow:0 -8px 30px rgba(0,0,0,.6);z-index:2147483001;display:flex;flex-direction:column;font-family:system-ui,sans-serif;transform:translateY(100%);transition:transform .25s ease;}' +
      '.via-opt-panel.open{transform:translateY(0);}' +
      '.via-opt-header{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid #1f2937;}' +
      '.via-opt-tabs{display:flex;overflow-x:auto;border-bottom:1px solid #1f2937;}' +
      '.via-opt-tab{flex:1;padding:10px 4px;text-align:center;font-size:18px;cursor:pointer;color:#9ca3af;white-space:nowrap;}' +
      '.via-opt-tab.active{color:#fff;border-bottom:2px solid #8b5cf6;background:rgba(139,92,246,.08);}' +
      '.via-opt-content{flex:1;overflow-y:auto;padding:14px;font-size:13px;}' +
      '.via-opt-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 0;border-bottom:1px solid #1f2937;}' +
      '.via-opt-row .lbl{flex:1;}' +
      '.via-opt-row small{color:#9ca3af;display:block;margin-top:2px;}' +
      '.via-opt-btn{background:#8b5cf6;color:#fff;border:none;border-radius:8px;padding:8px 12px;font-size:12px;cursor:pointer;}' +
      '.via-opt-btn.secondary{background:#374151;}' +
      '.via-opt-btn.block{display:block;width:100%;margin-top:8px;}' +
      '.via-opt-input,.via-opt-select,.via-opt-textarea{width:100%;background:#1f2937;color:#fff;border:1px solid #374151;border-radius:6px;padding:7px 8px;font-size:12px;margin-top:4px;box-sizing:border-box;}' +
      '.via-opt-textarea{min-height:90px;font-family:monospace;}' +
      '.via-opt-media-item{display:flex;gap:8px;align-items:flex-start;padding:8px 0;border-bottom:1px solid #1f2937;font-size:11px;}' +
      '.via-opt-progress{height:10px;background:#1f2937;border-radius:6px;overflow:hidden;margin-top:8px;}' +
      '.via-opt-progress > div{height:100%;background:linear-gradient(90deg,#8b5cf6,#3b82f6);width:0%;transition:width .15s;}' +
      '#via-opt-toast{position:fixed;left:50%;bottom:82px;transform:translate(-50%,20px);background:#111;color:#fff;padding:9px 16px;border-radius:8px;font-size:12px;z-index:2147483647;opacity:0;transition:all .25s;pointer-events:none;}' +
      '#via-opt-toast.show{opacity:1;transform:translate(-50%,0);}' +
      '.via-opt-qi-overlay{position:fixed;display:none;pointer-events:none;}' +
      '.via-opt-qi-info{position:fixed;left:8px;bottom:8px;display:none;max-width:92vw;background:#000;color:#22c55e;font:11px monospace;padding:6px 8px;border-radius:6px;z-index:2147483647;}' +
      '.via-opt-switch{width:38px;height:22px;border-radius:11px;background:#374151;position:relative;cursor:pointer;flex-shrink:0;}' +
      '.via-opt-switch.on{background:#8b5cf6;}' +
      '.via-opt-switch .dot{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;transition:left .15s;}' +
      '.via-opt-switch.on .dot{left:18px;}';
    document.documentElement.appendChild(css);
  }

  function makeSwitch(key, onToggle) {
    var sw = document.createElement('div');
    sw.className = 'via-opt-switch' + (CFG[key] ? ' on' : '');
    sw.innerHTML = '<div class="dot"></div>';
    sw.onclick = function () {
      CFG[key] = !CFG[key];
      sw.classList.toggle('on', CFG[key]);
      saveSettings();
      if (onToggle) onToggle(CFG[key]);
    };
    return sw;
  }

  function makeRow(label, hint, control) {
    var row = document.createElement('div');
    row.className = 'via-opt-row';
    var lbl = document.createElement('div');
    lbl.className = 'lbl';
    lbl.innerHTML = label + (hint ? '<small>' + hint + '</small>' : '');
    row.appendChild(lbl);
    row.appendChild(control);
    return row;
  }

  var panelEl, contentEl, tabsEl;
  var TABS = [
    { id: 'home', icon: '🏠', render: renderHome },
    { id: 'media', icon: '🎬', render: renderMedia },
    { id: 'download', icon: '⬇', render: renderDownload },
    { id: 'spoof', icon: '🎭', render: renderSpoof },
    { id: 'inspect', icon: '🔍', render: renderInspect },
    { id: 'settings', icon: '⚙', render: renderSettings }
  ];
  var activeTab = 'home';

  function buildUI() {
    injectStyles();

    var fab = document.createElement('button');
    fab.className = 'via-opt-fab';
    fab.textContent = '⚡';
    fab.onclick = function () { openPanel(activeTab); };
    document.body.appendChild(fab);

    panelEl = document.createElement('div');
    panelEl.className = 'via-opt-panel';
    panelEl.innerHTML =
      '<div class="via-opt-header">' +
        '<b style="color:#fff;">⚡ Via Ultimate Optimizer</b>' +
        '<button class="via-opt-btn secondary" id="via-opt-close">✕ Close</button>' +
      '</div>';
    tabsEl = document.createElement('div');
    tabsEl.className = 'via-opt-tabs';
    TABS.forEach(function (t) {
      var el = document.createElement('div');
      el.className = 'via-opt-tab' + (t.id === activeTab ? ' active' : '');
      el.textContent = t.icon;
      el.onclick = function () { switchTab(t.id); };
      el.dataset.tab = t.id;
      tabsEl.appendChild(el);
    });
    panelEl.appendChild(tabsEl);

    contentEl = document.createElement('div');
    contentEl.className = 'via-opt-content';
    panelEl.appendChild(contentEl);
    document.body.appendChild(panelEl);

    panelEl.querySelector('#via-opt-close').onclick = function () { panelEl.classList.remove('open'); };

    switchTab('home');
  }

  function openPanel(tabId) {
    if (!panelEl) buildUI();
    switchTab(tabId || 'home');
    panelEl.classList.add('open');
  }

  function switchTab(id) {
    activeTab = id;
    Array.from(tabsEl.children).forEach(function (el) { el.classList.toggle('active', el.dataset.tab === id); });
    var tab = TABS.filter(function (t) { return t.id === id; })[0];
    contentEl.innerHTML = '';
    if (tab) tab.render(contentEl);
  }

  function renderHome(c) {
    c.appendChild(makeRow('⚡ Speed Boost', 'Preconnect + lazy images + defer', makeSwitch('speedBoost', function () { toast('Reload page to fully apply'); })));
    c.appendChild(makeRow('🚫 Ad/Tracker Block', 'Remove known ad/tracker scripts', makeSwitch('blockAds', function () { toast('Reload page to fully apply'); })));
    c.appendChild(makeRow('🌙 Dark Mode', 'Invert-filter dark theme', makeSwitch('darkMode', toggleDarkModeLive)));
    c.appendChild(makeRow('🎞 Reduce Motion', 'Kill animations/transitions', makeSwitch('reduceMotion', function () { toast('Reload page to fully apply'); })));
    c.appendChild(makeRow('🎬 Media Grabber', 'Live-scan media URLs', makeSwitch('mediaGrabber', function () { toast('Reload page to fully apply'); })));
    c.appendChild(makeRow('🎚 Video Speed Ctrl', 'Use < / > keys on video', makeSwitch('videoSpeedController', function () { toast('Reload page to fully apply'); })));

    var speedRow = document.createElement('div');
    speedRow.style.marginTop = '14px';
    speedRow.innerHTML = '<div class="lbl" style="margin-bottom:6px;">Quick video speed</div>';
    var wrap = document.createElement('div');
    wrap.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap;';
    [0.5, 1, 1.5, 2, 3].forEach(function (r) {
      var b = document.createElement('button');
      b.className = 'via-opt-btn secondary';
      b.textContent = r + 'x';
      b.onclick = function () { setAllVideoSpeed(r); };
      wrap.appendChild(b);
    });
    speedRow.appendChild(wrap);
    c.appendChild(speedRow);

    var reload = document.createElement('button');
    reload.className = 'via-opt-btn block';
    reload.textContent = '🔄 Reload page now';
    reload.onclick = function () { location.reload(); };
    c.appendChild(reload);
  }

  function renderMedia(c) {
    var toolbar = document.createElement('div');
    toolbar.style.cssText = 'display:flex;gap:8px;margin-bottom:10px;';
    var scanBtn = document.createElement('button'); scanBtn.className = 'via-opt-btn'; scanBtn.textContent = '🔄 Rescan';
    scanBtn.onclick = function () { scanMedia(document); toast('Rescanned page'); };
    var copyAllBtn = document.createElement('button'); copyAllBtn.className = 'via-opt-btn secondary'; copyAllBtn.textContent = '📋 Copy All';
    copyAllBtn.onclick = function () { copyText(Array.from(capturedMedia.keys()).join('\\n')); toast('All URLs copied'); };
    var clearBtn = document.createElement('button'); clearBtn.className = 'via-opt-btn secondary'; clearBtn.textContent = '🗑 Clear';
    clearBtn.onclick = function () { capturedMedia.clear(); renderMediaList(); };
    toolbar.appendChild(scanBtn); toolbar.appendChild(copyAllBtn); toolbar.appendChild(clearBtn);
    c.appendChild(toolbar);

    mediaListEl = document.createElement('div');
    c.appendChild(mediaListEl);
    renderMediaList();
  }

  function renderDownload(c) {
    c.innerHTML =
      '<div class="lbl">Direct file URL</div>' +
      '<input class="via-opt-input" id="via-opt-dl-url" placeholder="https://example.com/file.mp4" />' +
      '<div class="lbl" style="margin-top:10px;">Threads</div>' +
      '<select class="via-opt-select" id="via-opt-dl-threads">' +
        '<option value="1">1 (normal)</option>' +
        '<option value="4" selected>4 (fast)</option>' +
        '<option value="8">8 (turbo)</option>' +
        '<option value="16">16 (max)</option>' +
      '</select>' +
      '<button class="via-opt-btn block" id="via-opt-dl-start">⬇ Start Turbo Download</button>' +
      '<div class="via-opt-progress"><div id="via-opt-dl-bar"></div></div>' +
      '<div id="via-opt-dl-status" style="margin-top:6px;color:#9ca3af;font-size:11px;"></div>';

    var urlInput = c.querySelector('#via-opt-dl-url');
    if (goToDownloadTabUrl) { urlInput.value = goToDownloadTabUrl; goToDownloadTabUrl = null; }

    c.querySelector('#via-opt-dl-start').onclick = function () {
      var url = urlInput.value.trim();
      if (!url) { toast('Enter a URL first'); return; }
      var threads = +c.querySelector('#via-opt-dl-threads').value;
      var bar = c.querySelector('#via-opt-dl-bar');
      var status = c.querySelector('#via-opt-dl-status');
      var startTime = Date.now();
      status.textContent = 'Starting…';
      turboDownload(url, threads, function (pct) {
        bar.style.width = (pct * 100).toFixed(1) + '%';
        var elapsed = (Date.now() - startTime) / 1000;
        status.textContent = (pct * 100).toFixed(1) + '% · ' + elapsed.toFixed(1) + 's elapsed';
      }).then(function () {
        status.textContent = '✅ Download complete!';
        toast('Download saved');
      });
    };
  }

  function renderSpoof(c) {
    c.appendChild(makeRow('🎭 Spoof User-Agent', '', makeSwitch('spoofUA')));
    var uaSel = document.createElement('select');
    uaSel.className = 'via-opt-select';
    Object.keys(UA_PRESETS).forEach(function (k) {
      var o = document.createElement('option'); o.value = k; o.textContent = k;
      if (CFG.uaPreset === k) o.selected = true;
      uaSel.appendChild(o);
    });
    uaSel.onchange = function () { CFG.uaPreset = uaSel.value; saveSettings(); };
    c.appendChild(uaSel);

    c.appendChild(makeRow('🖼 Canvas noise', 'Anti-fingerprint', makeSwitch('spoofCanvas')));
    c.appendChild(makeRow('🎮 WebGL spoof', 'Fake GPU vendor/renderer', makeSwitch('spoofWebGL')));
    c.appendChild(makeRow('🕒 Spoof timezone', '', makeSwitch('spoofTimezone')));
    var tzInput = document.createElement('input');
    tzInput.className = 'via-opt-input'; tzInput.type = 'number'; tzInput.placeholder = 'Offset in minutes e.g. -120';
    tzInput.value = CFG.timezoneOffset;
    tzInput.onchange = function () { CFG.timezoneOffset = +tzInput.value; saveSettings(); };
    c.appendChild(tzInput);

    c.appendChild(makeRow('📍 Fake geolocation', '', makeSwitch('spoofGeo')));
    var geoWrap = document.createElement('div'); geoWrap.style.cssText = 'display:flex;gap:6px;';
    var latInput = document.createElement('input'); latInput.className = 'via-opt-input'; latInput.placeholder = 'Lat'; latInput.value = CFG.geoLat;
    var lngInput = document.createElement('input'); lngInput.className = 'via-opt-input'; lngInput.placeholder = 'Lng'; lngInput.value = CFG.geoLng;
    latInput.onchange = function () { CFG.geoLat = +latInput.value; saveSettings(); };
    lngInput.onchange = function () { CFG.geoLng = +lngInput.value; saveSettings(); };
    geoWrap.appendChild(latInput); geoWrap.appendChild(lngInput);
    c.appendChild(geoWrap);

    c.appendChild(makeRow('🔗 Spoof referrer', '', makeSwitch('spoofReferrer')));
    var refInput = document.createElement('input');
    refInput.className = 'via-opt-input'; refInput.placeholder = 'https://google.com/';
    refInput.value = CFG.customReferrer;
    refInput.onchange = function () { CFG.customReferrer = refInput.value; saveSettings(); };
    c.appendChild(refInput);

    c.appendChild(makeRow('📡 Block WebRTC leak', 'Prevents local IP exposure', makeSwitch('blockWebRTC')));

    var note = document.createElement('div');
    note.style.cssText = 'color:#f59e0b;font-size:11px;margin-top:10px;';
    note.textContent = '⚠ Spoofing settings apply on next page load (document-start).';
    c.appendChild(note);

    var reload = document.createElement('button');
    reload.className = 'via-opt-btn block';
    reload.textContent = '🔄 Apply & Reload';
    reload.onclick = function () { location.reload(); };
    c.appendChild(reload);
  }

  function renderInspect(c) {
    c.innerHTML =
      '<div class="lbl" style="margin-bottom:10px;">Full Chrome-style DevTools (Elements, Console, Network, Storage, Resources) powered by Eruda.</div>' +
      '<button class="via-opt-btn block" id="via-opt-eruda">🔍 Launch Full DevTools</button>' +
      '<div class="lbl" style="margin-top:16px;margin-bottom:6px;">Offline element inspector — works without internet. Tap an element to view & edit its inline style.</div>' +
      '<button class="via-opt-btn secondary block" id="via-opt-qi">🎯 Toggle Quick Inspect</button>';
    c.querySelector('#via-opt-eruda').onclick = loadEruda;
    c.querySelector('#via-opt-qi').onclick = toggleQuickInspect;
  }

  function renderSettings(c) {
    c.innerHTML =
      '<div class="lbl">Custom CSS (applies live to this site)</div>' +
      '<textarea class="via-opt-textarea" id="via-opt-css" placeholder="body{ font-size:18px; }">' + (CFG.customCSS || '') + '</textarea>' +
      '<button class="via-opt-btn block" id="via-opt-css-apply">✅ Apply CSS</button>' +
      '<div style="margin-top:16px;display:flex;gap:8px;">' +
        '<button class="via-opt-btn secondary" id="via-opt-export">⬆ Export Settings</button>' +
        '<label class="via-opt-btn secondary" style="cursor:pointer;">⬇ Import<input type="file" id="via-opt-import" accept=".json" style="display:none;"></label>' +
      '</div>' +
      '<button class="via-opt-btn block" id="via-opt-reset" style="background:#dc2626;margin-top:10px;">♻ Reset All Settings</button>' +
      '<div style="margin-top:16px;color:#6b7280;font-size:11px;">Via Ultimate Optimizer v1.0.0 — settings stored locally in this browser only.</div>';

    c.querySelector('#via-opt-css-apply').onclick = function () {
      CFG.customCSS = c.querySelector('#via-opt-css').value;
      saveSettings(); applyCustomCSS(); toast('CSS applied');
    };
    c.querySelector('#via-opt-export').onclick = function () {
      var blob = new Blob([JSON.stringify(CFG, null, 2)], { type: 'application/json' });
      saveBlob(blob, 'via-optimizer-settings.json');
    };
    c.querySelector('#via-opt-import').onchange = function (e) {
      var file = e.target.files[0]; if (!file) return;
      var reader = new FileReader();
      reader.onload = function () {
        try { CFG = Object.assign({}, DEFAULTS, JSON.parse(reader.result)); saveSettings(); toast('Imported! Reloading…'); setTimeout(function () { location.reload(); }, 800); }
        catch (e2) { toast('Invalid settings file'); }
      };
      reader.readAsText(file);
    };
    c.querySelector('#via-opt-reset').onclick = function () {
      if (confirm('Reset all Via Ultimate Optimizer settings?')) {
        localStorage.removeItem(STORAGE_KEY);
        toast('Reset! Reloading…');
        setTimeout(function () { location.reload(); }, 700);
      }
    };
  }

  /* ============================================================
     12. BOOTSTRAP
  ============================================================ */
  function boot() {
    initAdBlock();
    initSpeedBoost();
    initDarkMode();
    initVideoSpeedController();
    applyCustomCSS();
    initMediaGrabber();
    buildUI();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', boot);
  } else {
    boot();
  }
})();