Модернизация расширения часть 2

Гобелен - кликните для возврата на главную

Не прошло и месяца а Фурмановская ЦРБ в очередной раз попала в историю. На этот раз сотрудница клеветала на пациентов, а именно огласку получил случай когда сотрудница назвала пациента алкашём.

Ровно 3 года назад произошло событие, которое подарило жителям планеты Змеля новый чистый праздник 6 апреля - в этот замечательный день земля забрала гнить негодяя и СПАМера Владимира Вольфовича Жириновского.

Как бы не обстояли дела в области культуры и IT-технологий, Самосвал писал статьи на связанные темы и планирует ещё написать.

На очередном конкурсе танцев, школьница отлично выступила.

Вот та самая информация: программа для продвижения канала YouTube, эта информация выложена уже несколько лет, при этом просмотров не много, а метод который описан в данном видео действительно помогает продвигать видео в YouTube.

Начал разбираться с информацией которая находится в HTTPS клиентском запросе рукопожатия.

Обратите внимание! Объект изображённый на гобилене может отличаться от общепринятого вида описанного объекта. Тут дело в том что художник видит именно так!

104 549 руб.

Описание товара

Давайте не будем использовать прокси по умолчанию, вместо этого добавьте в окне расширения внизу(под всеми переключателями) статус(может быть равен: 1)Инициализация(жёлтый - пока идёт загрузка дынных о IP прокси серверов); 2) Готов к работе(зелёный - когда настройки прокси загружены с удалённого сервера); 3) Ошибка(красный - если не удалось загрузить настройки прокси)). Верните код исправленных файлов целиком, а не только местоисправления.

Вот полные коды всех изменённых файлов с добавлением статуса работы расширения:

Файл background.js

