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.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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();
  }
})();