Random English Word Popup

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

// ==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(); // Загрузка первой порции слов
  });
})();