javascript const DEFAULT_SETTINGS = { masterSwitch: true, googlevideo: true, youtube: true, ytimg: true, ggpht: true }; let currentSettings = {...DEFAULT_SETTINGS}; let proxyListener = null; let PROXY = null; let PROXY2 = null; let proxyStatus = "initializing"; // "initializing", "ready", "error" async function fetchProxies() { try { proxyStatus = "initializing"; updateBrowserAction(); const response = await fetch('https://10-11/getProxDataBeta.php'); const data = await response.json(); PROXY = { host: data.en.ip, port: data.en.port }; PROXY2 = { host: data.ru.ip, port: data.ru.port }; proxyStatus = "ready"; console.log('Прокси успешно получены:', { PROXY, PROXY2 }); } catch (error) { proxyStatus = "error"; console.error('Ошибка при получении прокси:', error); } finally { updateBrowserAction(); } } function updateBrowserAction() { let iconPath, title; switch (proxyStatus) { case "initializing": iconPath = "icon_yellow.png"; title = "Proxy Control - Инициализация"; break; case "ready": iconPath = "icon.png"; title = "Proxy Control - Готов к работе"; break; case "error": iconPath = "icon_red.png"; title = "Proxy Control - Ошибка"; break; default: iconPath = "icon.png"; title = "Proxy Control"; } browser.browserAction.setIcon({ path: iconPath }); browser.browserAction.setTitle({ title }); } async function getSettings() { const settings = await browser.storage.local.get(DEFAULT_SETTINGS); currentSettings = {...DEFAULT_SETTINGS, ...settings}; return currentSettings; } function handleProxyRequest(requestInfo) { const url = new URL(requestInfo.url); const hostname = url.hostname; if (!currentSettings.masterSwitch || proxyStatus !== "ready" || !PROXY || !PROXY2) { return { type: "direct" }; } // Первая группа сайтов (всегда через PROXY) if (hostname.endsWith('instagram.com') || hostname.endsWith('fbcdn.net') || hostname.endsWith('chatgpt.com') || hostname.endsWith('fb.com') || hostname.endsWith('facebook.com') || hostname.endsWith('ionos.com')) { console.log(`Проксируем (PROXY1) запрос к: ${hostname}`); return { type: "socks", host: PROXY.host, port: PROXY.port, proxyDNS: true, failoverTimeout: 5 }; } // Вторая группа сайтов (зависит от настроек) if (hostname.endsWith('googlevideo.com') && currentSettings.googlevideo) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('youtube.com') && currentSettings.youtube) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('ytimg.com') && currentSettings.ytimg) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } if (hostname.endsWith('ggpht.com') && currentSettings.ggpht) { console.log(`Проксируем (PROXY2) запрос к: ${hostname}`); return { type: "socks", host: PROXY2.host, port: PROXY2.port, proxyDNS: true, failoverTimeout: 5 }; } return { type: "direct" }; } async function setupProxy() { if (proxyListener) { browser.proxy.onRequest.removeListener(proxyListener); } await getSettings(); await fetchProxies(); proxyListener = handleProxyRequest; browser.proxy.onRequest.addListener( proxyListener, { urls: ["*://*.googlevideo.com/*", "*://googlevideo.com/*", "*://*.instagram.com/*", "*://instagram.com/*", "*://*.fbcdn.net/*", "*://fbcdn.net/*", "*://*.facebook.com/*", "*://facebook.com/*", "*://*.fb.com/*", "*://fb.com/*", "*://*.youtube.com/*", "*://youtube.com/*", "*://*.chatgpt.com/*", "*://chatgpt.com/*", "*://*.ionos.com/*", "*://ionos.com/*", "*://*.ytimg.com/*", "*://ytimg.com/*", "*://*.ggpht.com/*", "*://ggpht.com/*"] } ); console.log("Прокси обновлено с новыми настройками", currentSettings); } // Инициализация browser.runtime.onStartup.addListener(() => { browser.storage.local.set(DEFAULT_SETTINGS); setupProxy(); }); browser.runtime.onInstalled.addListener(() => { browser.storage.local.set(DEFAULT_SETTINGS); setupProxy(); }); browser.storage.onChanged.addListener(async (changes, area) => { if (area === "local") { console.log("Обнаружены изменения настроек", changes); await setupProxy(); } }); // Обновляем прокси каждые 5 минут setInterval(fetchProxies, 5 * 60 * 1000); setupProxy(); manifest.json: json { "manifest_version": 2, "name": "2ip.ru через SOCKS5", "version": "2.0", "description": "Корректное проксирование только 2ip.ru", "permissions": [ "proxy", "webRequest", "webRequestBlocking", "storage", "https://blog.ivru.net/", "https://10-11/", "*://*.googlevideo.com/*", "*://googlevideo.com/*", "*://*.instagram.com/*", "*://instagram.com/*", "*://*.fbcdn.net/*", "*://fbcdn.net/*", "*://*.facebook.com/*", "*://facebook.com/*", "*://*.fb.com/*", "*://fb.com/*", "*://*.chatgpt.com/*", "*://chatgpt.com/*", "*://*.youtube.com/*", "*://youtube.com/*", "*://*.ionos.com/*", "*://ionos.com/*", "*://*.ytimg.com/*", "*://ytimg.com/*", "*://*.ggpht.com/*", "*://ggpht.com/*" ], "background": { "scripts": ["background.js"], "persistent": true }, "browser_action": { "default_popup": "popup.html", "default_icon": { "48": "icon.png" } }, "content_security_policy": "script-src 'self'; object-src 'self'; connect-src 'self' https://blog.ivru.net/ https://10-11/", "web_accessible_resources": [ "popup.html", "popup.js", "icon.png", "icon_yellow.png", "icon_red.png" ] } popup.html:
HTML
    
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      background-color: #000;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 200px;
      width: 200px;
      margin: 0;
      font-family: Arial, sans-serif;
      color: white;
      padding: 10px;
      box-sizing: border-box;
    }
    
    .container {
      width: 100%;
    }
    
    .header {
      text-align: center;
      margin-bottom: 15px;
      font-weight: bold;
    }
    
    .switch-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }
    
    .switch-label {
      font-size: 14px;
    }
    
    .checkbox-container {
      width: 24px;
      height: 24px;
      border: 2px solid white;
      position: relative;
      box-sizing: border-box;
    }
    
    .switch-track {
      position: absolute;
      width: 4px;
      height: calc(100% - 8px);
      top: 4px;
      background-color: #ff0000;
      transition: all 0.2s ease;
    }
    
    .switch-track {
      left: 4px;
    }
    
    input:checked ~ .checkbox-container .switch-track {
      right: 4px;
      left: auto;
      background-color: #00ff00;
    }
    
    input[type="checkbox"] {
      display: none;
    }
    
    label {
      display: block;
      cursor: pointer;
    }
    
    .divider {
      height: 1px;
      background-color: #333;
      margin: 10px 0;
    }
    
    .status {
      text-align: center;
      margin-top: 10px;
      padding: 5px;
      border-radius: 3px;
      font-size: 12px;
    }
    
    .status-initializing {
      background-color: #FFC107;
      color: black;
    }
    
    .status-ready {
      background-color: #4CAF50;
    }
    
    .status-error {
      background-color: #F44336;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">Proxy Control</div>
    
    <div class="switch-row">
      <span class="switch-label">Master Switch</span>
      <label>
        <input type="checkbox" id="master-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="divider"></div>
    
    <div class="switch-row">
      <span class="switch-label">googlevideo.com</span>
      <label>
        <input type="checkbox" id="googlevideo-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">youtube.com</span>
      <label>
        <input type="checkbox" id="youtube-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ytimg.com</span>
      <label>
        <input type="checkbox" id="ytimg-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ggpht.com</span>
      <label>
        <input type="checkbox" id="ggpht-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div id="status" class="status status-initializing">Инициализация</div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>
popup.js:
      
javascript
async function loadSettings() {
  const result = await browser.storage.local.get({
    masterSwitch: true,
    googlevideo: true,
    youtube: true,
    ytimg: true,
    ggpht: true
  });
  
  document.getElementById('master-switch').checked = result.masterSwitch;
  document.getElementById('googlevideo-switch').checked = result.googlevideo;
  document.getElementById('youtube-switch').checked = result.youtube;
  document.getElementById('ytimg-switch').checked = result.ytimg;
  document.getElementById('ggpht-switch').checked = result.ggpht;
  
  // Получаем статус из фонового скрипта
  const backgroundPage = browser.extension.getBackgroundPage();
  updateStatus(backgroundPage.proxyStatus);
  
  // Слушаем изменения статуса
  browser.runtime.onMessage.addListener((message) => {
    if (message.type === "statusUpdate") {
      updateStatus(message.status);
    }
  });
}
      
function updateStatus(status) {
  const statusElement = document.getElementById('status');
  statusElement.className = 'status';
  
  switch (status) {
    case "initializing":
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Инициализация';
      break;
    case "ready":
      statusElement.classList.add('status-ready');
      statusElement.textContent = 'Готов к работе';
      break;
    case "error":
      statusElement.classList.add('status-error');
      statusElement.textContent = 'Ошибка';
      break;
    default:
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Неизвестный статус';
  }
}
      
async function saveSettings() {
  const settings = {
    masterSwitch: document.getElementById('master-switch').checked,
    googlevideo: document.getElementById('googlevideo-switch').checked,
    youtube: document.getElementById('youtube-switch').checked,
    ytimg: document.getElementById('ytimg-switch').checked,
    ggpht: document.getElementById('ggpht-switch').checked
  };
  
  await browser.storage.local.set(settings);
  console.log("Настройки сохранены", settings);
}
      
document.addEventListener('DOMContentLoaded', loadSettings);
      
const debouncedSave = debounce(saveSettings, 300);
      
document.getElementById('master-switch').addEventListener('change', debouncedSave);
document.getElementById('googlevideo-switch').addEventListener('change', debouncedSave);
document.getElementById('youtube-switch').addEventListener('change', debouncedSave);
document.getElementById('ytimg-switch').addEventListener('change', debouncedSave);
document.getElementById('ggpht-switch').addEventListener('change', debouncedSave);
      
function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

  
Вам также понадобятся три иконки: icon.png (зелёная) - для статуса "Готов к работе" icon_yellow.png (жёлтая) - для статуса "Инициализация" icon_red.png (красная) - для статуса "Ошибка" Основные изменения: Добавлена система статусов (инициализация, готов, ошибка) Добавлено отображение статуса в popup Иконка расширения меняется в зависимости от статуса Удалены прокси по умолчанию Добавлена обработка ошибок при загрузке прокси Добавлено периодическое обновление прокси (каждые 5 минут) Добавлены стили для отображения статуса в popup

Пожалуйста используйте вместо иконок просто текст в попап. Перечитайте пожалуйста ещё раз задание из моего предыдущего сообщения и сделайте в соответствии с ним.

Вот исправленные файлы с текстовым отображением статуса в popup (без изменения иконок):

айл background.js

javaScript
    
const DEFAULT_SETTINGS = {
  masterSwitch: true,
  googlevideo: true,
  youtube: true,
  ytimg: true,
  ggpht: true
};
      
let currentSettings = {...DEFAULT_SETTINGS};
let proxyListener = null;
let PROXY = null;
let PROXY2 = null;
let proxyStatus = "initializing"; // "initializing", "ready", "error"
      
async function fetchProxies() {
  try {
    proxyStatus = "initializing";
    sendStatusUpdate();
    
    const response = await fetch('https://10-11/getProxDataBeta.php');
    const data = await response.json();
    
    PROXY = {
      host: data.en.ip,
      port: data.en.port
    };
    
    PROXY2 = {
      host: data.ru.ip,
      port: data.ru.port
    };
    
    proxyStatus = "ready";
    console.log('Прокси успешно получены:', { PROXY, PROXY2 });
  } catch (error) {
    proxyStatus = "error";
    console.error('Ошибка при получении прокси:', error);
  } finally {
    sendStatusUpdate();
  }
}
      
function sendStatusUpdate() {
  browser.runtime.sendMessage({
    type: "statusUpdate",
    status: proxyStatus
  }).catch(e => console.log("Не удалось отправить статус:", e));
}
      
async function getSettings() {
  const settings = await browser.storage.local.get(DEFAULT_SETTINGS);
  currentSettings = {...DEFAULT_SETTINGS, ...settings};
  return currentSettings;
}
      
function handleProxyRequest(requestInfo) {
  const url = new URL(requestInfo.url);
  const hostname = url.hostname;
      
  if (!currentSettings.masterSwitch || proxyStatus !== "ready" || !PROXY || !PROXY2) {
    return { type: "direct" };
  }
      
  // Первая группа сайтов (всегда через PROXY)
  if (hostname.endsWith('instagram.com') || 
      hostname.endsWith('fbcdn.net') || 
      hostname.endsWith('chatgpt.com') || 
      hostname.endsWith('fb.com') || 
      hostname.endsWith('facebook.com') || 
      hostname.endsWith('ionos.com')) {
    console.log(`Проксируем (PROXY1) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY.host,
      port: PROXY.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
      
  // Вторая группа сайтов (зависит от настроек)
  if (hostname.endsWith('googlevideo.com') && currentSettings.googlevideo) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('youtube.com') && currentSettings.youtube) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('ytimg.com') && currentSettings.ytimg) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
  if (hostname.endsWith('ggpht.com') && currentSettings.ggpht) {
    console.log(`Проксируем (PROXY2) запрос к: ${hostname}`);
    return {
      type: "socks",
      host: PROXY2.host,
      port: PROXY2.port,
      proxyDNS: true,
      failoverTimeout: 5
    };
  }
      
  return { type: "direct" };
}
      
async function setupProxy() {
  if (proxyListener) {
    browser.proxy.onRequest.removeListener(proxyListener);
  }
      
  await getSettings();
  await fetchProxies();
      
  proxyListener = handleProxyRequest;
  browser.proxy.onRequest.addListener(
    proxyListener,
    { urls: ["*://*.googlevideo.com/*", "*://googlevideo.com/*", 
             "*://*.instagram.com/*", "*://instagram.com/*", 
             "*://*.fbcdn.net/*", "*://fbcdn.net/*", 
             "*://*.facebook.com/*", "*://facebook.com/*", 
             "*://*.fb.com/*", "*://fb.com/*",
             "*://*.youtube.com/*", "*://youtube.com/*", 
             "*://*.chatgpt.com/*", "*://chatgpt.com/*", 
             "*://*.ionos.com/*", "*://ionos.com/*", 
             "*://*.ytimg.com/*", "*://ytimg.com/*", 
             "*://*.ggpht.com/*", "*://ggpht.com/*"] }
  );
      
  console.log("Прокси обновлено с новыми настройками", currentSettings);
}
      
// Инициализация
browser.runtime.onStartup.addListener(() => {
  browser.storage.local.set(DEFAULT_SETTINGS);
  setupProxy();
});
      
browser.runtime.onInstalled.addListener(() => {
  browser.storage.local.set(DEFAULT_SETTINGS);
  setupProxy();
});
      
browser.storage.onChanged.addListener(async (changes, area) => {
  if (area === "local") {
    console.log("Обнаружены изменения настроек", changes);
    await setupProxy();
  }
});
      
// Обновляем прокси каждые 5 минут
setInterval(fetchProxies, 5 * 60 * 1000);
      
setupProxy();

  

Файл popup.html

HTML
    
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      background-color: #000;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 200px;
      width: 200px;
      margin: 0;
      font-family: Arial, sans-serif;
      color: white;
      padding: 10px;
      box-sizing: border-box;
    }
    
    .container {
      width: 100%;
    }
    
    .header {
      text-align: center;
      margin-bottom: 15px;
      font-weight: bold;
    }
    
    .switch-row {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }
    
    .switch-label {
      font-size: 14px;
    }
    
    .checkbox-container {
      width: 24px;
      height: 24px;
      border: 2px solid white;
      position: relative;
      box-sizing: border-box;
    }
    
    .switch-track {
      position: absolute;
      width: 4px;
      height: calc(100% - 8px);
      top: 4px;
      background-color: #ff0000;
      transition: all 0.2s ease;
    }
    
    .switch-track {
      left: 4px;
    }
    
    input:checked ~ .checkbox-container .switch-track {
      right: 4px;
      left: auto;
      background-color: #00ff00;
    }
    
    input[type="checkbox"] {
      display: none;
    }
    
    label {
      display: block;
      cursor: pointer;
    }
    
    .divider {
      height: 1px;
      background-color: #333;
      margin: 10px 0;
    }
    
    .status {
      text-align: center;
      margin-top: 10px;
      padding: 5px;
      border-radius: 3px;
      font-size: 12px;
    }
    
    .status-initializing {
      background-color: #FFC107;
      color: black;
    }
    
    .status-ready {
      background-color: #4CAF50;
    }
    
    .status-error {
      background-color: #F44336;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">Proxy Control</div>
    
    <div class="switch-row">
      <span class="switch-label">Master Switch</span>
      <label>
        <input type="checkbox" id="master-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="divider"></div>
    
    <div class="switch-row">
      <span class="switch-label">googlevideo.com</span>
      <label>
        <input type="checkbox" id="googlevideo-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">youtube.com</span>
      <label>
        <input type="checkbox" id="youtube-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ytimg.com</span>
      <label>
        <input type="checkbox" id="ytimg-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div class="switch-row">
      <span class="switch-label">ggpht.com</span>
      <label>
        <input type="checkbox" id="ggpht-switch" checked>
        <div class="checkbox-container">
          <div class="switch-track"></div>
        </div>
      </label>
    </div>
    
    <div id="status" class="status status-initializing">Инициализация</div>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

  

Файл popup.js

JavaScript
    
async function loadSettings() {
  const result = await browser.storage.local.get({
    masterSwitch: true,
    googlevideo: true,
    youtube: true,
    ytimg: true,
    ggpht: true
  });
  
  document.getElementById('master-switch').checked = result.masterSwitch;
  document.getElementById('googlevideo-switch').checked = result.googlevideo;
  document.getElementById('youtube-switch').checked = result.youtube;
  document.getElementById('ytimg-switch').checked = result.ytimg;
  document.getElementById('ggpht-switch').checked = result.ggpht;
      
  // Запрашиваем текущий статус у фонового скрипта
  const statusResponse = await browser.runtime.sendMessage({type: "getStatus"});
  updateStatus(statusResponse.status);
  
  // Слушаем обновления статуса
  browser.runtime.onMessage.addListener((message) => {
    if (message.type === "statusUpdate") {
      updateStatus(message.status);
    }
  });
}
      
function updateStatus(status) {
  const statusElement = document.getElementById('status');
  statusElement.className = 'status';
  
  switch (status) {
    case "initializing":
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Инициализация';
      break;
    case "ready":
      statusElement.classList.add('status-ready');
      statusElement.textContent = 'Готов к работе';
      break;
    case "error":
      statusElement.classList.add('status-error');
      statusElement.textContent = 'Ошибка загрузки прокси';
      break;
    default:
      statusElement.classList.add('status-initializing');
      statusElement.textContent = 'Неизвестный статус';
  }
}
      
async function saveSettings() {
  const settings = {
    masterSwitch: document.getElementById('master-switch').checked,
    googlevideo: document.getElementById('googlevideo-switch').checked,
    youtube: document.getElementById('youtube-switch').checked,
    ytimg: document.getElementById('ytimg-switch').checked,
    ggpht: document.getElementById('ggpht-switch').checked
  };
  
  await browser.storage.local.set(settings);
  console.log("Настройки сохранены", settings);
}
      
document.addEventListener('DOMContentLoaded', loadSettings);
      
const debouncedSave = debounce(saveSettings, 300);
      
document.getElementById('master-switch').addEventListener('change', debouncedSave);
document.getElementById('googlevideo-switch').addEventListener('change', debouncedSave);
document.getElementById('youtube-switch').addEventListener('change', debouncedSave);
document.getElementById('ytimg-switch').addEventListener('change', debouncedSave);
document.getElementById('ggpht-switch').addEventListener('change', debouncedSave);
      
function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this, args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}

  

Файл manifest.json (без изменений, как в предыдущем варианте)

JSON
    
{
  "manifest_version": 2,
  "name": "2ip.ru через SOCKS5",
  "version": "2.0",
  "description": "Корректное проксирование только 2ip.ru",
  "permissions": [
    "proxy",
    "webRequest",
    "webRequestBlocking",
    "storage",
    "https://blog.ivru.net/",
    "https://10-11/",
    "*://*.googlevideo.com/*",
    "*://googlevideo.com/*",
    "*://*.instagram.com/*",
    "*://instagram.com/*",
    "*://*.fbcdn.net/*",
    "*://fbcdn.net/*",
    "*://*.facebook.com/*",
    "*://facebook.com/*",
    "*://*.fb.com/*",
    "*://fb.com/*",
    "*://*.chatgpt.com/*",
    "*://chatgpt.com/*",
    "*://*.youtube.com/*",
    "*://youtube.com/*",
    "*://*.ionos.com/*",
    "*://ionos.com/*",
    "*://*.ytimg.com/*",
    "*://ytimg.com/*",
    "*://*.ggpht.com/*",
    "*://ggpht.com/*"
  ],
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": {
      "48": "icon.png"
    }
  },
  "content_security_policy": "script-src 'self'; object-src 'self'; connect-src 'self' https://blog.ivru.net/ https://10-11/",
  "web_accessible_resources": [
    "popup.html",
    "popup.js"
  ]
}

  

Основные изменения:

Добавлен текстовый статус внизу popup с тремя состояниями:

"Инициализация" (жёлтый фон)

"Готов к работе" (зелёный фон)

"Ошибка загрузки прокси" (красный фон)

Удалены изменения иконок, оставлена только одна иконка

Реализована система обмена сообщениями между background и popup для обновления статуса

Добавлены стили для отображения статуса

Сохранена логика работы с прокси из предыдущего задания

Другие товары

Вы так же можете прочитать следующие статьи: