Random English Word Popup

Display a random word with its definition, pronunciation, and examples

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Random English Word Popup
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Display a random word with its definition, pronunciation, and examples
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @license MIT
// ==/UserScript==

(function() {
  'use strict';

  let wordsQueue = [];
  let currentWordIndex = 0;
  let popupCreated = false;
  let changeWordTimeout;
  const wordsToLoad = 50; // Загружаем по 50 слов за раз

  // Используем localStorage для хранения состояния свёрнутости
  let isPopupMinimized = localStorage.getItem('isPopupMinimized') === 'true' || false;

  function openTranslationWindow(word) {
    const parentWindowX = window.screenX;
    const parentWindowY = window.screenY + window.outerHeight;
    window.open(`https://translate.google.com/#view=home&op=translate&sl=auto&tl=ru&text=${encodeURIComponent(word)}`, '_blank', `width=500,height=500,top=${parentWindowY + 600},left=${parentWindowX + 700},popup=yes`);
  }

  GM_addStyle(`
    .word-popup-button {
      background-color: transparent;
      border: 1px solid #D3D3D3;
      color: white;
      padding: 8px 16px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 16px;
      margin: 4px 2px;
      cursor: pointer;
      border-radius: 4px;
      transition-duration: 0.4s;
    }

    .word-popup-button:hover {
      border-color: #A9A9A9;
      box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19);
    }
  `);

  function fetchWordData(word) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: `https://api.dictionaryapi.dev/api/v2/entries/en/${word}`,
        onload: function (response) {
          try {
            const wordData = JSON.parse(response.responseText)[0];
            const definition = wordData.meanings[0].definitions[0].definition;
            const phoneticSpelling = wordData.phonetics[0] ? wordData.phonetics[0].text : 'N/A';
            let examples = "-";
            for (const meaning of wordData.meanings) {
              for (const definitionItem of meaning.definitions) {
                if (definitionItem.example) {
                  examples = `"${definitionItem.example}"`;
                  break;
                }
              }
            }
            resolve({ word, phoneticSpelling, definition, examples });
          } catch (error) {
            reject(error);
          }
        },
        onerror: function (error) {
          reject(error);
        }
      });
    });
  }

  function loadNextWord() {
    // Проверяем, нужно ли загружать ещё слова
    if (wordsQueue.length >= wordsToLoad) return;

    for (let i = wordsQueue.length; i < wordsToLoad; i++) {
      GM_xmlhttpRequest({
        method: "GET",
        url: "https://random-word-api.herokuapp.com/word?number=1",
        onload: async function(response) {
          const word = JSON.parse(response.responseText)[0];
          try {
            const wordData = await fetchWordData(word);
            wordsQueue.push(wordData);

            // Если это первое слово в очереди, создаём попап и отображаем слово
            if (wordsQueue.length === 1) {
              createPopup();
              displayWord();
            }
          } catch (error) {
            console.error("Ошибка при получении данных слова:", error);
          }
        },
        onerror: function(error) {
          console.error("Ошибка при получении случайного слова:", error);
        }
      });
    }
  }

  function displayWord() {
    if (wordsQueue.length === 0) return;

    const wordInfo = wordsQueue[currentWordIndex];
    $('#word').text(wordInfo.word);
    $('#phonetic').html(`<br><span style="font-size: smaller; color: lightgrey;">[${wordInfo.phoneticSpelling}]</span>`);
    $('#definition').text(wordInfo.definition);
    $('#examples').text(`Examples: ${wordInfo.examples}`);
  }

  function resetChangeWordTimer() {
    clearTimeout(changeWordTimeout);
    changeWordTimeout = setTimeout(showNextWord, 60000);
  }

  function createPopup() {
    if (popupCreated) return;
    popupCreated = true;

    const popupHTML = `
      <div id="wordPopup" style="position: fixed; bottom: 20px; right: 20px; width: 450px; height: 170px; background: rgba(0, 0, 0, 0.5); color: white; padding: 10px; z-index: 9999; ${isPopupMinimized ? 'display: none;' : ''}">
        <div style="display: flex; justify-content: space-between; align-items: center;">
          <h2 style="margin: 0;"><span id="word" style="font-weight: bold; color: #fff; cursor: pointer;"></span><span id="phonetic" style="font-size: smaller; color: lightgrey;"></span></h2>
          <div>
            <button id="prevWord" class="word-popup-button" style="font-size: 16px;">←</button>
            <button id="togglePopup" class="word-popup-button" style="font-size: 16px;">−</button>
            <button id="nextWord" class="word-popup-button" style="font-size: 16px;">→</button>
          </div>
        </div>
        <p id="definition" style="margin: 5px 0;"></p>
        <p id="examples" style="margin: 5px 0;"></p>
      </div>
      <div id="minimizedPopup" style="position: fixed; bottom: 20px; right: 20px; width: 30px; height: 30px; background: rgba(0, 0, 0, 0.5); color: white; padding: 5px; z-index: 9999; display: flex; justify-content: center; align-items: center; cursor: pointer; ${!isPopupMinimized ? 'display: none;' : ''}">
        <span id="expandPopup" class="word-popup-button" style="font-size: 16px;">+</span>
      </div>
    `;
    $('body').append(popupHTML);

    // При загрузке страницы проверяем состояние свёрнутости
    applyPopupState();
    $(window).on('load hashchange', applyPopupState);

    $('#togglePopup').one('click', function() {
      togglePopupState();
    });

    $('#minimizedPopup').one('click', function() {
      togglePopupState();
    });

    $('#prevWord').on('click', showPreviousWord);
    $('#nextWord').on('click', showNextWord);
    $('#word').on('click', () => openTranslationWindow($('#word').text()));
  }

  function showPreviousWord() {
    if (wordsQueue.length > 1) {
      currentWordIndex = (currentWordIndex - 1 + wordsQueue.length) % wordsQueue.length;
      displayWord();
      resetChangeWordTimer();
    }
  }

  function showNextWord() {
    if (wordsQueue.length > 0) {
      currentWordIndex = (currentWordIndex + 1) % wordsQueue.length;
      displayWord();
      resetChangeWordTimer();
      loadNextWord(); // Запускаем подгрузку новых слов
    }
  }

  // Функция для переключения состояния свёрнутости и сохранения в localStorage
  function togglePopupState() {
    $('#wordPopup').toggle();
    $('#minimizedPopup').toggle();
    isPopupMinimized = !isPopupMinimized;
    localStorage.setItem('isPopupMinimized', isPopupMinimized);
  }

  // Функция для установки состояния попапа
  function applyPopupState() {
    if (isPopupMinimized) {
      $('#wordPopup').hide();
      $('#minimizedPopup').show();
    } else {
      $('#wordPopup').show();
      $('#minimizedPopup').hide();
    }
  }

  $(document).on('keydown', function(event) {
    if (event.key === 'ArrowLeft') {
      showPreviousWord();
    } else if (event.key === 'ArrowRight') {
      showNextWord();
    }
  });

  $(document).ready(function() {
    loadNextWord(); // Загрузка первой порции слов
  });
})();